aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/blocks/checkpoints.datbin38400036 -> 44480036 bytes
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/base58.cpp14
-rw-r--r--src/common/command_line.cpp5
-rw-r--r--src/common/command_line.h1
-rw-r--r--src/common/common_fwd.h3
-rw-r--r--src/common/sfinae_helpers.h147
-rw-r--r--src/common/task_region.cpp94
-rw-r--r--src/common/task_region.h223
-rw-r--r--src/common/thread_group.cpp153
-rw-r--r--src/common/thread_group.h143
-rw-r--r--src/common/threadpool.cpp117
-rw-r--r--src/common/threadpool.h87
-rw-r--r--src/cryptonote_basic/checkpoints.cpp2
-rw-r--r--src/cryptonote_basic/miner.cpp13
-rw-r--r--src/cryptonote_config.h2
-rw-r--r--src/cryptonote_core/blockchain.cpp277
-rw-r--r--src/cryptonote_core/blockchain.h63
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp81
-rw-r--r--src/cryptonote_core/cryptonote_core.h30
-rw-r--r--src/cryptonote_core/tx_pool.cpp63
-rw-r--r--src/cryptonote_core/tx_pool.h23
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl2
-rw-r--r--src/daemon/CMakeLists.txt3
-rw-r--r--src/daemon/command_line_args.h19
-rw-r--r--src/daemon/command_server.cpp2
-rw-r--r--src/daemon/daemon.cpp41
-rw-r--r--src/daemon/daemon.h2
-rw-r--r--src/daemon/main.cpp3
-rw-r--r--src/daemon/rpc_command_executor.cpp5
-rw-r--r--src/p2p/net_node.h6
-rw-r--r--src/p2p/net_node.inl144
-rw-r--r--src/ringct/rctSigs.cpp49
-rw-r--r--src/rpc/CMakeLists.txt70
-rw-r--r--src/rpc/core_rpc_server.cpp2
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/daemon_handler.cpp887
-rw-r--r--src/rpc/daemon_handler.h145
-rw-r--r--src/rpc/daemon_messages.cpp900
-rw-r--r--src/rpc/daemon_messages.h421
-rw-r--r--src/rpc/daemon_rpc_version.h44
-rw-r--r--src/rpc/message.cpp286
-rw-r--r--src/rpc/message.h131
-rw-r--r--src/rpc/message_data_structs.h189
-rw-r--r--src/rpc/rpc_handler.h54
-rw-r--r--src/rpc/zmq_server.cpp141
-rw-r--r--src/rpc/zmq_server.h83
-rw-r--r--src/serialization/CMakeLists.txt54
-rw-r--r--src/serialization/json_object.cpp1165
-rw-r--r--src/serialization/json_object.h371
-rw-r--r--src/simplewallet/simplewallet.cpp14
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/wallet/wallet2.cpp63
54 files changed, 5920 insertions, 927 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e473c2984..e3cb7cfd6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -107,6 +107,7 @@ endif()
add_subdirectory(mnemonics)
if(NOT IOS)
add_subdirectory(rpc)
+ add_subdirectory(serialization)
endif()
add_subdirectory(wallet)
if(NOT IOS)
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index a15f53e67..15fa042cf 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 55b8ad3e6..19d90253b 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -37,8 +37,7 @@ set(common_sources
i18n.cpp
password.cpp
perf_timer.cpp
- task_region.cpp
- thread_group.cpp
+ threadpool.cpp
updates.cpp)
if (STACK_TRACE)
@@ -66,8 +65,7 @@ set(common_private_headers
password.h
perf_timer.h
stack_trace.h
- task_region.h
- thread_group.h
+ threadpool.h
updates.h)
monero_private_headers(common
diff --git a/src/common/base58.cpp b/src/common/base58.cpp
index 64cb7c0de..941373443 100644
--- a/src/common/base58.cpp
+++ b/src/common/base58.cpp
@@ -111,13 +111,13 @@ namespace tools
uint64_t res = 0;
switch (9 - size)
{
- case 1: res |= *data++;
- case 2: res <<= 8; res |= *data++;
- case 3: res <<= 8; res |= *data++;
- case 4: res <<= 8; res |= *data++;
- case 5: res <<= 8; res |= *data++;
- case 6: res <<= 8; res |= *data++;
- case 7: res <<= 8; res |= *data++;
+ case 1: res |= *data++; /* FALLTHRU */
+ case 2: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 3: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 4: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 5: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 6: res <<= 8; res |= *data++; /* FALLTHRU */
+ case 7: res <<= 8; res |= *data++; /* FALLTHRU */
case 8: res <<= 8; res |= *data; break;
default: assert(false);
}
diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp
index 8c03bed0d..666b3267f 100644
--- a/src/common/command_line.cpp
+++ b/src/common/command_line.cpp
@@ -120,4 +120,9 @@ namespace command_line
, "Check for new versions of monero: [disabled|notify|download|update]"
, "notify"
};
+ const arg_descriptor<bool> arg_fluffy_blocks = {
+ "fluffy-blocks"
+ , "Relay blocks as fluffy blocks where possible (automatic on testnet)"
+ , false
+ };
}
diff --git a/src/common/command_line.h b/src/common/command_line.h
index ac64f519c..d4231acd0 100644
--- a/src/common/command_line.h
+++ b/src/common/command_line.h
@@ -217,4 +217,5 @@ namespace command_line
extern const arg_descriptor<uint64_t> arg_show_time_stats;
extern const arg_descriptor<size_t> arg_block_sync_size;
extern const arg_descriptor<std::string> arg_check_updates;
+ extern const arg_descriptor<bool> arg_fluffy_blocks;
}
diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h
index 5d67251b1..f33e185b5 100644
--- a/src/common/common_fwd.h
+++ b/src/common/common_fwd.h
@@ -36,6 +36,5 @@ namespace tools
struct login;
class password_container;
class t_http_connection;
- class task_region;
- class thread_group;
+ class threadpool;
}
diff --git a/src/common/sfinae_helpers.h b/src/common/sfinae_helpers.h
new file mode 100644
index 000000000..ddd456dd2
--- /dev/null
+++ b/src/common/sfinae_helpers.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2016-2017, 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
+
+// the loose definitions of types in this file are, well, loose.
+//
+// these helpers aren't here for absolute type certainty at compile-time,
+// but rather to help with templated functions telling types apart.
+
+namespace sfinae
+{
+
+ typedef char true_type;
+
+ struct false_type { true_type a[2]; };
+
+ template <typename T>
+ struct is_not_container
+ {
+ private:
+
+ // does not have const iterator
+ template <typename C> static false_type c_iter(typename C::const_iterator*);
+ template <typename C> static true_type c_iter(...);
+
+ // does not have value_type
+ template <typename C> static false_type v_type(typename C::value_type*);
+ template <typename C> static true_type v_type(...);
+
+ // does not have key_type
+ template <typename C> static false_type k_type(typename C::key_type*);
+ template <typename C> static true_type k_type(...);
+
+ // does not have mapped_type
+ template <typename C> static false_type m_type(typename C::mapped_type*);
+ template <typename C> static true_type m_type(...);
+
+ public:
+
+ static const bool value = (
+ (
+ sizeof(c_iter<T>(0)) == sizeof(true_type) &&
+ sizeof(v_type<T>(0)) == sizeof(true_type) &&
+ sizeof(k_type<T>(0)) == sizeof(true_type) &&
+ sizeof(m_type<T>(0)) == sizeof(true_type)
+ )
+ || std::is_same<T, std::string>::value
+ );
+
+ typedef T type;
+ };
+
+ template <typename T>
+ struct is_vector_like
+ {
+ private:
+
+ // has const iterator
+ template <typename C> static true_type c_iter(typename C::const_iterator*);
+ template <typename C> static false_type c_iter(...);
+
+ // has value_type
+ template <typename C> static true_type v_type(typename C::value_type*);
+ template <typename C> static false_type v_type(...);
+
+ // does not have key_type
+ template <typename C> static false_type k_type(typename C::key_type*);
+ template <typename C> static true_type k_type(...);
+
+ // does not have mapped_type
+ template <typename C> static false_type m_type(typename C::mapped_type*);
+ template <typename C> static true_type m_type(...);
+
+ public:
+
+ static const bool value = (
+ sizeof(c_iter<T>(0)) == sizeof(true_type) &&
+ sizeof(v_type<T>(0)) == sizeof(true_type) &&
+ sizeof(k_type<T>(0)) == sizeof(true_type) &&
+ sizeof(m_type<T>(0)) == sizeof(true_type) &&
+ !std::is_same<T, std::string>::value
+ );
+
+ typedef T type;
+ };
+
+ template <typename T>
+ struct is_map_like
+ {
+ private:
+
+ // has const iterator
+ template <typename C> static true_type c_iter(typename C::const_iterator*);
+ template <typename C> static false_type c_iter(...);
+
+ // has value_type
+ template <typename C> static true_type v_type(typename C::value_type*);
+ template <typename C> static false_type v_type(...);
+
+ // has key_type
+ template <typename C> static true_type k_type(typename C::key_type*);
+ template <typename C> static false_type k_type(...);
+
+ // has mapped_type
+ template <typename C> static true_type m_type(typename C::mapped_type*);
+ template <typename C> static false_type m_type(...);
+
+ public:
+
+ static const bool value = (
+ sizeof(c_iter<T>(0)) == sizeof(true_type) &&
+ sizeof(v_type<T>(0)) == sizeof(true_type) &&
+ sizeof(k_type<T>(0)) == sizeof(true_type) &&
+ sizeof(m_type<T>(0)) == sizeof(true_type) &&
+ !std::is_same<T, std::string>::value
+ );
+
+ typedef T type;
+ };
+
+} // namespace sfinae
diff --git a/src/common/task_region.cpp b/src/common/task_region.cpp
deleted file mode 100644
index 9b4620c6e..000000000
--- a/src/common/task_region.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2014-2017, 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 "common/task_region.h"
-
-#include <boost/thread/locks.hpp>
-#include <cassert>
-
-/* `mark_completed` and `wait` can throw in the lock call, but its difficult to
-recover from either. An exception in `wait` means the post condition of joining
-all threads cannot be achieved, and an exception in `mark_completed` means
-certain deadlock. `noexcept` qualifier will force a call to `std::terminate` if
-locking throws an exception, which should only happen if a recursive lock
-attempt is made (which is not possible since no external function is called
-while holding the lock). */
-
-namespace tools
-{
-void task_region_handle::state::mark_completed(id task_id) noexcept {
- assert(task_id != 0 && (task_id & (task_id - 1)) == 0); // power of 2 check
- if (pending.fetch_and(~task_id) == task_id) {
- // synchronize with wait call, but do not need to hold
- boost::unique_lock<boost::mutex>{sync_on_complete};
- all_complete.notify_all();
- }
-}
-
-void task_region_handle::state::abort() noexcept {
- state* current = this;
- while (current) {
- current->ready = 0;
- current = current->next.get();
- }
-}
-
-void task_region_handle::state::wait() noexcept {
- state* current = this;
- while (current) {
- {
- boost::unique_lock<boost::mutex> lock{current->sync_on_complete};
- current->all_complete.wait(lock, [current] { return current->pending == 0; });
- }
- current = current->next.get();
- }
-}
-
-void task_region_handle::state::wait(thread_group& threads) noexcept {
- state* current = this;
- while (current) {
- while (current->pending != 0) {
- if (!threads.try_run_one()) {
- current->wait();
- return;
- }
- }
- current = current->next.get();
- }
-}
-
-void task_region_handle::create_state() {
- st = std::make_shared<state>(std::move(st));
- next_id = 1;
-}
-
-void task_region_handle::do_wait() noexcept {
- assert(st);
- const std::shared_ptr<state> temp = std::move(st);
- temp->wait(threads);
-}
-}
diff --git a/src/common/task_region.h b/src/common/task_region.h
deleted file mode 100644
index 30972cce3..000000000
--- a/src/common/task_region.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (c) 2014-2017, 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 <atomic>
-#include <boost/thread/condition_variable.hpp>
-#include <boost/thread/mutex.hpp>
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-#include "common/thread_group.h"
-
-namespace tools
-{
-
-/*! A model of the fork-join concept. `run(...)` "forks" (i.e. spawns new
-tasks), and `~task_region_handle()` or `wait()` "joins" the spawned tasks.
-`wait` will block until all tasks have completed, while `~task_region_handle()`
-blocks until all tasks have completed or aborted.
-
-Do _NOT_ give this object to separate thread of execution (which includes
-`task_region_handle::run(...)`) because joining on a different thread is
-undesireable (potential deadlock).
-
-This class cannot be constructed directly, use the function
-`task_region(...)` instead.
-*/
-class task_region_handle
-{
- struct state
- {
- using id = unsigned;
-
- explicit state(std::shared_ptr<state> next_src) noexcept
- : next(std::move(next_src))
- , ready(0)
- , pending(0)
- , sync_on_complete()
- , all_complete() {
- }
-
- state(const state&) = default;
- state(state&&) = default;
- ~state() = default;
- state& operator=(const state&) = default;
- state& operator=(state&&) = default;
-
- void track_id(id task_id) noexcept {
- pending |= task_id;
- ready |= task_id;
- }
-
- //! \return True only once whether a given id can execute
- bool can_run(id task_id) noexcept {
- return (ready.fetch_and(~task_id) & task_id);
- }
-
- //! Mark id as completed, and synchronize with waiting threads
- void mark_completed(id task_id) noexcept;
-
- //! Tell all unstarted functions in region to return immediately
- void abort() noexcept;
-
- //! Blocks until all functions in region have aborted or completed.
- void wait() noexcept;
-
- //! Same as `wait()`, except `this_thread` runs tasks while waiting.
- void wait(thread_group& threads) noexcept;
-
- private:
- /* This implementation is a bit pessimistic, it ensures that all copies
- of a wrapped task can only be executed once. `thread_group` should never
- do this, but some variable needs to track whether an abort should be done
- anyway... */
- std::shared_ptr<state> next;
- std::atomic<id> ready; //!< Tracks whether a task has been invoked
- std::atomic<id> pending; //!< Tracks when a task has completed or aborted
- boost::mutex sync_on_complete;
- boost::condition_variable all_complete;
- };
-
- template<typename F>
- struct wrapper
- {
- wrapper(state::id id_src, std::shared_ptr<state> st_src, F f_src)
- : task_id(id_src), st(std::move(st_src)), f(std::move(f_src)) {
- }
-
- wrapper(const wrapper&) = default;
- wrapper(wrapper&&) = default;
- wrapper& operator=(const wrapper&) = default;
- wrapper& operator=(wrapper&&) = default;
-
- void operator()() {
- if (st) {
- if (st->can_run(task_id)) {
- f();
- }
- st->mark_completed(task_id);
- }
- }
-
- private:
- const state::id task_id;
- std::shared_ptr<state> st;
- F f;
- };
-
-public:
- friend struct task_region_;
-
- task_region_handle() = delete;
- task_region_handle(const task_region_handle&) = delete;
- task_region_handle(task_region_handle&&) = delete;
-
- //! Cancels unstarted pending tasks, and waits for them to respond.
- ~task_region_handle() noexcept {
- if (st) {
- st->abort();
- st->wait(threads);
- }
- }
-
- task_region_handle& operator=(const task_region_handle&) = delete;
- task_region_handle& operator=(task_region_handle&&) = delete;
-
- /*! If the group has no threads, `f` is immediately run before returning.
- Otherwise, `f` is dispatched to the thread_group associated with `this`
- region. If `f` is dispatched to another thread, and it throws, the process
- will immediately terminate. See std::packaged_task for getting exceptions on
- functions executed on other threads. */
- template<typename F>
- void run(F&& f) {
- if (threads.count() == 0) {
- f();
- } else {
- if (!st || next_id == 0) {
- create_state();
- }
- const state::id this_id = next_id;
- next_id <<= 1;
-
- st->track_id(this_id);
- threads.dispatch(wrapper<F>{this_id, st, std::move(f)});
- }
- }
-
- //! Wait until all functions provided to `run` have completed.
- void wait() noexcept {
- if (st) {
- do_wait();
- }
- }
-
-private:
- explicit task_region_handle(thread_group& threads_src)
- : st(nullptr), threads(threads_src), next_id(0) {
- }
-
- void create_state();
- void do_wait() noexcept;
-
- std::shared_ptr<state> st;
- thread_group& threads;
- state::id next_id;
-};
-
-/*! Function for creating a `task_region_handle`, which automatically calls
-`task_region_handle::wait()` before returning. If a `thread_group` is not
-provided, one is created with an optimal number of threads. The callback `f`
-must have the signature `void(task_region_handle&)`. */
-struct task_region_ {
- template<typename F>
- void operator()(thread_group& threads, F&& f) const {
- static_assert(
- std::is_same<void, typename std::result_of<F(task_region_handle&)>::type>::value,
- "f cannot have a return value"
- );
- task_region_handle region{threads};
- f(region);
- region.wait();
- }
-
- template<typename F>
- void operator()(thread_group&& threads, F&& f) const {
- (*this)(threads, std::forward<F>(f));
- }
-
- template<typename F>
- void operator()(F&& f) const {
- thread_group threads;
- (*this)(threads, std::forward<F>(f));
- }
-};
-
-constexpr const task_region_ task_region{};
-}
diff --git a/src/common/thread_group.cpp b/src/common/thread_group.cpp
deleted file mode 100644
index 691a27a25..000000000
--- a/src/common/thread_group.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2014-2017, 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 "common/thread_group.h"
-
-#include <boost/thread/locks.hpp>
-#include <cassert>
-#include <limits>
-#include <stdexcept>
-
-#include "cryptonote_config.h"
-#include "common/util.h"
-
-namespace tools
-{
-std::size_t thread_group::optimal() {
- static_assert(
- std::numeric_limits<unsigned>::max() <= std::numeric_limits<std::size_t>::max(),
- "unexpected truncation"
- );
- const std::size_t hardware = get_max_concurrency();
- return hardware ? (hardware - 1) : 0;
-}
-
-std::size_t thread_group::optimal_with_max(std::size_t count) {
- return count ? std::min(count - 1, optimal()) : 0;
-}
-
-thread_group::thread_group(std::size_t count) : internal() {
- if (count) {
- internal.emplace(count);
- }
-}
-
-thread_group::data::data(std::size_t count)
- : threads()
- , head{nullptr}
- , last(std::addressof(head))
- , mutex()
- , has_work()
- , stop(false) {
- threads.reserve(count);
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
- while (count--) {
- threads.push_back(boost::thread(attrs, boost::bind(&thread_group::data::run, this)));
- }
-}
-
-thread_group::data::~data() noexcept {
- {
- const boost::unique_lock<boost::mutex> lock(mutex);
- stop = true;
- }
- has_work.notify_all();
- for (auto& worker : threads) {
- try {
- worker.join();
- }
- catch(...) {}
- }
-}
-
-std::unique_ptr<thread_group::data::work> thread_group::data::get_next() noexcept {
- std::unique_ptr<work> rc = std::move(head.ptr);
- if (rc != nullptr) {
- head.ptr = std::move(rc->next.ptr);
- if (head.ptr == nullptr) {
- last = std::addressof(head);
- }
- }
- return rc;
-}
-
-bool thread_group::data::try_run_one() noexcept {
- /* This function and `run()` can both throw when acquiring the lock, or in
- dispatched function. It is tough to recover from either, particularly the
- lock case. These functions are marked as noexcept so that if either call
- throws, the entire process is terminated. Users of the `dispatch` call are
- expected to make their functions noexcept, or use std::packaged_task to copy
- exceptions so that the process will continue in all but the most pessimistic
- cases (std::bad_alloc). This was the existing behavior;
- `asio::io_service::run` propogates errors from dispatched calls, and uncaught
- exceptions on threads result in process termination. */
- std::unique_ptr<work> next = nullptr;
- {
- const boost::unique_lock<boost::mutex> lock(mutex);
- next = get_next();
- }
- if (next) {
- assert(next->f);
- next->f();
- return true;
- }
- return false;
-}
-
-void thread_group::data::run() noexcept {
- // see `try_run_one()` source for additional information
- while (true) {
- std::unique_ptr<work> next = nullptr;
- {
- boost::unique_lock<boost::mutex> lock(mutex);
- has_work.wait(lock, [this] { return head.ptr != nullptr || stop; });
- if (stop) {
- return;
- }
- next = get_next();
- }
- assert(next != nullptr);
- assert(next->f);
- next->f();
- }
-}
-
-void thread_group::data::dispatch(std::function<void()> f) {
- std::unique_ptr<work> latest(new work{std::move(f), node{nullptr}});
- node* const latest_node = std::addressof(latest->next);
- {
- const boost::unique_lock<boost::mutex> lock(mutex);
- assert(last != nullptr);
- assert(last->ptr == nullptr);
-
- last->ptr = std::move(latest);
- last = latest_node;
- }
- has_work.notify_one();
-}
-}
diff --git a/src/common/thread_group.h b/src/common/thread_group.h
deleted file mode 100644
index 48fd4cd56..000000000
--- a/src/common/thread_group.h
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (c) 2014-2017, 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 <boost/thread/condition_variable.hpp>
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/thread.hpp>
-#include <cstddef>
-#include <functional>
-#include <thread>
-#include <utility>
-#include <vector>
-
-namespace tools
-{
-//! Manages zero or more threads for work dispatching.
-class thread_group
-{
-public:
-
- //! \return `get_max_concurrency() ? get_max_concurrency() - 1 : 0`
- static std::size_t optimal();
-
- //! \return `count ? min(count - 1, optimal()) : 0`
- static std::size_t optimal_with_max(std::size_t count);
-
- //! Create an optimal number of threads.
- explicit thread_group() : thread_group(optimal()) {}
-
- //! Create exactly `count` threads.
- explicit thread_group(std::size_t count);
-
- thread_group(thread_group const&) = delete;
- thread_group(thread_group&&) = delete;
-
- //! Joins threads, but does not necessarily run all dispatched functions.
- ~thread_group() = default;
-
- thread_group& operator=(thread_group const&) = delete;
- thread_group& operator=(thread_group&&) = delete;
-
- //! \return Number of threads owned by `this` group.
- std::size_t count() const noexcept {
- if (internal) {
- return internal->count();
- }
- return 0;
- }
-
- //! \return True iff a function was available and executed (on `this_thread`).
- bool try_run_one() noexcept {
- if (internal) {
- return internal->try_run_one();
- }
- return false;
- }
-
- /*! `f` is invoked immediately if `count() == 0`, otherwise execution of `f`
- is queued for next available thread. If `f` is queued, any exception leaving
- that function will result in process termination. Use std::packaged_task if
- exceptions need to be handled. */
- template<typename F>
- void dispatch(F&& f) {
- if (internal) {
- internal->dispatch(std::forward<F>(f));
- }
- else {
- f();
- }
- }
-
-private:
- class data {
- public:
- data(std::size_t count);
- ~data() noexcept;
-
- std::size_t count() const noexcept {
- return threads.size();
- }
-
- bool try_run_one() noexcept;
- void dispatch(std::function<void()> f);
-
- private:
- struct work;
-
- struct node {
- std::unique_ptr<work> ptr;
- };
-
- struct work {
- std::function<void()> f;
- node next;
- };
-
- //! Requires lock on `mutex`.
- std::unique_ptr<work> get_next() noexcept;
-
- //! Blocks until destructor is invoked, only call from thread.
- void run() noexcept;
-
- private:
- std::vector<boost::thread> threads;
- node head;
- node* last;
- boost::condition_variable has_work;
- boost::mutex mutex;
- bool stop;
- };
-
-private:
- // optionally construct elements, without separate heap allocation
- boost::optional<data> internal;
-};
-
-}
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
new file mode 100644
index 000000000..41d0c25e0
--- /dev/null
+++ b/src/common/threadpool.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2017, 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 "common/threadpool.h"
+
+#include <cassert>
+#include <limits>
+#include <stdexcept>
+
+#include "cryptonote_config.h"
+#include "common/util.h"
+
+namespace tools
+{
+threadpool::threadpool() : running(true), active(0) {
+ boost::thread::attributes attrs;
+ attrs.set_stack_size(THREAD_STACK_SIZE);
+ max = tools::get_max_concurrency() * 2;
+ size_t i = max;
+ while(i--) {
+ threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this)));
+ }
+}
+
+threadpool::~threadpool() {
+ {
+ const boost::unique_lock<boost::mutex> lock(mutex);
+ running = false;
+ has_work.notify_all();
+ }
+ for (size_t i = 0; i<threads.size(); i++) {
+ threads[i].join();
+ }
+}
+
+void threadpool::submit(waiter *obj, std::function<void()> f) {
+ entry e = {obj, f};
+ boost::unique_lock<boost::mutex> lock(mutex);
+ if (active == max && !queue.empty()) {
+ // if all available threads are already running
+ // and there's work waiting, just run in current thread
+ lock.unlock();
+ f();
+ } else {
+ if (obj)
+ obj->inc();
+ queue.push_back(e);
+ has_work.notify_one();
+ }
+}
+
+int threadpool::get_max_concurrency() {
+ return max / 2;
+}
+
+void threadpool::waiter::wait() {
+ boost::unique_lock<boost::mutex> lock(mt);
+ while(num) cv.wait(lock);
+}
+
+void threadpool::waiter::inc() {
+ const boost::unique_lock<boost::mutex> lock(mt);
+ num++;
+}
+
+void threadpool::waiter::dec() {
+ const boost::unique_lock<boost::mutex> lock(mt);
+ num--;
+ if (!num)
+ cv.notify_one();
+}
+
+void threadpool::run() {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ while (running) {
+ entry e;
+ while(queue.empty() && running)
+ has_work.wait(lock);
+ if (!running) break;
+
+ active++;
+ e = queue.front();
+ queue.pop_front();
+ lock.unlock();
+ e.f();
+
+ if (e.wo)
+ e.wo->dec();
+ lock.lock();
+ active--;
+ }
+}
+}
diff --git a/src/common/threadpool.h b/src/common/threadpool.h
new file mode 100644
index 000000000..1d56d7605
--- /dev/null
+++ b/src/common/threadpool.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2017, 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/thread/condition_variable.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/thread.hpp>
+#include <cstddef>
+#include <functional>
+#include <utility>
+#include <vector>
+
+namespace tools
+{
+//! A global thread pool
+class threadpool
+{
+public:
+ static threadpool& getInstance() {
+ static threadpool instance;
+ return instance;
+ }
+
+ // The waiter lets the caller know when all of its
+ // tasks are completed.
+ class waiter {
+ boost::mutex mt;
+ boost::condition_variable cv;
+ int num;
+ public:
+ void inc();
+ void dec();
+ void wait(); //! Wait for a set of tasks to finish.
+ waiter() : num(0){}
+ ~waiter() { wait(); }
+ };
+
+ // Submit a task to the pool. The waiter pointer may be
+ // NULL if the caller doesn't care to wait for the
+ // task to finish.
+ void submit(waiter *waiter, std::function<void()> f);
+
+ int get_max_concurrency();
+
+ private:
+ threadpool();
+ ~threadpool();
+ typedef struct entry {
+ waiter *wo;
+ std::function<void()> f;
+ } entry;
+ std::deque<entry> queue;
+ boost::condition_variable has_work;
+ boost::mutex mutex;
+ std::vector<boost::thread> threads;
+ int active;
+ int max;
+ bool running;
+ void run();
+};
+
+}
diff --git a/src/cryptonote_basic/checkpoints.cpp b/src/cryptonote_basic/checkpoints.cpp
index 103a4a33e..98e509561 100644
--- a/src/cryptonote_basic/checkpoints.cpp
+++ b/src/cryptonote_basic/checkpoints.cpp
@@ -167,6 +167,8 @@ namespace cryptonote
ADD_CHECKPOINT(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903");
ADD_CHECKPOINT(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2");
ADD_CHECKPOINT(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f");
+ ADD_CHECKPOINT(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0");
+ ADD_CHECKPOINT(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c");
return true;
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 3c5811d61..b620e3426 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -858,19 +858,6 @@ namespace cryptonote
const boost::filesystem::path& power_supply_path = iter->path();
if (boost::filesystem::is_directory(power_supply_path))
{
- std::ifstream power_supply_present_stream((power_supply_path / "present").string());
- if (power_supply_present_stream.fail())
- {
- LOG_PRINT_L0("Unable to read from " << power_supply_path << " to check if power supply present");
- continue;
- }
-
- if (power_supply_present_stream.get() != '1')
- {
- LOG_PRINT_L4("Power supply not present at " << power_supply_path);
- continue;
- }
-
boost::filesystem::path power_supply_type_path = power_supply_path / "type";
if (boost::filesystem::is_regular_file(power_supply_type_path))
{
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 8ff01cf8b..cdb46dda9 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -150,6 +150,7 @@ namespace config
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19;
uint16_t const P2P_DEFAULT_PORT = 18080;
uint16_t const RPC_DEFAULT_PORT = 18081;
+ uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082;
boost::uuids::uuid const NETWORK_ID = { {
0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10
} }; // Bender's nightmare
@@ -162,6 +163,7 @@ namespace config
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54;
uint16_t const P2P_DEFAULT_PORT = 28080;
uint16_t const RPC_DEFAULT_PORT = 28081;
+ uint16_t const ZMQ_RPC_DEFAULT_PORT = 28082;
boost::uuids::uuid const NETWORK_ID = { {
0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11
} }; // Bender's daydream
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 93a4e26f8..619dbdc07 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -45,6 +45,7 @@
#include "profile_tools.h"
#include "file_io_utils.h"
#include "common/int-util.h"
+#include "common/threadpool.h"
#include "common/boost_serialization_helper.h"
#include "warnings.h"
#include "crypto/hash.h"
@@ -751,7 +752,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
m_timestamps = timestamps;
m_difficulties = difficulties;
}
- size_t target = get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+ size_t target = get_difficulty_target();
return next_difficulty(timestamps, difficulties, target);
}
//------------------------------------------------------------------
@@ -1571,6 +1572,98 @@ void Blockchain::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_A
output_data_t data = m_db->get_output_key(amount, i);
oen.out_key = data.pubkey;
}
+
+uint64_t Blockchain::get_num_mature_outputs(uint64_t amount) const
+{
+ uint64_t num_outs = m_db->get_num_outputs(amount);
+ // ensure we don't include outputs that aren't yet eligible to be used
+ // outpouts are sorted by height
+ while (num_outs > 0)
+ {
+ const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1);
+ const uint64_t height = m_db->get_tx_block_height(toi.first);
+ if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
+ break;
+ --num_outs;
+ }
+
+ return num_outs;
+}
+
+std::vector<uint64_t> Blockchain::get_random_outputs(uint64_t amount, uint64_t count) const
+{
+ uint64_t num_outs = get_num_mature_outputs(amount);
+
+ std::vector<uint64_t> indices;
+
+ std::unordered_set<uint64_t> seen_indices;
+
+ // if there aren't enough outputs to mix with (or just enough),
+ // use all of them. Eventually this should become impossible.
+ if (num_outs <= count)
+ {
+ for (uint64_t i = 0; i < num_outs; i++)
+ {
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if tx is unlocked, add output to indices
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ indices.push_back(i);
+ }
+ }
+ }
+ else
+ {
+ // while we still need more mixins
+ while (indices.size() < count)
+ {
+ // if we've gone through every possible output, we've gotten all we can
+ if (seen_indices.size() == num_outs)
+ {
+ break;
+ }
+
+ // get a random output index from the DB. If we've already seen it,
+ // return to the top of the loop and try again, otherwise add it to the
+ // list of output indices we've seen.
+
+ // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
+ uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
+ double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
+ uint64_t i = (uint64_t)(frac*num_outs);
+ // just in case rounding up to 1 occurs after sqrt
+ if (i == num_outs)
+ --i;
+
+ if (seen_indices.count(i))
+ {
+ continue;
+ }
+ seen_indices.emplace(i);
+
+ // get tx_hash, tx_out_index from DB
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
+
+ // if the output's transaction is unlocked, add the output's index to
+ // our list.
+ if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
+ {
+ indices.push_back(i);
+ }
+ }
+ }
+
+ return indices;
+}
+
+crypto::public_key Blockchain::get_output_key(uint64_t amount, uint64_t global_index) const
+{
+ output_data_t data = m_db->get_output_key(amount, global_index);
+ return data.pubkey;
+}
+
//------------------------------------------------------------------
// This function takes an RPC request for mixins and creates an RPC response
// with the requested mixins.
@@ -1585,80 +1678,18 @@ bool Blockchain::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUT
// from BlockchainDB where <n> is req.outs_count (number of mixins).
for (uint64_t amount : req.amounts)
{
- auto num_outs = m_db->get_num_outputs(amount);
- // ensure we don't include outputs that aren't yet eligible to be used
- // outpouts are sorted by height
- while (num_outs > 0)
- {
- const tx_out_index toi = m_db->get_output_tx_and_index(amount, num_outs - 1);
- const uint64_t height = m_db->get_tx_block_height(toi.first);
- if (height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height())
- break;
- --num_outs;
- }
-
// create outs_for_amount struct and populate amount field
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
- std::unordered_set<uint64_t> seen_indices;
+ std::vector<uint64_t> indices = get_random_outputs(amount, req.outs_count);
- // if there aren't enough outputs to mix with (or just enough),
- // use all of them. Eventually this should become impossible.
- if (num_outs <= req.outs_count)
- {
- for (uint64_t i = 0; i < num_outs; i++)
- {
- // get tx_hash, tx_out_index from DB
- tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
-
- // if tx is unlocked, add output to result_outs
- if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
- {
- add_out_to_get_random_outs(result_outs, amount, i);
- }
-
- }
- }
- else
+ for (auto i : indices)
{
- // while we still need more mixins
- while (result_outs.outs.size() < req.outs_count)
- {
- // if we've gone through every possible output, we've gotten all we can
- if (seen_indices.size() == num_outs)
- {
- break;
- }
+ COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oe = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
- // get a random output index from the DB. If we've already seen it,
- // return to the top of the loop and try again, otherwise add it to the
- // list of output indices we've seen.
-
- // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
- uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
- double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
- uint64_t i = (uint64_t)(frac*num_outs);
- // just in case rounding up to 1 occurs after sqrt
- if (i == num_outs)
- --i;
-
- if (seen_indices.count(i))
- {
- continue;
- }
- seen_indices.emplace(i);
-
- // get tx_hash, tx_out_index from DB
- tx_out_index toi = m_db->get_output_tx_and_index(amount, i);
-
- // if the output's transaction is unlocked, add the output's index to
- // our list.
- if (is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first)))
- {
- add_out_to_get_random_outs(result_outs, amount, i);
- }
- }
+ oe.global_amount_index = i;
+ oe.out_key = get_output_key(amount, i);
}
}
return true;
@@ -1816,6 +1847,15 @@ bool Blockchain::get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMA
return true;
}
//------------------------------------------------------------------
+void Blockchain::get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const
+{
+ const auto o_data = m_db->get_output_key(amount, index);
+ key = o_data.pubkey;
+ mask = o_data.commitment;
+ tx_out_index toi = m_db->get_output_tx_and_index(amount, index);
+ unlocked = is_tx_spendtime_unlocked(m_db->get_tx_unlock_time(toi.first));
+}
+//------------------------------------------------------------------
// This function takes a list of block hashes from another node
// on the network to find where the split point is between us and them.
// This is used to see what to send another node that needs to sync.
@@ -2025,28 +2065,39 @@ void Blockchain::print_blockchain_outs(const std::string& file) const
// Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
-bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// if we can't find the split point, return false
- if(!find_blockchain_supplement(qblock_ids, resp.start_height))
+ if(!find_blockchain_supplement(qblock_ids, start_height))
{
return false;
}
m_db->block_txn_start(true);
- resp.total_height = get_current_blockchain_height();
+ current_height = get_current_blockchain_height();
size_t count = 0;
- for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
+ for(size_t i = start_height; i < current_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
{
- resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i));
+ hashes.push_back(m_db->get_block_hash_from_height(i));
}
- resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+
m_db->block_txn_stop();
return true;
}
+
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height);
+ resp.cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
+
+ return result;
+}
//------------------------------------------------------------------
//FIXME: change argument to std::vector, low priority
// find split point between ours and foreign blockchain (or start at
@@ -2513,33 +2564,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
std::vector < uint64_t > results;
results.resize(tx.vin.size(), 0);
- int threads = tools::get_max_concurrency();
-
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- bool ioservice_active = false;
-
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- if(threads > 1)
- {
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
- ioservice_active = true;
- }
-
-#define KILL_IOSERVICE() \
- if(ioservice_active) \
- { \
- work.reset(); \
- while (!ioservice.stopped()) ioservice.poll(); \
- threadpool.join_all(); \
- ioservice.stop(); \
- ioservice_active = false; \
- }
-
- epee::misc_utils::auto_scope_leave_caller ioservice_killer = epee::misc_utils::create_scope_leave_handler([&]() { KILL_IOSERVICE(); });
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ int threads = tpool.get_max_concurrency();
for (const auto& txin : tx.vin)
{
@@ -2600,7 +2627,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
// ND: Speedup
// 1. Thread ring signature verification if possible.
- ioservice.dispatch(boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
+ tpool.submit(&waiter, boost::bind(&Blockchain::check_ring_signature, this, std::cref(tx_prefix_hash), std::cref(in_to_key.k_image), std::cref(pubkeys[sig_index]), std::cref(tx.signatures[sig_index]), std::ref(results[sig_index])));
}
else
{
@@ -2623,8 +2650,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
sig_index++;
}
-
- KILL_IOSERVICE();
+ if (tx.version == 1 && threads > 1)
+ waiter.wait();
if (tx.version == 1)
{
@@ -3699,7 +3726,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
return true;
bool blocks_exist = false;
- uint64_t threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ uint64_t threads = tpool.get_max_concurrency();
if (blocks_entry.size() > 1 && threads > 1 && m_max_prepare_blocks_threads > 1)
{
@@ -3708,15 +3736,12 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
threads = m_max_prepare_blocks_threads;
uint64_t height = m_db->height();
- std::vector<boost::thread *> thread_list;
int batches = blocks_entry.size() / threads;
int extra = blocks_entry.size() % threads;
MDEBUG("block_batches: " << batches);
std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads);
std::vector < std::vector < block >> blocks(threads);
auto it = blocks_entry.begin();
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
for (uint64_t i = 0; i < threads; i++)
{
@@ -3775,19 +3800,14 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
{
m_blocks_longhash_table.clear();
uint64_t thread_height = height;
+ tools::threadpool::waiter waiter;
for (uint64_t i = 0; i < threads; i++)
{
- thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i]))));
+ tpool.submit(&waiter, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])));
thread_height += blocks[i].size();
}
- for (size_t j = 0; j < thread_list.size(); j++)
- {
- thread_list[j]->join();
- delete thread_list[j];
- }
-
- thread_list.clear();
+ waiter.wait();
if (m_cancel)
return false;
@@ -3911,30 +3931,20 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
// [output] stores all transactions for each tx_out_index::hash found
std::vector<std::unordered_map<crypto::hash, cryptonote::transaction>> transactions(amounts.size());
- threads = tools::get_max_concurrency();
+ threads = tpool.get_max_concurrency();
if (!m_db->can_thread_bulk_indices())
threads = 1;
if (threads > 1)
{
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
-
- for (uint64_t i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool::waiter waiter;
for (size_t i = 0; i < amounts.size(); i++)
{
uint64_t amount = amounts[i];
- ioservice.dispatch(boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::ref(transactions[i])));
+ tpool.submit(&waiter, boost::bind(&Blockchain::output_scan_worker, this, amount, std::cref(offset_map[amount]), std::ref(tx_map[amount]), std::ref(transactions[i])));
}
-
- work.reset();
- threadpool.join_all();
- ioservice.stop();
+ waiter.wait();
}
else
{
@@ -4086,6 +4096,11 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui
return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting);
}
+uint64_t Blockchain::get_difficulty_target() const
+{
+ return get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+}
+
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff) const
{
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff);
@@ -4129,7 +4144,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "23d8a8c73de7b2383c72a016d9a6034e69d62dd48077d1c414e064ceab6daa94";
+static const char expected_block_hashes_hash[] = "d3ca80d50661684cde0e715d46d7c19704d2e216b21ed088af9fd4ef37ed4d65";
void Blockchain::load_compiled_in_block_hashes()
{
if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0)
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index b8ea657b4..e2da535cd 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -374,6 +374,22 @@ namespace cryptonote
* BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
*
* @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
+ * @param hashes the hashes to be returned, return-by-reference
+ * @param start_height the start height, return-by-reference
+ * @param current_height the current blockchain height, return-by-reference
+ *
+ * @return true if a block found in common, else false
+ */
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::list<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const;
+
+ /**
+ * @brief get recent block hashes for a foreign chain
+ *
+ * Find the split point between us and foreign blockchain and return
+ * (by reference) the most recent common block hash along with up to
+ * BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
+ *
+ * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
* @param resp return-by-reference the split height and subsequent blocks' hashes
*
* @return true if a block found in common, else false
@@ -427,6 +443,35 @@ namespace cryptonote
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
/**
+ * @brief get number of outputs of an amount past the minimum spendable age
+ *
+ * @param amount the output amount
+ *
+ * @return the number of mature outputs
+ */
+ uint64_t get_num_mature_outputs(uint64_t amount) const;
+
+ /**
+ * @brief get random outputs (indices) for an amount
+ *
+ * @param amount the amount
+ * @param count the number of random outputs to choose
+ *
+ * @return the outputs' amount-global indices
+ */
+ std::vector<uint64_t> get_random_outputs(uint64_t amount, uint64_t count) const;
+
+ /**
+ * @brief get the public key for an output
+ *
+ * @param amount the output amount
+ * @param global_index the output amount-global index
+ *
+ * @return the public key
+ */
+ crypto::public_key get_output_key(uint64_t amount, uint64_t global_index) const;
+
+ /**
* @brief gets random outputs to mix with
*
* This function takes an RPC request for outputs to mix with
@@ -458,6 +503,17 @@ namespace cryptonote
bool get_outs(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) const;
/**
+ * @brief gets an output's key and unlocked state
+ *
+ * @param amount in - the output amount
+ * @param index in - the output global amount index
+ * @param mask out - the output's RingCT mask
+ * @param key out - the output's key
+ * @param unlocked out - the output's unlocked state
+ */
+ void get_output_key_mask_unlocked(const uint64_t& amount, const uint64_t& index, crypto::public_key& key, rct::key& mask, bool& unlocked) const;
+
+ /**
* @brief gets random ringct outputs to mix with
*
* This function takes an RPC request for outputs to mix with
@@ -775,6 +831,13 @@ namespace cryptonote
bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const;
/**
+ * @brief get difficulty target based on chain and hardfork version
+ *
+ * @return difficulty target
+ */
+ uint64_t get_difficulty_target() const;
+
+ /**
* @brief remove transactions from the transaction pool (if present)
*
* @param txids a list of hashes of transactions to be removed
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 73ce88c79..56485aedf 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -37,7 +37,7 @@ using namespace epee;
#include "common/util.h"
#include "common/updates.h"
#include "common/download.h"
-#include "common/task_region.h"
+#include "common/threadpool.h"
#include "warnings.h"
#include "crypto/crypto.h"
#include "cryptonote_config.h"
@@ -74,7 +74,7 @@ namespace cryptonote
m_last_dns_checkpoints_update(0),
m_last_json_checkpoints_update(0),
m_disable_dns_checkpoints(false),
- m_threadpool(tools::thread_group::optimal()),
+ m_threadpool(tools::threadpool::getInstance()),
m_update_download(0)
{
m_checkpoints_updating.clear();
@@ -161,6 +161,7 @@ namespace cryptonote
command_line::add_arg(desc, command_line::arg_show_time_stats);
command_line::add_arg(desc, command_line::arg_block_sync_size);
command_line::add_arg(desc, command_line::arg_check_updates);
+ command_line::add_arg(desc, command_line::arg_fluffy_blocks);
// we now also need some of net_node's options (p2p bind arg, for separate data dir)
command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false);
@@ -197,6 +198,7 @@ namespace cryptonote
set_enforce_dns_checkpoints(command_line::get_arg(vm, command_line::arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, command_line::arg_test_drop_download_height));
+ m_fluffy_blocks_enabled = m_testnet || get_arg(vm, command_line::arg_fluffy_blocks);
if (command_line::get_arg(vm, command_line::arg_test_drop_download) == true)
test_drop_download();
@@ -589,38 +591,53 @@ namespace cryptonote
std::vector<result> results(tx_blobs.size());
tvc.resize(tx_blobs.size());
- tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) {
- std::list<blobdata>::const_iterator it = tx_blobs.begin();
- for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
- region.run([&, i, it] {
- results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
- });
- }
- });
- tools::task_region(m_threadpool, [&] (tools::task_region_handle& region) {
- std::list<blobdata>::const_iterator it = tx_blobs.begin();
- for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
- if (!results[i].res)
- continue;
- if(m_mempool.have_tx(results[i].hash))
+ tools::threadpool::waiter waiter;
+ std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ m_threadpool.submit(&waiter, [&, i, it] {
+ try
{
- LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
+ results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
}
- else if(m_blockchain_storage.have_tx(results[i].hash))
+ catch (const std::exception &e)
{
- LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain");
+ MERROR_VER("Exception in handle_incoming_tx_pre: " << e.what());
+ results[i].res = false;
}
- else
- {
- region.run([&, i, it] {
+ });
+ }
+ waiter.wait();
+ it = tx_blobs.begin();
+ for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
+ if (!results[i].res)
+ continue;
+ if(m_mempool.have_tx(results[i].hash))
+ {
+ LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
+ }
+ else if(m_blockchain_storage.have_tx(results[i].hash))
+ {
+ LOG_PRINT_L2("tx " << results[i].hash << " already have transaction in blockchain");
+ }
+ else
+ {
+ m_threadpool.submit(&waiter, [&, i, it] {
+ try
+ {
results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, results[i].prefix_hash, keeped_by_block, relayed, do_not_relay);
- });
- }
+ }
+ catch (const std::exception &e)
+ {
+ MERROR_VER("Exception in handle_incoming_tx_post: " << e.what());
+ results[i].res = false;
+ }
+ });
}
- });
+ }
+ waiter.wait();
bool ok = true;
- std::list<blobdata>::const_iterator it = tx_blobs.begin();
+ it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
if (!results[i].res)
{
@@ -792,6 +809,13 @@ namespace cryptonote
return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4;
}
//-----------------------------------------------------------------------------------------------
+ bool core::are_key_images_spent_in_pool(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const
+ {
+ spent.clear();
+
+ return m_mempool.check_for_key_images(key_im, spent);
+ }
+ //-----------------------------------------------------------------------------------------------
std::pair<uint64_t, uint64_t> core::get_coinbase_tx_sum(const uint64_t start_offset, const size_t count)
{
uint64_t emission_amount = 0;
@@ -1182,6 +1206,11 @@ namespace cryptonote
return m_mempool.get_transactions_and_spent_keys_info(tx_infos, key_image_infos);
}
//-----------------------------------------------------------------------------------------------
+ bool core::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
+ {
+ return m_mempool.get_pool_for_rpc(tx_infos, key_image_infos);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_short_chain_history(std::list<crypto::hash>& ids) const
{
return m_blockchain_storage.get_short_chain_history(ids);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index f565afd87..4ea33dbe1 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -40,7 +40,7 @@
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
#include "common/download.h"
-#include "common/thread_group.h"
+#include "common/threadpool.h"
#include "tx_pool.h"
#include "blockchain.h"
#include "cryptonote_basic/miner.h"
@@ -463,6 +463,13 @@ namespace cryptonote
bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/**
+ * @copydoc tx_memory_pool::get_pool_for_rpc
+ *
+ * @note see tx_memory_pool::get_pool_for_rpc
+ */
+ bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
+
+ /**
* @copydoc tx_memory_pool::get_transactions_count
*
* @note see tx_memory_pool::get_transactions_count
@@ -708,6 +715,16 @@ namespace cryptonote
bool are_key_images_spent(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const;
/**
+ * @brief check if multiple key images are spent in the transaction pool
+ *
+ * @param key_im list of key images to check
+ * @param spent return-by-reference result for each image checked
+ *
+ * @return true
+ */
+ bool are_key_images_spent_in_pool(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const;
+
+ /**
* @brief get the number of blocks to sync in one go
*
* @return the number of blocks to sync in one go
@@ -728,6 +745,13 @@ namespace cryptonote
*/
bool get_testnet() const { return m_testnet; };
+ /**
+ * @brief get whether fluffy blocks are enabled
+ *
+ * @return whether fluffy blocks are enabled
+ */
+ bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
+
private:
/**
@@ -933,7 +957,7 @@ namespace cryptonote
std::unordered_set<crypto::hash> bad_semantics_txes[2];
boost::mutex bad_semantics_txes_lock;
- tools::thread_group m_threadpool;
+ tools::threadpool& m_threadpool;
enum {
UPDATES_DISABLED,
@@ -945,6 +969,8 @@ namespace cryptonote
tools::download_async_handle m_update_download;
size_t m_last_update_length;
boost::mutex m_update_mutex;
+
+ bool m_fluffy_blocks_enabled;
};
}
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 68e0b556f..7de392036 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -80,7 +80,7 @@ namespace cryptonote
uint64_t get_transaction_size_limit(uint8_t version)
{
- return get_min_block_size(version) * 125 / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ return get_min_block_size(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
// This class is meant to create a batch when none currently exists.
@@ -92,7 +92,7 @@ namespace cryptonote
LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false) {
m_batch = m_blockchain.get_db().batch_start();
}
- ~LockedTXN() { if (m_batch) { m_blockchain.get_db().batch_stop(); } }
+ ~LockedTXN() { try { if (m_batch) { m_blockchain.get_db().batch_stop(); } } catch (const std::exception &e) { MWARNING("LockedTXN dtor filtering exception: " << e.what()); } }
private:
Blockchain &m_blockchain;
bool m_batch;
@@ -684,6 +684,65 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ bool tx_memory_pool::get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+ m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
+ cryptonote::rpc::tx_in_pool txi;
+ txi.tx_hash = txid;
+ transaction tx;
+ if (!parse_and_validate_tx_from_blob(*bd, tx))
+ {
+ MERROR("Failed to parse tx from txpool");
+ // continue
+ return true;
+ }
+ txi.tx = tx;
+ txi.blob_size = meta.blob_size;
+ txi.fee = meta.fee;
+ txi.kept_by_block = meta.kept_by_block;
+ txi.max_used_block_height = meta.max_used_block_height;
+ txi.max_used_block_hash = meta.max_used_block_id;
+ txi.last_failed_block_height = meta.last_failed_height;
+ txi.last_failed_block_hash = meta.last_failed_id;
+ txi.receive_time = meta.receive_time;
+ txi.relayed = meta.relayed;
+ txi.last_relayed_time = meta.last_relayed_time;
+ txi.do_not_relay = meta.do_not_relay;
+ tx_infos.push_back(txi);
+ return true;
+ }, true);
+
+ for (const key_images_container::value_type& kee : m_spent_key_images) {
+ std::vector<crypto::hash> tx_hashes;
+ const std::unordered_set<crypto::hash>& kei_image_set = kee.second;
+ for (const crypto::hash& tx_id_hash : kei_image_set)
+ {
+ tx_hashes.push_back(tx_id_hash);
+ }
+
+ const crypto::key_image& k_image = kee.first;
+ key_image_infos[k_image] = tx_hashes;
+ }
+ return true;
+ }
+ //---------------------------------------------------------------------------------
+ bool tx_memory_pool::check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+
+ spent.clear();
+
+ for (const auto& image : key_images)
+ {
+ spent.push_back(m_spent_key_images.find(image) == m_spent_key_images.end() ? false : true);
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------------
bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 6414620c9..3e4ccb338 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -46,6 +46,7 @@
#include "blockchain_db/blockchain_db.h"
#include "crypto/hash.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "rpc/message_data_structs.h"
namespace cryptonote
{
@@ -269,6 +270,28 @@ namespace cryptonote
bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const;
/**
+ * @brief get information about all transactions and key images in the pool
+ *
+ * see documentation on tx_in_pool and key_images_with_tx_hashes for more details
+ *
+ * @param tx_infos [out] the transactions' information
+ * @param key_image_infos [out] the spent key images' information
+ *
+ * @return true
+ */
+ bool get_pool_for_rpc(std::vector<cryptonote::rpc::tx_in_pool>& tx_infos, cryptonote::rpc::key_images_with_tx_hashes& key_image_infos) const;
+
+ /**
+ * @brief check for presence of key images in the pool
+ *
+ * @param key_images [in] vector of key images to check
+ * @param spent [out] vector of bool to return
+ *
+ * @return true
+ */
+ bool check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const;
+
+ /**
* @brief get a specific transaction from the pool
*
* @param h the hash of the transaction to get
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 7d95f134b..803d948cc 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -1622,7 +1622,7 @@ skip:
{
if (peer_id && exclude_context.m_connection_id != context.m_connection_id)
{
- if(m_core.get_testnet() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS))
+ if(m_core.fluffy_blocks_enabled() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS))
{
LOG_DEBUG_CC(context, "PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK");
fluffyConnections.push_back(context.m_connection_id);
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index 795442a2d..782667867 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -92,12 +92,15 @@ target_link_libraries(daemon
p2p
cryptonote_protocol
daemonizer
+ serialization
+ daemon_rpc_server
${Boost_CHRONO_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_REGEX_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
+ ${ZMQ_LIB}
${EXTRA_LIBRARIES})
add_dependencies(daemon version)
set_property(TARGET daemon
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index 8eb3db195..f19d5cc63 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -64,6 +64,25 @@ namespace daemon_args
, "Max number of threads to use for a parallel job"
, 0
};
+
+ const command_line::arg_descriptor<std::string> arg_zmq_rpc_bind_ip = {
+ "zmq-rpc-bind-ip"
+ , "IP for ZMQ RPC server to listen on"
+ , "127.0.0.1"
+ };
+
+ const command_line::arg_descriptor<std::string> arg_zmq_rpc_bind_port = {
+ "zmq-rpc-bind-port"
+ , "Port for ZMQ RPC server to listen on"
+ , std::to_string(config::ZMQ_RPC_DEFAULT_PORT)
+ };
+
+ const command_line::arg_descriptor<std::string> arg_zmq_testnet_rpc_bind_port = {
+ "zmq-testnet-rpc-bind-port"
+ , "Port for testnet ZMQ RPC server to listen on"
+ , std::to_string(config::testnet::ZMQ_RPC_DEFAULT_PORT)
+ };
+
} // namespace daemon_args
#endif // DAEMON_COMMAND_LINE_ARGS_H
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 9df698547..b9f503c6b 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -241,7 +241,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"bc_dyn_stats"
, std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1)
- , "Print information about current blockchain dynamic state"
+ , "Print information about current blockchain dynamic state, bc_dyn_stats <last n blocks>"
);
m_command_lookup.set_handler(
"update"
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 683eaf4ff..faa620c54 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -32,6 +32,8 @@
#include <stdexcept>
#include "misc_log_ex.h"
#include "daemon/daemon.h"
+#include "rpc/daemon_handler.h"
+#include "rpc/zmq_server.h"
#include "common/password.h"
#include "common/util.h"
@@ -85,7 +87,18 @@ t_daemon::t_daemon(
boost::program_options::variables_map const & vm
)
: mp_internals{new t_internals{vm}}
-{}
+{
+ bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+ if (testnet)
+ {
+ zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_testnet_rpc_bind_port);
+ }
+ else
+ {
+ zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port);
+ }
+ zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip);
+}
t_daemon::~t_daemon() = default;
@@ -133,6 +146,30 @@ bool t_daemon::run(bool interactive)
rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
}
+ cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get());
+ cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler);
+
+ if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port))
+ {
+ LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address
+ + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server");
+
+ if (interactive)
+ {
+ rpc_commands->stop_handling();
+ }
+
+ mp_internals->rpc.stop();
+
+ return false;
+ }
+
+ MINFO("Starting ZMQ server...");
+ zmq_server.run();
+
+ MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address
+ + ":" + zmq_rpc_bind_port + ".");
+
mp_internals->p2p.run(); // blocks until p2p goes down
if (rpc_commands)
@@ -140,6 +177,8 @@ bool t_daemon::run(bool interactive)
rpc_commands->stop_handling();
}
+ zmq_server.stop();
+
mp_internals->rpc.stop();
mp_internals->core.get().get_miner().stop();
MGINFO("Node stopped.");
diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h
index 2b9f18669..8c7547f62 100644
--- a/src/daemon/daemon.h
+++ b/src/daemon/daemon.h
@@ -43,6 +43,8 @@ private:
void stop_p2p();
private:
std::unique_ptr<t_internals> mp_internals;
+ std::string zmq_rpc_bind_address;
+ std::string zmq_rpc_bind_port;
public:
t_daemon(
boost::program_options::variables_map const & vm
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 44d2dae43..8f6d542b6 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -89,6 +89,9 @@ int main(int argc, char const * argv[])
command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string());
command_line::add_arg(core_settings, daemon_args::arg_log_level);
command_line::add_arg(core_settings, daemon_args::arg_max_concurrency);
+ command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip);
+ command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port);
+ command_line::add_arg(core_settings, daemon_args::arg_zmq_testnet_rpc_bind_port);
daemonizer::init_options(hidden_options, visible_options);
daemonize::t_executor::init_options(core_settings);
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 3d6a01cd1..cda6f3f95 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -420,16 +420,17 @@ bool t_rpc_command_executor::show_status() {
}
std::time_t uptime = std::time(nullptr) - ires.start_time;
+ uint64_t net_height = ires.target_height > ires.height ? ires.target_height : ires.height;
tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
% (unsigned long long)ires.height
- % (unsigned long long)(ires.target_height >= ires.height ? ires.target_height : ires.height)
+ % (unsigned long long)net_height
% get_sync_percentage(ires)
% (ires.testnet ? "testnet" : "mainnet")
% (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining")
% get_mining_speed(ires.difficulty / ires.target)
% (unsigned)hfres.version
- % get_fork_extra_info(hfres.earliest_height, ires.height, ires.target)
+ % get_fork_extra_info(hfres.earliest_height, net_height, ires.target)
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
% (unsigned)ires.outgoing_connections_count
% (unsigned)ires.incoming_connections_count
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 22ae5ec26..8bbaa9138 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -105,7 +105,7 @@ namespace nodetool
bool init(const boost::program_options::variables_map& vm);
bool deinit();
bool send_stop_signal();
- uint32_t get_this_peer_port(){return m_listenning_port;}
+ uint32_t get_this_peer_port(){return m_listening_port;}
t_payload_net_handler& get_payload_object();
template <class Archive, class t_version_type>
@@ -218,6 +218,8 @@ namespace nodetool
bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer);
bool is_addr_connected(const epee::net_utils::network_address& peer);
+ void add_upnp_port_mapping(uint32_t port);
+ void delete_upnp_port_mapping(uint32_t port);
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb);
bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f);
@@ -287,7 +289,7 @@ namespace nodetool
bool m_have_address;
bool m_first_connection_maker_call;
- uint32_t m_listenning_port;
+ uint32_t m_listening_port;
uint32_t m_external_port;
uint32_t m_ip_address;
bool m_allow_local_ip;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 889cfaf9d..13352c24a 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -573,55 +573,15 @@ namespace nodetool
res = m_net_server.init_server(m_port, m_bind_ip);
CHECK_AND_ASSERT_MES(res, false, "Failed to bind server");
- m_listenning_port = m_net_server.get_binded_port();
- MLOG_GREEN(el::Level::Info, "Net service bound to " << m_bind_ip << ":" << m_listenning_port);
+ m_listening_port = m_net_server.get_binded_port();
+ MLOG_GREEN(el::Level::Info, "Net service bound to " << m_bind_ip << ":" << m_listening_port);
if(m_external_port)
MDEBUG("External port defined as " << m_external_port);
- // Add UPnP port mapping
- if(m_no_igd == false) {
- MDEBUG("Attempting to add IGD port mapping.");
- int result;
-#if MINIUPNPC_API_VERSION > 13
- // default according to miniupnpc.h
- unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
-#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
-#endif
- UPNPUrls urls;
- IGDdatas igdData;
- char lanAddress[64];
- result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
- freeUPNPDevlist(deviceList);
- if (result != 0) {
- if (result == 1) {
- std::ostringstream portString;
- portString << m_listenning_port;
-
- // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly
- UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
-
- int portMappingResult;
- portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0");
- if (portMappingResult != 0) {
- LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult));
- } else {
- MLOG_GREEN(el::Level::Info, "Added IGD port mapping.");
- }
- } else if (result == 2) {
- MWARNING("IGD was found but reported as not connected.");
- } else if (result == 3) {
- MWARNING("UPnP device was found but not recognized as IGD.");
- } else {
- MWARNING("UPNP_GetValidIGD returned an unknown result code.");
- }
+ // add UPnP port mapping
+ if(!m_no_igd)
+ add_upnp_port_mapping(m_listening_port);
- FreeUPNPUrls(&urls);
- } else {
- MINFO("No IGD was found.");
- }
- }
return res;
}
//-----------------------------------------------------------------------------------
@@ -688,6 +648,9 @@ namespace nodetool
kill();
m_peerlist.deinit();
m_net_server.deinit_server();
+ // remove UPnP port mapping
+ if(!m_no_igd)
+ delete_upnp_port_mapping(m_listening_port);
return store_config();
}
//-----------------------------------------------------------------------------------
@@ -1389,7 +1352,7 @@ namespace nodetool
node_data.local_time = local_time;
node_data.peer_id = m_config.m_peer_id;
if(!m_hide_my_port)
- node_data.my_port = m_external_port ? m_external_port : m_listenning_port;
+ node_data.my_port = m_external_port ? m_external_port : m_listening_port;
else
node_data.my_port = 0;
node_data.network_id = m_network_id;
@@ -1973,4 +1936,93 @@ namespace nodetool
return true;
}
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port)
+ {
+ MDEBUG("Attempting to add IGD port mapping.");
+ int result;
+#if MINIUPNPC_API_VERSION > 13
+ // default according to miniupnpc.h
+ unsigned char ttl = 2;
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+#else
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+#endif
+ UPNPUrls urls;
+ IGDdatas igdData;
+ char lanAddress[64];
+ result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
+ freeUPNPDevlist(deviceList);
+ if (result != 0) {
+ if (result == 1) {
+ std::ostringstream portString;
+ portString << port;
+
+ // Delete the port mapping before we create it, just in case we have dangling port mapping from the daemon not being shut down correctly
+ UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
+
+ int portMappingResult;
+ portMappingResult = UPNP_AddPortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), portString.str().c_str(), lanAddress, CRYPTONOTE_NAME, "TCP", 0, "0");
+ if (portMappingResult != 0) {
+ LOG_ERROR("UPNP_AddPortMapping failed, error: " << strupnperror(portMappingResult));
+ } else {
+ MLOG_GREEN(el::Level::Info, "Added IGD port mapping.");
+ }
+ } else if (result == 2) {
+ MWARNING("IGD was found but reported as not connected.");
+ } else if (result == 3) {
+ MWARNING("UPnP device was found but not recognized as IGD.");
+ } else {
+ MWARNING("UPNP_GetValidIGD returned an unknown result code.");
+ }
+
+ FreeUPNPUrls(&urls);
+ } else {
+ MINFO("No IGD was found.");
+ }
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ {
+ MDEBUG("Attempting to delete IGD port mapping.");
+ int result;
+#if MINIUPNPC_API_VERSION > 13
+ // default according to miniupnpc.h
+ unsigned char ttl = 2;
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+#else
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+#endif
+ UPNPUrls urls;
+ IGDdatas igdData;
+ char lanAddress[64];
+ result = UPNP_GetValidIGD(deviceList, &urls, &igdData, lanAddress, sizeof lanAddress);
+ freeUPNPDevlist(deviceList);
+ if (result != 0) {
+ if (result == 1) {
+ std::ostringstream portString;
+ portString << port;
+
+ int portMappingResult;
+ portMappingResult = UPNP_DeletePortMapping(urls.controlURL, igdData.first.servicetype, portString.str().c_str(), "TCP", 0);
+ if (portMappingResult != 0) {
+ LOG_ERROR("UPNP_DeletePortMapping failed, error: " << strupnperror(portMappingResult));
+ } else {
+ MLOG_GREEN(el::Level::Info, "Deleted IGD port mapping.");
+ }
+ } else if (result == 2) {
+ MWARNING("IGD was found but reported as not connected.");
+ } else if (result == 3) {
+ MWARNING("UPnP device was found but not recognized as IGD.");
+ } else {
+ MWARNING("UPNP_GetValidIGD returned an unknown result code.");
+ }
+
+ FreeUPNPUrls(&urls);
+ } else {
+ MINFO("No IGD was found.");
+ }
+ }
}
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 8efd6a07c..946325367 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -30,8 +30,7 @@
#include "misc_log_ex.h"
#include "common/perf_timer.h"
-#include "common/task_region.h"
-#include "common/thread_group.h"
+#include "common/threadpool.h"
#include "common/util.h"
#include "rctSigs.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -731,17 +730,16 @@ namespace rct {
try
{
if (semantics) {
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
std::deque<bool> results(rv.outPk.size(), false);
- tools::thread_group threadpool(tools::thread_group::optimal_with_max(rv.outPk.size()));
-
- tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
- DP("range proofs verified?");
- for (size_t i = 0; i < rv.outPk.size(); i++) {
- region.run([&, i] {
- results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
- });
- }
- });
+ DP("range proofs verified?");
+ for (size_t i = 0; i < rv.outPk.size(); i++) {
+ tpool.submit(&waiter, [&, i] {
+ results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ });
+ }
+ waiter.wait();
for (size_t i = 0; i < rv.outPk.size(); ++i) {
if (!results[i]) {
@@ -794,7 +792,8 @@ namespace rct {
const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size());
std::deque<bool> results(threads);
- tools::thread_group threadpool(tools::thread_group::optimal_with_max(threads));
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
if (semantics) {
key sumOutpks = identity();
@@ -819,13 +818,12 @@ namespace rct {
results.clear();
results.resize(rv.outPk.size());
- tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
- for (size_t i = 0; i < rv.outPk.size(); i++) {
- region.run([&, i] {
- results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
- });
- }
- });
+ for (size_t i = 0; i < rv.outPk.size(); i++) {
+ tpool.submit(&waiter, [&, i] {
+ results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ });
+ }
+ waiter.wait();
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
@@ -839,13 +837,12 @@ namespace rct {
results.clear();
results.resize(rv.mixRing.size());
- tools::task_region(threadpool, [&] (tools::task_region_handle& region) {
- for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
- region.run([&, i] {
+ for (size_t i = 0 ; i < rv.mixRing.size() ; i++) {
+ tpool.submit(&waiter, [&, i] {
results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]);
- });
- }
- });
+ });
+ }
+ waiter.wait();
for (size_t i = 0; i < results.size(); ++i) {
if (!results[i]) {
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index f6037d7e3..b5c38b1a8 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -30,20 +30,61 @@ set(rpc_sources
core_rpc_server.cpp
rpc_args.cpp)
+set(daemon_messages_sources
+ message.cpp
+ daemon_messages.cpp)
+
+set(daemon_rpc_server_sources
+ daemon_handler.cpp
+ zmq_server.cpp)
+
+
set(rpc_headers
rpc_args.h)
-set(rpc_private_headers
+set(daemon_rpc_server_headers)
+
+
+set(rpc_daemon_private_headers
core_rpc_server.h
core_rpc_server_commands_defs.h
core_rpc_server_error_codes.h)
+set(daemon_messages_private_headers
+ message.h
+ daemon_messages.h)
+
+set(daemon_rpc_server_private_headers
+ message.h
+ daemon_messages.h
+ daemon_handler.h
+ rpc_handler.h
+ zmq_server.h)
+
+
monero_private_headers(rpc
${rpc_private_headers})
+
+monero_private_headers(daemon_rpc_server
+ ${daemon_rpc_server_private_headers})
+
+
monero_add_library(rpc
${rpc_sources}
${rpc_headers}
${rpc_private_headers})
+
+monero_add_library(daemon_messages
+ ${daemon_messages_sources}
+ ${daemon_messages_headers}
+ ${daemon_messages_private_headers})
+
+monero_add_library(daemon_rpc_server
+ ${daemon_rpc_server_sources}
+ ${daemon_rpc_server_headers}
+ ${daemon_rpc_server_private_headers})
+
+
target_link_libraries(rpc
PUBLIC
common
@@ -54,5 +95,32 @@ target_link_libraries(rpc
${Boost_THREAD_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
+
+target_link_libraries(daemon_messages
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ serialization
+ ${EXTRA_LIBRARIES})
+
+target_link_libraries(daemon_rpc_server
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ daemon_messages
+ serialization
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_REGEX_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${ZMQ_LIB}
+ ${EXTRA_LIBRARIES})
+target_include_directories(daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH})
+target_include_directories(obj_daemon_rpc_server PUBLIC ${ZMQ_INCLUDE_PATH})
+
+
add_dependencies(rpc
version)
+
+add_dependencies(daemon_rpc_server
+ version)
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index a6a61705b..1f7f4a1ff 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -137,7 +137,7 @@ namespace cryptonote
res.top_block_hash = string_tools::pod_to_hex(top_hash);
res.target_height = m_core.get_target_blockchain_height();
res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block();
- res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
+ res.target = m_core.get_blockchain_storage().get_difficulty_target();
res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase
res.tx_pool_size = m_core.get_pool_transactions_count();
res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count();
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index b526277a8..dbbe07972 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -125,7 +125,7 @@ namespace cryptonote
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted)
- MAP_JON_RPC_WE_IF("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG, !m_restricted)
+ MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG)
END_JSON_RPC_MAP()
END_URI_MAP2()
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
new file mode 100644
index 000000000..53eeb5e76
--- /dev/null
+++ b/src/rpc/daemon_handler.cpp
@@ -0,0 +1,887 @@
+// Copyright (c) 2017, 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_handler.h"
+
+// likely included by daemon_handler.h's includes,
+// but including here for clarity
+#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_protocol/blobdatatype.h"
+#include "ringct/rctSigs.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ void DaemonHandler::handle(const GetHeight::Request& req, GetHeight::Response& res)
+ {
+ res.height = m_core.get_current_blockchain_height();
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res)
+ {
+ std::list<std::pair<blobdata, std::list<blobdata> > > blocks;
+
+ if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, blocks, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::find_blockchain_supplement() returned false";
+ return;
+ }
+
+ res.blocks.resize(blocks.size());
+ res.output_indices.resize(blocks.size());
+
+ //TODO: really need to switch uses of std::list to std::vector unless
+ // it's a huge performance concern
+
+ auto it = blocks.begin();
+
+ uint64_t block_count = 0;
+ while (it != blocks.end())
+ {
+ cryptonote::rpc::block_with_transactions& bwt = res.blocks[block_count];
+
+ if (!parse_and_validate_block_from_blob(it->first, bwt.block))
+ {
+ res.blocks.clear();
+ res.output_indices.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "failed retrieving a requested block";
+ return;
+ }
+
+ if (it->second.size() != bwt.block.tx_hashes.size())
+ {
+ res.blocks.clear();
+ res.output_indices.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "incorrect number of transactions retrieved for block";
+ return;
+ }
+ std::list<transaction> txs;
+ for (const auto& blob : it->second)
+ {
+ txs.resize(txs.size() + 1);
+ if (!parse_and_validate_tx_from_blob(blob, txs.back()))
+ {
+ res.blocks.clear();
+ res.output_indices.clear();
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "failed retrieving a requested transaction";
+ return;
+ }
+ }
+
+ cryptonote::rpc::block_output_indices& indices = res.output_indices[block_count];
+
+ // miner tx output indices
+ {
+ cryptonote::rpc::tx_output_indices tx_indices;
+ bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(bwt.block.miner_tx), tx_indices);
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+ indices.push_back(tx_indices);
+ }
+
+ // assume each block returned is returned with all its transactions
+ // in the correct order.
+ auto tx_it = txs.begin();
+ for (const crypto::hash& h : bwt.block.tx_hashes)
+ {
+ bwt.transactions.emplace(h, *tx_it);
+ tx_it++;
+
+ cryptonote::rpc::tx_output_indices tx_indices;
+ bool r = m_core.get_tx_outputs_gindexs(h, tx_indices);
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+
+ indices.push_back(tx_indices);
+ }
+
+ it++;
+ block_count++;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetHashesFast::Request& req, GetHashesFast::Response& res)
+ {
+ res.start_height = req.start_height;
+
+ auto& chain = m_core.get_blockchain_storage();
+
+ if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Blockchain::find_blockchain_supplement() returned false";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetTransactions::Request& req, GetTransactions::Response& res)
+ {
+ std::list<cryptonote::transaction> found_txs;
+ std::list<crypto::hash> missed_hashes;
+
+ bool r = m_core.get_transactions(req.tx_hashes, found_txs, missed_hashes);
+
+ // TODO: consider fixing core::get_transactions to not hide exceptions
+ if (!r)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_transactions() returned false (exception caught there)";
+ return;
+ }
+
+ size_t num_found = found_txs.size();
+
+ // std::list is annoying
+ std::vector<cryptonote::transaction> found_txs_vec
+ {
+ std::make_move_iterator(std::begin(found_txs)),
+ std::make_move_iterator(std::end(found_txs))
+ };
+
+ std::vector<crypto::hash> missed_vec
+ {
+ std::make_move_iterator(std::begin(missed_hashes)),
+ std::make_move_iterator(std::end(missed_hashes))
+ };
+
+ std::vector<uint64_t> heights(num_found);
+ std::vector<bool> in_pool(num_found, false);
+ std::vector<crypto::hash> found_hashes(num_found);
+
+ for (size_t i=0; i < num_found; i++)
+ {
+ found_hashes[i] = get_transaction_hash(found_txs_vec[i]);
+ heights[i] = m_core.get_blockchain_storage().get_db().get_tx_block_height(found_hashes[i]);
+ }
+
+ // if any missing from blockchain, check in tx pool
+ if (!missed_vec.empty())
+ {
+ std::list<cryptonote::transaction> pool_txs;
+
+ m_core.get_pool_transactions(pool_txs);
+
+ for (const auto& tx : pool_txs)
+ {
+ crypto::hash h = get_transaction_hash(tx);
+
+ auto itr = std::find(missed_vec.begin(), missed_vec.end(), h);
+
+ if (itr != missed_vec.end())
+ {
+ found_hashes.push_back(h);
+ found_txs_vec.push_back(tx);
+ heights.push_back(std::numeric_limits<uint64_t>::max());
+ in_pool.push_back(true);
+ missed_vec.erase(itr);
+ }
+ }
+ }
+
+ for (size_t i=0; i < found_hashes.size(); i++)
+ {
+ cryptonote::rpc::transaction_info info;
+ info.height = heights[i];
+ info.in_pool = in_pool[i];
+ info.transaction = std::move(found_txs_vec[i]);
+
+ res.txs.emplace(found_hashes[i], std::move(info));
+ }
+
+ res.missed_hashes = std::move(missed_vec);
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res)
+ {
+ res.spent_status.resize(req.key_images.size(), KeyImagesSpent::STATUS::UNSPENT);
+
+ std::vector<bool> chain_spent_status;
+ std::vector<bool> pool_spent_status;
+
+ m_core.are_key_images_spent(req.key_images, chain_spent_status);
+ m_core.are_key_images_spent_in_pool(req.key_images, pool_spent_status);
+
+ if ((chain_spent_status.size() != req.key_images.size()) || (pool_spent_status.size() != req.key_images.size()))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "tx_pool::have_key_images_as_spent() gave vectors of wrong size(s).";
+ return;
+ }
+
+ for(size_t i=0; i < req.key_images.size(); i++)
+ {
+ if ( chain_spent_status[i] )
+ {
+ res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_BLOCKCHAIN;
+ }
+ else if ( pool_spent_status[i] )
+ {
+ res.spent_status[i] = KeyImagesSpent::STATUS::SPENT_IN_POOL;
+ }
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res)
+ {
+ if (!m_core.get_tx_outputs_gindexs(req.tx_hash, res.output_indices))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "core::get_tx_outputs_gindexs() returned false";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+
+ }
+
+ //TODO: handle "restricted" RPC
+ void DaemonHandler::handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res)
+ {
+ auto& chain = m_core.get_blockchain_storage();
+
+ try
+ {
+ for (const uint64_t& amount : req.amounts)
+ {
+ std::vector<uint64_t> indices = chain.get_random_outputs(amount, req.count);
+
+ outputs_for_amount ofa;
+
+ ofa.resize(indices.size());
+
+ for (size_t i = 0; i < indices.size(); i++)
+ {
+ crypto::public_key key = chain.get_output_key(amount, indices[i]);
+ ofa[i].amount_index = indices[i];
+ ofa[i].key = key;
+ }
+
+ amount_with_random_outputs amt;
+ amt.amount = amount;
+ amt.outputs = ofa;
+
+ res.amounts_with_outputs.push_back(amt);
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+ catch (const std::exception& e)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = e.what();
+ }
+ }
+
+ void DaemonHandler::handle(const SendRawTx::Request& req, SendRawTx::Response& res)
+ {
+ auto tx_blob = cryptonote::tx_to_blob(req.tx);
+
+ cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
+ tx_verification_context tvc = AUTO_VAL_INIT(tvc);
+
+ if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !req.relay) || tvc.m_verifivation_failed)
+ {
+ if (tvc.m_verifivation_failed)
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed");
+ }
+ else
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx");
+ }
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "";
+
+ if (tvc.m_low_mixin)
+ {
+ res.error_details = "mixin too low";
+ }
+ if (tvc.m_double_spend)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "double spend";
+ }
+ if (tvc.m_invalid_input)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "invalid input";
+ }
+ if (tvc.m_invalid_output)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "invalid output";
+ }
+ if (tvc.m_too_big)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "too big";
+ }
+ if (tvc.m_overspend)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "overspend";
+ }
+ if (tvc.m_fee_too_low)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "fee too low";
+ }
+ if (tvc.m_not_rct)
+ {
+ if (!res.error_details.empty()) res.error_details += " and ";
+ res.error_details = "tx is not ringct";
+ }
+ if (res.error_details.empty())
+ {
+ res.error_details = "an unknown issue was found with the transaction";
+ }
+
+ return;
+ }
+
+ if(!tvc.m_should_be_relayed || !req.relay)
+ {
+ LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed");
+ res.error_details = "Not relayed";
+ res.relayed = false;
+ res.status = Message::STATUS_OK;
+
+ return;
+ }
+
+ NOTIFY_NEW_TRANSACTIONS::request r;
+ r.txs.push_back(tx_blob);
+ m_core.get_protocol()->relay_transactions(r, fake_context);
+
+ //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
+ res.status = Message::STATUS_OK;
+ res.relayed = true;
+
+ return;
+ }
+
+ void DaemonHandler::handle(const StartMining::Request& req, StartMining::Response& res)
+ {
+ account_public_address adr;
+ if(!get_account_address_from_str(adr, m_core.get_testnet(), req.miner_address))
+ {
+ res.error_details = "Failed, wrong address";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+
+ unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
+
+ // if we couldn't detect threads, set it to a ridiculously high number
+ if(concurrency_count == 0)
+ {
+ concurrency_count = 257;
+ }
+
+ // if there are more threads requested than the hardware supports
+ // then we fail and log that.
+ if(req.threads_count > concurrency_count)
+ {
+ res.error_details = "Failed, too many threads relative to CPU cores.";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+
+ boost::thread::attributes attrs;
+ attrs.set_stack_size(THREAD_STACK_SIZE);
+
+ if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
+ {
+ res.error_details = "Failed, mining not started";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+
+ }
+
+ void DaemonHandler::handle(const GetInfo::Request& req, GetInfo::Response& res)
+ {
+ res.info.height = m_core.get_current_blockchain_height();
+
+ res.info.target_height = m_core.get_target_blockchain_height();
+
+ if (res.info.height > res.info.target_height)
+ {
+ res.info.target_height = res.info.height;
+ }
+
+ auto& chain = m_core.get_blockchain_storage();
+
+ res.info.difficulty = chain.get_difficulty_for_next_block();
+
+ res.info.target = chain.get_difficulty_target();
+
+ res.info.tx_count = chain.get_total_transactions() - res.info.height; //without coinbase
+
+ res.info.tx_pool_size = m_core.get_pool_transactions_count();
+
+ res.info.alt_blocks_count = chain.get_alternative_blocks_count();
+
+ uint64_t total_conn = m_p2p.get_connections_count();
+ res.info.outgoing_connections_count = m_p2p.get_outgoing_connections_count();
+ res.info.incoming_connections_count = total_conn - res.info.outgoing_connections_count;
+
+ res.info.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count();
+
+ res.info.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count();
+
+ res.info.testnet = m_core.get_testnet();
+ res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1);
+ res.info.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
+ res.info.start_time = (uint64_t)m_core.get_start_time();
+
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+ }
+
+ void DaemonHandler::handle(const StopMining::Request& req, StopMining::Response& res)
+ {
+ if(!m_core.get_miner().stop())
+ {
+ res.error_details = "Failed, mining not stopped";
+ LOG_PRINT_L0(res.error_details);
+ res.status = Message::STATUS_FAILED;
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+ }
+
+ void DaemonHandler::handle(const MiningStatus::Request& req, MiningStatus::Response& res)
+ {
+ const cryptonote::miner& lMiner = m_core.get_miner();
+ res.active = lMiner.is_mining();
+ res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled();
+
+ if ( lMiner.is_mining() ) {
+ res.speed = lMiner.get_speed();
+ res.threads_count = lMiner.get_threads_count();
+ const account_public_address& lMiningAdr = lMiner.get_mining_address();
+ res.address = get_account_address_as_str(m_core.get_testnet(), lMiningAdr);
+ }
+
+ res.status = Message::STATUS_OK;
+ res.error_details = "";
+ }
+
+ void DaemonHandler::handle(const SaveBC::Request& req, SaveBC::Response& res)
+ {
+ if (!m_core.get_blockchain_storage().store_blockchain())
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Error storing the blockchain";
+ }
+ else
+ {
+ res.status = Message::STATUS_OK;
+ }
+ }
+
+ void DaemonHandler::handle(const GetBlockHash::Request& req, GetBlockHash::Response& res)
+ {
+ if (m_core.get_current_blockchain_height() <= req.height)
+ {
+ res.hash = cryptonote::null_hash;
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "height given is higher than current chain height";
+ return;
+ }
+
+ res.hash = m_core.get_block_id_by_height(req.height);
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SubmitBlock::Request& req, SubmitBlock::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res)
+ {
+ const crypto::hash block_hash = m_core.get_tail_id();
+
+ if (!getBlockHeaderByHash(block_hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res)
+ {
+ if (!getBlockHeaderByHash(req.hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res)
+ {
+ const crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
+
+ if (!getBlockHeaderByHash(block_hash, res.header))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Requested block does not exist";
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res)
+ {
+ res.headers.resize(req.heights.size());
+
+ for (size_t i=0; i < req.heights.size(); i++)
+ {
+ const crypto::hash block_hash = m_core.get_block_id_by_height(req.heights[i]);
+
+ if (!getBlockHeaderByHash(block_hash, res.headers[i]))
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "A requested block does not exist";
+ return;
+ }
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBlock::Request& req, GetBlock::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetPeerList::Request& req, GetPeerList::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetLogLevel::Request& req, SetLogLevel::Response& res)
+ {
+ if (req.level < 0 || req.level > 4)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "Error: log level not valid";
+ }
+ else
+ {
+ res.status = Message::STATUS_OK;
+ mlog_set_log_level(req.level);
+ }
+ }
+
+ void DaemonHandler::handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res)
+ {
+ bool r = m_core.get_pool_for_rpc(res.transactions, res.key_images);
+
+ if (!r) res.status = Message::STATUS_FAILED;
+ else res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetConnections::Request& req, GetConnections::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StopDaemon::Request& req, StopDaemon::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const HardForkInfo::Request& req, HardForkInfo::Response& res)
+ {
+ const Blockchain &blockchain = m_core.get_blockchain_storage();
+ uint8_t version = req.version > 0 ? req.version : blockchain.get_ideal_hard_fork_version();
+ res.info.version = blockchain.get_current_hard_fork_version();
+ res.info.enabled = blockchain.get_hard_fork_voting_info(version, res.info.window, res.info.votes, res.info.threshold, res.info.earliest_height, res.info.voting);
+ res.info.state = blockchain.get_hard_fork_state();
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetBans::Request& req, GetBans::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const SetBans::Request& req, SetBans::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = "RPC method not yet implemented.";
+ }
+
+ void DaemonHandler::handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res)
+ {
+ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t> > histogram;
+ try
+ {
+ histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts, req.unlocked, req.recent_cutoff);
+ }
+ catch (const std::exception &e)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = e.what();
+ return;
+ }
+
+ res.histogram.clear();
+ res.histogram.reserve(histogram.size());
+ for (const auto &i: histogram)
+ {
+ if (std::get<0>(i.second) >= req.min_count && (std::get<0>(i.second) <= req.max_count || req.max_count == 0))
+ res.histogram.emplace_back(output_amount_count{i.first, std::get<0>(i.second), std::get<1>(i.second), std::get<2>(i.second)});
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res)
+ {
+ try
+ {
+ for (const auto& i : req.outputs)
+ {
+ crypto::public_key key;
+ rct::key mask;
+ bool unlocked;
+ m_core.get_blockchain_storage().get_output_key_mask_unlocked(i.amount, i.index, key, mask, unlocked);
+ res.keys.emplace_back(output_key_mask_unlocked{key, mask, unlocked});
+ }
+ }
+ catch (const std::exception& e)
+ {
+ res.status = Message::STATUS_FAILED;
+ res.error_details = e.what();
+ return;
+ }
+
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res)
+ {
+ res.version = DAEMON_RPC_VERSION_ZMQ;
+ res.status = Message::STATUS_OK;
+ }
+
+ void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res)
+ {
+ res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks);
+ res.status = Message::STATUS_OK;
+ }
+
+ bool DaemonHandler::getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& header)
+ {
+ block b;
+
+ if (!m_core.get_block_by_hash(hash_in, b))
+ {
+ return false;
+ }
+
+ header.hash = hash_in;
+ header.height = boost::get<txin_gen>(b.miner_tx.vin.front()).height;
+
+ header.major_version = b.major_version;
+ header.minor_version = b.minor_version;
+ header.timestamp = b.timestamp;
+ header.nonce = b.nonce;
+ header.prev_id = b.prev_id;
+
+ header.depth = m_core.get_current_blockchain_height() - header.height - 1;
+
+ header.reward = 0;
+ for (const auto& out : b.miner_tx.vout)
+ {
+ header.reward += out.amount;
+ }
+
+ header.difficulty = m_core.get_blockchain_storage().block_difficulty(header.height);
+
+ return true;
+ }
+
+ std::string DaemonHandler::handle(const std::string& request)
+ {
+ MDEBUG("Handling RPC request: " << request);
+
+ Message* resp_message = NULL;
+
+ try
+ {
+ FullMessage req_full(request, true);
+
+ rapidjson::Value& req_json = req_full.getMessage();
+
+ const std::string request_type = req_full.getRequestType();
+
+ // create correct Message subclass and call handle() on it
+ REQ_RESP_TYPES_MACRO(request_type, GetHeight, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlocksFast, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetHashesFast, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetTransactions, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, KeyImagesSpent, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetTxGlobalOutputIndices, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetRandomOutputsForAmounts, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, SendRawTx, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetInfo, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, StartMining, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, StopMining, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, MiningStatus, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, SaveBC, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHash, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetLastBlockHeader, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHash, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeaderByHeight, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetBlockHeadersByHeight, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetPeerList, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, SetLogLevel, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetTransactionPool, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, HardForkInfo, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetOutputHistogram, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetOutputKeys, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetRPCVersion, req_json, resp_message, handle);
+ REQ_RESP_TYPES_MACRO(request_type, GetPerKBFeeEstimate, req_json, resp_message, handle);
+
+ // if none of the request types matches
+ if (resp_message == NULL)
+ {
+ return BAD_REQUEST(request_type, req_full.getID());
+ }
+
+ FullMessage resp_full = FullMessage::responseMessage(resp_message, req_full.getID());
+
+ const std::string response = resp_full.getJson();
+ delete resp_message;
+ resp_message = NULL;
+
+ MDEBUG("Returning RPC response: " << response);
+
+ return response;
+ }
+ catch (const std::exception& e)
+ {
+ if (resp_message)
+ {
+ delete resp_message;
+ }
+
+ return BAD_JSON(e.what());
+ }
+ }
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_handler.h b/src/rpc/daemon_handler.h
new file mode 100644
index 000000000..0d356bad2
--- /dev/null
+++ b/src/rpc/daemon_handler.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2017, 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 "daemon_messages.h"
+#include "daemon_rpc_version.h"
+#include "rpc_handler.h"
+#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_protocol/cryptonote_protocol_handler.h"
+#include "p2p/net_node.h"
+
+namespace
+{
+ typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> > t_p2p;
+} // anonymous namespace
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+class DaemonHandler : public RpcHandler
+{
+ public:
+
+ DaemonHandler(cryptonote::core& c, t_p2p& p2p) : m_core(c), m_p2p(p2p) { }
+
+ ~DaemonHandler() { }
+
+ void handle(const GetHeight::Request& req, GetHeight::Response& res);
+
+ void handle(const GetBlocksFast::Request& req, GetBlocksFast::Response& res);
+
+ void handle(const GetHashesFast::Request& req, GetHashesFast::Response& res);
+
+ void handle(const GetTransactions::Request& req, GetTransactions::Response& res);
+
+ void handle(const KeyImagesSpent::Request& req, KeyImagesSpent::Response& res);
+
+ void handle(const GetTxGlobalOutputIndices::Request& req, GetTxGlobalOutputIndices::Response& res);
+
+ void handle(const GetRandomOutputsForAmounts::Request& req, GetRandomOutputsForAmounts::Response& res);
+
+ void handle(const SendRawTx::Request& req, SendRawTx::Response& res);
+
+ void handle(const StartMining::Request& req, StartMining::Response& res);
+
+ void handle(const GetInfo::Request& req, GetInfo::Response& res);
+
+ void handle(const StopMining::Request& req, StopMining::Response& res);
+
+ void handle(const MiningStatus::Request& req, MiningStatus::Response& res);
+
+ void handle(const SaveBC::Request& req, SaveBC::Response& res);
+
+ void handle(const GetBlockHash::Request& req, GetBlockHash::Response& res);
+
+ void handle(const GetBlockTemplate::Request& req, GetBlockTemplate::Response& res);
+
+ void handle(const SubmitBlock::Request& req, SubmitBlock::Response& res);
+
+ void handle(const GetLastBlockHeader::Request& req, GetLastBlockHeader::Response& res);
+
+ void handle(const GetBlockHeaderByHash::Request& req, GetBlockHeaderByHash::Response& res);
+
+ void handle(const GetBlockHeaderByHeight::Request& req, GetBlockHeaderByHeight::Response& res);
+
+ void handle(const GetBlockHeadersByHeight::Request& req, GetBlockHeadersByHeight::Response& res);
+
+ void handle(const GetBlock::Request& req, GetBlock::Response& res);
+
+ void handle(const GetPeerList::Request& req, GetPeerList::Response& res);
+
+ void handle(const SetLogHashRate::Request& req, SetLogHashRate::Response& res);
+
+ void handle(const SetLogLevel::Request& req, SetLogLevel::Response& res);
+
+ void handle(const GetTransactionPool::Request& req, GetTransactionPool::Response& res);
+
+ void handle(const GetConnections::Request& req, GetConnections::Response& res);
+
+ void handle(const GetBlockHeadersRange::Request& req, GetBlockHeadersRange::Response& res);
+
+ void handle(const StopDaemon::Request& req, StopDaemon::Response& res);
+
+ void handle(const StartSaveGraph::Request& req, StartSaveGraph::Response& res);
+
+ void handle(const StopSaveGraph::Request& req, StopSaveGraph::Response& res);
+
+ void handle(const HardForkInfo::Request& req, HardForkInfo::Response& res);
+
+ void handle(const GetBans::Request& req, GetBans::Response& res);
+
+ void handle(const SetBans::Request& req, SetBans::Response& res);
+
+ void handle(const FlushTransactionPool::Request& req, FlushTransactionPool::Response& res);
+
+ void handle(const GetOutputHistogram::Request& req, GetOutputHistogram::Response& res);
+
+ void handle(const GetOutputKeys::Request& req, GetOutputKeys::Response& res);
+
+ void handle(const GetRPCVersion::Request& req, GetRPCVersion::Response& res);
+
+ void handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res);
+
+ std::string handle(const std::string& request);
+
+ private:
+
+ bool getBlockHeaderByHash(const crypto::hash& hash_in, cryptonote::rpc::BlockHeaderResponse& response);
+
+ cryptonote::core& m_core;
+ t_p2p& m_p2p;
+};
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_messages.cpp b/src/rpc/daemon_messages.cpp
new file mode 100644
index 000000000..640058df9
--- /dev/null
+++ b/src/rpc/daemon_messages.cpp
@@ -0,0 +1,900 @@
+// Copyright (c) 2016-2017, 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_messages.h"
+#include "serialization/json_object.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+const char* const GetHeight::name = "get_height";
+const char* const GetBlocksFast::name = "get_blocks_fast";
+const char* const GetHashesFast::name = "get_hashes_fast";
+const char* const GetTransactions::name = "get_transactions";
+const char* const KeyImagesSpent::name = "key_images_spent";
+const char* const GetTxGlobalOutputIndices::name = "get_tx_global_output_indices";
+const char* const GetRandomOutputsForAmounts::name = "get_random_outputs_for_amounts";
+const char* const SendRawTx::name = "send_raw_tx";
+const char* const StartMining::name = "start_mining";
+const char* const StopMining::name = "stop_mining";
+const char* const MiningStatus::name = "mining_status";
+const char* const GetInfo::name = "get_info";
+const char* const SaveBC::name = "save_bc";
+const char* const GetBlockHash::name = "get_block_hash";
+const char* const GetLastBlockHeader::name = "get_last_block_header";
+const char* const GetBlockHeaderByHash::name = "get_block_header_by_hash";
+const char* const GetBlockHeaderByHeight::name = "get_block_header_by_height";
+const char* const GetBlockHeadersByHeight::name = "get_block_headers_by_height";
+const char* const GetPeerList::name = "get_peer_list";
+const char* const SetLogLevel::name = "set_log_level";
+const char* const GetTransactionPool::name = "get_transaction_pool";
+const char* const HardForkInfo::name = "hard_fork_info";
+const char* const GetOutputHistogram::name = "get_output_histogram";
+const char* const GetOutputKeys::name = "get_output_keys";
+const char* const GetRPCVersion::name = "get_rpc_version";
+const char* const GetPerKBFeeEstimate::name = "get_dynamic_per_kb_fee_estimate";
+
+
+
+
+rapidjson::Value GetHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetHeight::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("height", height, al);
+
+ return val;
+}
+
+void GetHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+
+rapidjson::Value GetBlocksFast::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, block_ids, block_ids);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("prune", prune, al);
+
+ return val;
+}
+
+void GetBlocksFast::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, block_ids, block_ids);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, prune, prune);
+}
+
+rapidjson::Value GetBlocksFast::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, blocks, blocks);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("current_height", current_height, al);
+ INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
+
+ return val;
+}
+
+void GetBlocksFast::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, blocks, blocks);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, current_height, current_height);
+ GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
+}
+
+
+rapidjson::Value GetHashesFast::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, known_hashes, known_hashes);
+ val.AddMember("start_height", start_height, al);
+
+ return val;
+}
+
+void GetHashesFast::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, known_hashes, known_hashes);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+}
+
+rapidjson::Value GetHashesFast::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hashes, hashes);
+ val.AddMember("start_height", start_height, al);
+ val.AddMember("current_height", current_height, al);
+
+ return val;
+}
+
+void GetHashesFast::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hashes, hashes);
+ GET_FROM_JSON_OBJECT(val, start_height, start_height);
+ GET_FROM_JSON_OBJECT(val, current_height, current_height);
+}
+
+
+rapidjson::Value GetTransactions::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, tx_hashes);
+
+ return val;
+}
+
+void GetTransactions::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx_hashes, tx_hashes);
+}
+
+rapidjson::Value GetTransactions::Response::toJson(rapidjson::Document& doc) const
+{
+ rapidjson::Value val(rapidjson::kObjectType);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, txs, txs);
+ INSERT_INTO_JSON_OBJECT(val, doc, missed_hashes, missed_hashes);
+
+ return val;
+}
+
+void GetTransactions::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, txs, txs);
+ GET_FROM_JSON_OBJECT(val, missed_hashes, missed_hashes);
+}
+
+
+rapidjson::Value KeyImagesSpent::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
+
+ return val;
+}
+
+void KeyImagesSpent::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, key_images, key_images);
+}
+
+rapidjson::Value KeyImagesSpent::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, spent_status, spent_status);
+
+ return val;
+}
+
+void KeyImagesSpent::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, spent_status, spent_status);
+}
+
+
+rapidjson::Value GetTxGlobalOutputIndices::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx_hash);
+
+ return val;
+}
+
+void GetTxGlobalOutputIndices::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx_hash, tx_hash);
+}
+
+rapidjson::Value GetTxGlobalOutputIndices::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, output_indices, output_indices);
+
+ return val;
+}
+
+void GetTxGlobalOutputIndices::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, output_indices, output_indices);
+}
+
+
+rapidjson::Value GetRandomOutputsForAmounts::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(val, doc, count, count);
+
+ return val;
+}
+
+void GetRandomOutputsForAmounts::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts, amounts);
+ GET_FROM_JSON_OBJECT(val, count, count);
+}
+
+rapidjson::Value GetRandomOutputsForAmounts::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts_with_outputs, amounts_with_outputs);
+
+ return val;
+}
+
+void GetRandomOutputsForAmounts::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts_with_outputs, amounts_with_outputs);
+}
+
+
+rapidjson::Value SendRawTx::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx, tx);
+ INSERT_INTO_JSON_OBJECT(val, doc, relay, relay);
+
+ return val;
+}
+
+void SendRawTx::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, tx, tx);
+ GET_FROM_JSON_OBJECT(val, relay, relay);
+}
+
+rapidjson::Value SendRawTx::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, relayed, relayed);
+
+ return val;
+}
+
+
+void SendRawTx::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, relayed, relayed);
+}
+
+rapidjson::Value StartMining::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, miner_address, miner_address);
+ INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, do_background_mining, do_background_mining);
+ INSERT_INTO_JSON_OBJECT(val, doc, ignore_battery, ignore_battery);
+
+ return val;
+}
+
+void StartMining::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, miner_address, miner_address);
+ GET_FROM_JSON_OBJECT(val, threads_count, threads_count);
+ GET_FROM_JSON_OBJECT(val, do_background_mining, do_background_mining);
+ GET_FROM_JSON_OBJECT(val, ignore_battery, ignore_battery);
+}
+
+rapidjson::Value StartMining::Response::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void StartMining::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value StopMining::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void StopMining::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value StopMining::Response::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void StopMining::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value MiningStatus::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void MiningStatus::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value MiningStatus::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, active, active);
+ INSERT_INTO_JSON_OBJECT(val, doc, speed, speed);
+ INSERT_INTO_JSON_OBJECT(val, doc, threads_count, threads_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, address, address);
+ INSERT_INTO_JSON_OBJECT(val, doc, is_background_mining_enabled, is_background_mining_enabled);
+
+ return val;
+}
+
+void MiningStatus::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, active, active);
+ GET_FROM_JSON_OBJECT(val, speed, speed);
+ GET_FROM_JSON_OBJECT(val, threads_count, threads_count);
+ GET_FROM_JSON_OBJECT(val, address, address);
+ GET_FROM_JSON_OBJECT(val, is_background_mining_enabled, is_background_mining_enabled);
+}
+
+
+rapidjson::Value GetInfo::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetInfo::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetInfo::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, info, info);
+
+ return val;
+}
+
+void GetInfo::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, info, info);
+}
+
+
+rapidjson::Value SaveBC::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void SaveBC::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value SaveBC::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void SaveBC::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value GetBlockHash::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, height);
+
+ return val;
+}
+
+void GetBlockHash::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+rapidjson::Value GetBlockHash::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
+
+ return val;
+}
+
+void GetBlockHash::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hash, hash);
+}
+
+
+rapidjson::Value GetLastBlockHeader::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void GetLastBlockHeader::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetLastBlockHeader::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetLastBlockHeader::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeaderByHash::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, hash);
+
+ return val;
+}
+
+void GetBlockHeaderByHash::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, hash, hash);
+}
+
+rapidjson::Value GetBlockHeaderByHash::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetBlockHeaderByHash::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeaderByHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, height);
+
+ return val;
+}
+
+void GetBlockHeaderByHeight::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, height, height);
+}
+
+rapidjson::Value GetBlockHeaderByHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, header, header);
+
+ return val;
+}
+
+void GetBlockHeaderByHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, header, header);
+}
+
+
+rapidjson::Value GetBlockHeadersByHeight::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, heights, heights);
+
+ return val;
+}
+
+void GetBlockHeadersByHeight::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, heights, heights);
+}
+
+rapidjson::Value GetBlockHeadersByHeight::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, headers, headers);
+
+ return val;
+}
+
+void GetBlockHeadersByHeight::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, headers, headers);
+}
+
+
+rapidjson::Value GetPeerList::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ return val;
+}
+
+void GetPeerList::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetPeerList::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, white_list, white_list);
+ INSERT_INTO_JSON_OBJECT(val, doc, gray_list, gray_list);
+
+ return val;
+}
+
+void GetPeerList::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, white_list, white_list);
+ GET_FROM_JSON_OBJECT(val, gray_list, gray_list);
+}
+
+
+rapidjson::Value SetLogLevel::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("level", level, al);
+
+ return val;
+}
+
+void SetLogLevel::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, level, level);
+}
+
+rapidjson::Value SetLogLevel::Response::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void SetLogLevel::Response::fromJson(rapidjson::Value& val)
+{
+}
+
+
+rapidjson::Value GetTransactionPool::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetTransactionPool::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetTransactionPool::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, transactions, transactions);
+ INSERT_INTO_JSON_OBJECT(val, doc, key_images, key_images);
+
+ return val;
+}
+
+void GetTransactionPool::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, transactions, transactions);
+ GET_FROM_JSON_OBJECT(val, key_images, key_images);
+}
+
+
+rapidjson::Value HardForkInfo::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, version);
+
+ return val;
+}
+
+void HardForkInfo::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, version, version);
+}
+
+rapidjson::Value HardForkInfo::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, info, info);
+
+ return val;
+}
+
+void HardForkInfo::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, info, info);
+}
+
+
+rapidjson::Value GetOutputHistogram::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amounts, amounts);
+ INSERT_INTO_JSON_OBJECT(val, doc, min_count, min_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, max_count, max_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlocked, unlocked);
+ INSERT_INTO_JSON_OBJECT(val, doc, recent_cutoff, recent_cutoff);
+
+ return val;
+}
+
+void GetOutputHistogram::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, amounts, amounts);
+ GET_FROM_JSON_OBJECT(val, min_count, min_count);
+ GET_FROM_JSON_OBJECT(val, max_count, max_count);
+ GET_FROM_JSON_OBJECT(val, unlocked, unlocked);
+ GET_FROM_JSON_OBJECT(val, recent_cutoff, recent_cutoff);
+}
+
+rapidjson::Value GetOutputHistogram::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, histogram, histogram);
+
+ return val;
+}
+
+void GetOutputHistogram::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, histogram, histogram);
+}
+
+
+rapidjson::Value GetOutputKeys::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, outputs, outputs);
+
+ return val;
+}
+
+void GetOutputKeys::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, outputs, outputs);
+}
+
+rapidjson::Value GetOutputKeys::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, keys, keys);
+
+ return val;
+}
+
+void GetOutputKeys::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, keys, keys);
+}
+
+
+rapidjson::Value GetRPCVersion::Request::toJson(rapidjson::Document& doc) const
+{
+ return Message::toJson(doc);
+}
+
+void GetRPCVersion::Request::fromJson(rapidjson::Value& val)
+{
+}
+
+rapidjson::Value GetRPCVersion::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, version);
+
+ return val;
+}
+
+void GetRPCVersion::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, version, version);
+}
+
+rapidjson::Value GetPerKBFeeEstimate::Request::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, num_grace_blocks, num_grace_blocks);
+
+ return val;
+}
+
+void GetPerKBFeeEstimate::Request::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, num_grace_blocks, num_grace_blocks);
+}
+
+rapidjson::Value GetPerKBFeeEstimate::Response::toJson(rapidjson::Document& doc) const
+{
+ auto val = Message::toJson(doc);
+
+ auto& al = doc.GetAllocator();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, estimated_fee_per_kb, estimated_fee_per_kb);
+
+ return val;
+}
+
+void GetPerKBFeeEstimate::Response::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, estimated_fee_per_kb, estimated_fee_per_kb);
+}
+
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_messages.h b/src/rpc/daemon_messages.h
new file mode 100644
index 000000000..685f66843
--- /dev/null
+++ b/src/rpc/daemon_messages.h
@@ -0,0 +1,421 @@
+// Copyright (c) 2016-2017, 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 "message.h"
+#include "cryptonote_protocol/cryptonote_protocol_defs.h"
+#include "rpc/message_data_structs.h"
+#include "rpc/daemon_rpc_version.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+
+#define BEGIN_RPC_MESSAGE_CLASS(classname) \
+class classname \
+{ \
+ public: \
+ static const char* const name;
+
+#define BEGIN_RPC_MESSAGE_REQUEST \
+ class Request : public Message \
+ { \
+ public: \
+ Request() { } \
+ ~Request() { } \
+ rapidjson::Value toJson(rapidjson::Document& doc) const; \
+ void fromJson(rapidjson::Value& val);
+
+#define BEGIN_RPC_MESSAGE_RESPONSE \
+ class Response : public Message \
+ { \
+ public: \
+ Response() { } \
+ ~Response() { } \
+ rapidjson::Value toJson(rapidjson::Document& doc) const; \
+ void fromJson(rapidjson::Value& val);
+
+#define END_RPC_MESSAGE_REQUEST };
+#define END_RPC_MESSAGE_RESPONSE };
+#define END_RPC_MESSAGE_CLASS };
+
+#define COMMA() ,
+
+// NOTE: when using a type with multiple template parameters,
+// replace any comma in the template specifier with the macro
+// above, or the preprocessor will eat the comma in a bad way.
+#define RPC_MESSAGE_MEMBER(type, name) type name;
+
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+BEGIN_RPC_MESSAGE_CLASS(GetHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlocksFast);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::list<crypto::hash>, block_ids);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ RPC_MESSAGE_MEMBER(bool, prune);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_with_transactions>, blocks);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ RPC_MESSAGE_MEMBER(uint64_t, current_height);
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::block_output_indices>, output_indices);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetHashesFast);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::list<crypto::hash>, known_hashes);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::list<crypto::hash>, hashes);
+ RPC_MESSAGE_MEMBER(uint64_t, start_height);
+ RPC_MESSAGE_MEMBER(uint64_t, current_height);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetTransactions);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, tx_hashes);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::unordered_map<crypto::hash COMMA() cryptonote::rpc::transaction_info>, txs);
+ RPC_MESSAGE_MEMBER(std::vector<crypto::hash>, missed_hashes);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(KeyImagesSpent);
+ enum STATUS {
+ UNSPENT = 0,
+ SPENT_IN_BLOCKCHAIN = 1,
+ SPENT_IN_POOL = 2,
+ };
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<crypto::key_image>, key_images);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, spent_status);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetTxGlobalOutputIndices);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(crypto::hash, tx_hash);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, output_indices);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+
+BEGIN_RPC_MESSAGE_CLASS(GetRandomOutputsForAmounts);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, amounts);
+ RPC_MESSAGE_MEMBER(uint64_t, count);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<amount_with_random_outputs>, amounts_with_outputs);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SendRawTx);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(cryptonote::transaction, tx);
+ RPC_MESSAGE_MEMBER(bool, relay);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(bool, relayed);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StartMining);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::string, miner_address);
+ RPC_MESSAGE_MEMBER(uint64_t, threads_count);
+ RPC_MESSAGE_MEMBER(bool, do_background_mining);
+ RPC_MESSAGE_MEMBER(bool, ignore_battery);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetInfo);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(DaemonInfo, info);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopMining);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(MiningStatus);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(bool, active);
+ RPC_MESSAGE_MEMBER(uint64_t, speed);
+ RPC_MESSAGE_MEMBER(uint64_t, threads_count);
+ RPC_MESSAGE_MEMBER(std::string, address);
+ RPC_MESSAGE_MEMBER(bool, is_background_mining_enabled);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SaveBC);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHash);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(crypto::hash, hash);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockTemplate);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SubmitBlock);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetLastBlockHeader);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHash);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(crypto::hash, hash);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeaderByHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, height);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(cryptonote::rpc::BlockHeaderResponse, header);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersByHeight);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, heights);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::BlockHeaderResponse>, headers);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlock);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetPeerList);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<peer>, white_list);
+ RPC_MESSAGE_MEMBER(std::vector<peer>, gray_list);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SetLogHashRate);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SetLogLevel);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(int8_t, level);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetTransactionPool);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<cryptonote::rpc::tx_in_pool>, transactions);
+ RPC_MESSAGE_MEMBER(key_images_with_tx_hashes, key_images);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetConnections);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBlockHeadersRange);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopDaemon);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StartSaveGraph);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(StopSaveGraph);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(HardForkInfo);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint8_t, version);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(hard_fork_info, info);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetBans);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(SetBans);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(FlushTransactionPool);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetOutputHistogram);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<uint64_t>, amounts);
+ RPC_MESSAGE_MEMBER(uint64_t, min_count);
+ RPC_MESSAGE_MEMBER(uint64_t, max_count);
+ RPC_MESSAGE_MEMBER(bool, unlocked);
+ RPC_MESSAGE_MEMBER(uint64_t, recent_cutoff);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<output_amount_count>, histogram);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetOutputKeys);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(std::vector<output_amount_and_index>, outputs);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(std::vector<output_key_mask_unlocked>, keys);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetRPCVersion);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint32_t, version);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+BEGIN_RPC_MESSAGE_CLASS(GetPerKBFeeEstimate);
+ BEGIN_RPC_MESSAGE_REQUEST;
+ RPC_MESSAGE_MEMBER(uint64_t, num_grace_blocks);
+ END_RPC_MESSAGE_REQUEST;
+ BEGIN_RPC_MESSAGE_RESPONSE;
+ RPC_MESSAGE_MEMBER(uint64_t, estimated_fee_per_kb);
+ END_RPC_MESSAGE_RESPONSE;
+END_RPC_MESSAGE_CLASS;
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/daemon_rpc_version.h b/src/rpc/daemon_rpc_version.h
new file mode 100644
index 000000000..bb735fe7c
--- /dev/null
+++ b/src/rpc/daemon_rpc_version.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2016-2017, 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 cryptonote
+{
+
+namespace rpc
+{
+
+static const uint32_t DAEMON_RPC_VERSION_ZMQ_MINOR = 0;
+static const uint32_t DAEMON_RPC_VERSION_ZMQ_MAJOR = 1;
+
+static const uint32_t DAEMON_RPC_VERSION_ZMQ = DAEMON_RPC_VERSION_ZMQ_MINOR + (DAEMON_RPC_VERSION_ZMQ_MAJOR << 16);
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
new file mode 100644
index 000000000..086820180
--- /dev/null
+++ b/src/rpc/message.cpp
@@ -0,0 +1,286 @@
+// Copyright (c) 2016-2017, 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 "message.h"
+#include "daemon_rpc_version.h"
+#include "serialization/json_object.h"
+
+#include "rapidjson/writer.h"
+#include "rapidjson/stringbuffer.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+const char* Message::STATUS_OK = "OK";
+const char* Message::STATUS_RETRY = "Retry";
+const char* Message::STATUS_FAILED = "Failed";
+const char* Message::STATUS_BAD_REQUEST = "Invalid request type";
+const char* Message::STATUS_BAD_JSON = "Malformed json";
+
+rapidjson::Value Message::toJson(rapidjson::Document& doc) const
+{
+ rapidjson::Value val(rapidjson::kObjectType);
+
+ auto& al = doc.GetAllocator();
+
+ val.AddMember("status", rapidjson::StringRef(status.c_str()), al);
+ val.AddMember("error_details", rapidjson::StringRef(error_details.c_str()), al);
+ INSERT_INTO_JSON_OBJECT(val, doc, rpc_version, DAEMON_RPC_VERSION_ZMQ);
+
+ return val;
+}
+
+void Message::fromJson(rapidjson::Value& val)
+{
+ GET_FROM_JSON_OBJECT(val, status, status);
+ GET_FROM_JSON_OBJECT(val, error_details, error_details);
+ GET_FROM_JSON_OBJECT(val, rpc_version, rpc_version);
+}
+
+
+FullMessage::FullMessage(const std::string& request, Message* message)
+{
+ doc.SetObject();
+
+ doc.AddMember("method", rapidjson::StringRef(request.c_str()), doc.GetAllocator());
+ doc.AddMember("params", message->toJson(doc), doc.GetAllocator());
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", rapidjson::Value("2.0"), doc.GetAllocator());
+}
+
+FullMessage::FullMessage(Message* message)
+{
+ doc.SetObject();
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", "2.0", doc.GetAllocator());
+
+ if (message->status == Message::STATUS_OK)
+ {
+ doc.AddMember("response", message->toJson(doc), doc.GetAllocator());
+ }
+ else
+ {
+ cryptonote::rpc::error err;
+
+ err.error_str = message->status;
+ err.message = message->error_details;
+
+ INSERT_INTO_JSON_OBJECT(doc, doc, error, err);
+ }
+}
+
+FullMessage::FullMessage(const std::string& json_string, bool request)
+{
+ doc.Parse(json_string.c_str());
+ if (doc.HasParseError())
+ {
+ throw cryptonote::json::PARSE_FAIL();
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "jsonrpc")
+
+ if (request)
+ {
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "method")
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "params")
+ }
+ else
+ {
+ if (!doc.HasMember("response") && !doc.HasMember("error"))
+ {
+ throw cryptonote::json::MISSING_KEY("error/response");
+ }
+ }
+}
+
+std::string FullMessage::getJson()
+{
+
+ if (!doc.HasMember("id"))
+ {
+ doc.AddMember("id", rapidjson::Value("unused"), doc.GetAllocator());
+ }
+
+ rapidjson::StringBuffer buf;
+
+ rapidjson::Writer<rapidjson::StringBuffer> writer(buf);
+
+ doc.Accept(writer);
+
+ return std::string(buf.GetString(), buf.GetSize());
+}
+
+std::string FullMessage::getRequestType() const
+{
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "method")
+ return doc["method"].GetString();
+}
+
+rapidjson::Value& FullMessage::getMessage()
+{
+ if (doc.HasMember("params"))
+ {
+ return doc["params"];
+ }
+ else if (doc.HasMember("response"))
+ {
+ return doc["response"];
+ }
+
+ //else
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "error")
+ return doc["error"];
+
+}
+
+rapidjson::Value FullMessage::getMessageCopy()
+{
+ rapidjson::Value& val = getMessage();
+
+ return rapidjson::Value(val, doc.GetAllocator());
+}
+
+rapidjson::Value& FullMessage::getID()
+{
+ OBJECT_HAS_MEMBER_OR_THROW(doc, "id")
+ return doc["id"];
+}
+
+void FullMessage::setID(rapidjson::Value& id)
+{
+ auto itr = doc.FindMember("id");
+ if (itr != doc.MemberEnd())
+ {
+ itr->value = id;
+ }
+ else
+ {
+ doc.AddMember("id", id, doc.GetAllocator());
+ }
+}
+
+cryptonote::rpc::error FullMessage::getError()
+{
+ cryptonote::rpc::error err;
+ err.use = false;
+ if (doc.HasMember("error"))
+ {
+ GET_FROM_JSON_OBJECT(doc, err, error);
+ err.use = true;
+ }
+
+ return err;
+}
+
+FullMessage FullMessage::requestMessage(const std::string& request, Message* message)
+{
+ return FullMessage(request, message);
+}
+
+FullMessage FullMessage::requestMessage(const std::string& request, Message* message, rapidjson::Value& id)
+{
+ auto mes = requestMessage(request, message);
+ mes.setID(id);
+ return mes;
+}
+
+FullMessage FullMessage::responseMessage(Message* message)
+{
+ return FullMessage(message);
+}
+
+FullMessage FullMessage::responseMessage(Message* message, rapidjson::Value& id)
+{
+ auto mes = responseMessage(message);
+ mes.setID(id);
+ return mes;
+}
+
+FullMessage* FullMessage::timeoutMessage()
+{
+ auto *full_message = new FullMessage();
+
+ auto& doc = full_message->doc;
+ auto& al = full_message->doc.GetAllocator();
+
+ doc.SetObject();
+
+ // required by JSON-RPC 2.0 spec
+ doc.AddMember("jsonrpc", "2.0", al);
+
+ cryptonote::rpc::error err;
+
+ err.error_str = "RPC request timed out.";
+ INSERT_INTO_JSON_OBJECT(doc, doc, err, err);
+
+ return full_message;
+}
+
+// convenience functions for bad input
+std::string BAD_REQUEST(const std::string& request)
+{
+ Message fail;
+ fail.status = Message::STATUS_BAD_REQUEST;
+ fail.error_details = std::string("\"") + request + "\" is not a valid request.";
+
+ FullMessage fail_response = FullMessage::responseMessage(&fail);
+
+ return fail_response.getJson();
+}
+
+std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id)
+{
+ Message fail;
+ fail.status = Message::STATUS_BAD_REQUEST;
+ fail.error_details = std::string("\"") + request + "\" is not a valid request.";
+
+ FullMessage fail_response = FullMessage::responseMessage(&fail, id);
+
+ return fail_response.getJson();
+}
+
+std::string BAD_JSON(const std::string& error_details)
+{
+ Message fail;
+ fail.status = Message::STATUS_BAD_JSON;
+ fail.error_details = error_details;
+
+ FullMessage fail_response = FullMessage::responseMessage(&fail);
+
+ return fail_response.getJson();
+}
+
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/message.h b/src/rpc/message.h
new file mode 100644
index 000000000..d1abe3fbe
--- /dev/null
+++ b/src/rpc/message.h
@@ -0,0 +1,131 @@
+// Copyright (c) 2016-2017, 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 "rapidjson/document.h"
+#include "rpc/message_data_structs.h"
+#include <string>
+
+/* I normally hate using macros, but in this case it would be untenably
+ * verbose to not use a macro. This macro saves the trouble of explicitly
+ * writing the below if block for every single RPC call.
+ */
+#define REQ_RESP_TYPES_MACRO( runtime_str, type, reqjson, resp_message_ptr, handler) \
+ \
+ if (runtime_str == type::name) \
+ { \
+ type::Request reqvar; \
+ type::Response *respvar = new type::Response(); \
+ \
+ reqvar.fromJson(reqjson); \
+ \
+ handler(reqvar, *respvar); \
+ \
+ resp_message_ptr = respvar; \
+ }
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ class Message
+ {
+ public:
+ static const char* STATUS_OK;
+ static const char* STATUS_RETRY;
+ static const char* STATUS_FAILED;
+ static const char* STATUS_BAD_REQUEST;
+ static const char* STATUS_BAD_JSON;
+
+ Message() : status(STATUS_OK) { }
+
+ virtual ~Message() { }
+
+ virtual rapidjson::Value toJson(rapidjson::Document& doc) const;
+
+ virtual void fromJson(rapidjson::Value& val);
+
+ std::string status;
+ std::string error_details;
+ uint32_t rpc_version;
+ };
+
+ class FullMessage
+ {
+ public:
+ ~FullMessage() { }
+
+ FullMessage(FullMessage&& rhs) noexcept : doc(std::move(rhs.doc)) { }
+
+ FullMessage(const std::string& json_string, bool request=false);
+
+ std::string getJson();
+
+ std::string getRequestType() const;
+
+ rapidjson::Value& getMessage();
+
+ rapidjson::Value getMessageCopy();
+
+ rapidjson::Value& getID();
+
+ void setID(rapidjson::Value& id);
+
+ cryptonote::rpc::error getError();
+
+ static FullMessage requestMessage(const std::string& request, Message* message);
+ static FullMessage requestMessage(const std::string& request, Message* message, rapidjson::Value& id);
+
+ static FullMessage responseMessage(Message* message);
+ static FullMessage responseMessage(Message* message, rapidjson::Value& id);
+
+ static FullMessage* timeoutMessage();
+ private:
+
+ FullMessage() = default;
+
+ FullMessage(const std::string& request, Message* message);
+ FullMessage(Message* message);
+
+ rapidjson::Document doc;
+ };
+
+
+ // convenience functions for bad input
+ std::string BAD_REQUEST(const std::string& request);
+ std::string BAD_REQUEST(const std::string& request, rapidjson::Value& id);
+
+ std::string BAD_JSON(const std::string& error_details);
+
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
new file mode 100644
index 000000000..00f1e0caa
--- /dev/null
+++ b/src/rpc/message_data_structs.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2016-2017, 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 "crypto/hash.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "ringct/rctSigs.h"
+
+#include <unordered_map>
+#include <vector>
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ struct block_with_transactions
+ {
+ cryptonote::block block;
+ std::unordered_map<crypto::hash, cryptonote::transaction> transactions;
+ };
+
+ typedef std::vector<uint64_t> tx_output_indices;
+
+ typedef std::vector<tx_output_indices> block_output_indices;
+
+ struct transaction_info
+ {
+ cryptonote::transaction transaction;
+ bool in_pool;
+ uint64_t height;
+ };
+
+ struct output_key_and_amount_index
+ {
+ uint64_t amount_index;
+ crypto::public_key key;
+ };
+
+ typedef std::vector<output_key_and_amount_index> outputs_for_amount;
+
+ struct amount_with_random_outputs
+ {
+ uint64_t amount;
+ outputs_for_amount outputs;
+ };
+
+ struct peer
+ {
+ uint64_t id;
+ uint32_t ip;
+ uint16_t port;
+ uint64_t last_seen;
+ };
+
+ struct tx_in_pool
+ {
+ cryptonote::transaction tx;
+ crypto::hash tx_hash;
+ uint64_t blob_size;
+ uint64_t fee;
+ crypto::hash max_used_block_hash;
+ uint64_t max_used_block_height;
+ bool kept_by_block;
+ crypto::hash last_failed_block_hash;
+ uint64_t last_failed_block_height;
+ uint64_t receive_time;
+ uint64_t last_relayed_time;
+ bool relayed;
+ bool do_not_relay;
+ };
+
+ typedef std::unordered_map<crypto::key_image, std::vector<crypto::hash> > key_images_with_tx_hashes;
+
+ struct output_amount_count
+ {
+ uint64_t amount;
+ uint64_t total_count;
+ uint64_t unlocked_count;
+ uint64_t recent_count;
+ };
+
+ struct output_amount_and_index
+ {
+ uint64_t amount;
+ uint64_t index;
+ };
+
+ struct output_key_mask_unlocked
+ {
+ crypto::public_key key;
+ rct::key mask;
+ bool unlocked;
+ };
+
+ struct hard_fork_info
+ {
+ uint8_t version;
+ bool enabled;
+ uint32_t window;
+ uint32_t votes;
+ uint32_t threshold;
+ uint8_t voting;
+ uint32_t state;
+ uint64_t earliest_height;
+ };
+
+ //required by JSON-RPC 2.0 spec
+ struct error
+ {
+ // not really using code, maybe later.
+ error() : use(false), code(1) { }
+
+ bool use; // do not serialize
+
+ int32_t code;
+
+ // not required by spec, but int error codes aren't perfect
+ std::string error_str;
+
+ std::string message;
+
+ //TODO: data member? not required, may want later.
+ };
+
+ struct BlockHeaderResponse
+ {
+ uint64_t major_version;
+ uint64_t minor_version;
+ uint64_t timestamp;
+ crypto::hash prev_id;
+ uint32_t nonce;
+ uint64_t height;
+ uint64_t depth;
+ crypto::hash hash;
+ uint64_t difficulty;
+ uint64_t reward;
+ };
+
+ struct DaemonInfo
+ {
+ uint64_t height;
+ uint64_t target_height;
+ uint64_t difficulty;
+ uint64_t target;
+ uint64_t tx_count;
+ uint64_t tx_pool_size;
+ uint64_t alt_blocks_count;
+ uint64_t outgoing_connections_count;
+ uint64_t incoming_connections_count;
+ uint64_t white_peerlist_size;
+ uint64_t grey_peerlist_size;
+ bool testnet;
+ crypto::hash top_block_hash;
+ uint64_t cumulative_difficulty;
+ uint64_t block_size_limit;
+ uint64_t start_time;
+ };
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h
new file mode 100644
index 000000000..d75180199
--- /dev/null
+++ b/src/rpc/rpc_handler.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2016, 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>
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+
+class RpcHandler
+{
+ public:
+
+ virtual std::string handle(const std::string& request) = 0;
+
+ RpcHandler() { }
+
+ virtual ~RpcHandler() { }
+};
+
+
+} // rpc
+
+} // cryptonote
diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp
new file mode 100644
index 000000000..afdff2328
--- /dev/null
+++ b/src/rpc/zmq_server.cpp
@@ -0,0 +1,141 @@
+// Copyright (c) 2016, 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 "zmq_server.h"
+#include <boost/chrono/chrono.hpp>
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+ZmqServer::ZmqServer(RpcHandler& h) :
+ handler(h),
+ stop_signal(false),
+ running(false),
+ context(DEFAULT_NUM_ZMQ_THREADS) // TODO: make this configurable
+{
+}
+
+ZmqServer::~ZmqServer()
+{
+}
+
+void ZmqServer::serve()
+{
+
+ while (1)
+ {
+ try
+ {
+ zmq::message_t message;
+
+ if (!rep_socket)
+ {
+ throw std::runtime_error("ZMQ RPC server reply socket is null");
+ }
+ while (rep_socket->recv(&message))
+ {
+ std::string message_string(reinterpret_cast<const char *>(message.data()), message.size());
+
+ MDEBUG(std::string("Received RPC request: \"") + message_string + "\"");
+
+ std::string response = handler.handle(message_string);
+
+ zmq::message_t reply(response.size());
+ memcpy((void *) reply.data(), response.c_str(), response.size());
+
+ rep_socket->send(reply);
+ MDEBUG(std::string("Sent RPC reply: \"") + response + "\"");
+
+ }
+ }
+ catch (const boost::thread_interrupted& e)
+ {
+ MDEBUG("ZMQ Server thread interrupted.");
+ }
+ catch (const zmq::error_t& e)
+ {
+ MERROR(std::string("ZMQ error: ") + e.what());
+ }
+ boost::this_thread::interruption_point();
+ }
+}
+
+bool ZmqServer::addIPCSocket(std::string address, std::string port)
+{
+ MERROR("ZmqServer::addIPCSocket not yet implemented!");
+ return false;
+}
+
+bool ZmqServer::addTCPSocket(std::string address, std::string port)
+{
+ try
+ {
+ std::string addr_prefix("tcp://");
+
+ rep_socket.reset(new zmq::socket_t(context, ZMQ_REP));
+
+ rep_socket->setsockopt(ZMQ_RCVTIMEO, DEFAULT_RPC_RECV_TIMEOUT_MS);
+
+ std::string bind_address = addr_prefix + address + std::string(":") + port;
+ rep_socket->bind(bind_address.c_str());
+ }
+ catch (const std::exception& e)
+ {
+ MERROR(std::string("Error creating ZMQ Socket: ") + e.what());
+ return false;
+ }
+ return true;
+}
+
+void ZmqServer::run()
+{
+ running = true;
+ run_thread = boost::thread(boost::bind(&ZmqServer::serve, this));
+}
+
+void ZmqServer::stop()
+{
+ if (!running) return;
+
+ stop_signal = true;
+
+ run_thread.interrupt();
+ run_thread.join();
+
+ running = false;
+
+ return;
+}
+
+
+} // namespace cryptonote
+
+} // namespace rpc
diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h
new file mode 100644
index 000000000..c278ff759
--- /dev/null
+++ b/src/rpc/zmq_server.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2016, 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/thread/thread.hpp>
+#include <zmq.hpp>
+#include <string>
+#include <memory>
+
+#include "common/command_line.h"
+
+#include "rpc_handler.h"
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+static constexpr int DEFAULT_NUM_ZMQ_THREADS = 1;
+static constexpr int DEFAULT_RPC_RECV_TIMEOUT_MS = 1000;
+
+class ZmqServer
+{
+ public:
+
+ ZmqServer(RpcHandler& h);
+
+ ~ZmqServer();
+
+ static void init_options(boost::program_options::options_description& desc);
+
+ void serve();
+
+ bool addIPCSocket(std::string address, std::string port);
+ bool addTCPSocket(std::string address, std::string port);
+
+ void run();
+ void stop();
+
+ private:
+ RpcHandler& handler;
+
+ volatile bool stop_signal;
+ volatile bool running;
+
+ zmq::context_t context;
+
+ boost::thread run_thread;
+
+ std::unique_ptr<zmq::socket_t> rep_socket;
+};
+
+
+} // namespace cryptonote
+
+} // namespace rpc
diff --git a/src/serialization/CMakeLists.txt b/src/serialization/CMakeLists.txt
new file mode 100644
index 000000000..e4aa0b6a2
--- /dev/null
+++ b/src/serialization/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (c) 2016, 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(serialization_sources
+ json_object.cpp)
+
+set(serialization_headers)
+
+set(serialization_private_headers
+ json_object.h)
+
+monero_private_headers(serialization
+ ${serialization_private_headers})
+monero_add_library(serialization
+ ${serialization_sources}
+ ${serialization_headers}
+ ${serialization_private_headers})
+target_link_libraries(serialization
+ LINK_PRIVATE
+ cryptonote_core
+ cryptonote_protocol
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_REGEX_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${EXTRA_LIBRARIES})
+add_dependencies(serialization
+ version)
+
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
new file mode 100644
index 000000000..ead3fdd58
--- /dev/null
+++ b/src/serialization/json_object.cpp
@@ -0,0 +1,1165 @@
+// Copyright (c) 2016-2017, 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 "json_object.h"
+
+#include <limits>
+#include "string_tools.h"
+
+namespace cryptonote
+{
+
+namespace json
+{
+
+namespace
+{
+ template<typename Source, typename Destination>
+ constexpr bool precision_loss()
+ {
+ return
+ std::numeric_limits<Destination>::is_signed != std::numeric_limits<Source>::is_signed ||
+ std::numeric_limits<Destination>::min() > std::numeric_limits<Source>::min() ||
+ std::numeric_limits<Destination>::max() < std::numeric_limits<Source>::max();
+ }
+
+ template<typename Source, typename Type>
+ void convert_numeric(Source source, Type& i)
+ {
+ static_assert(
+ std::numeric_limits<Source>::is_signed == std::numeric_limits<Type>::is_signed,
+ "source and destination signs do not match"
+ );
+ if (source < std::numeric_limits<Type>::min())
+ {
+ throw WRONG_TYPE{"numeric underflow"};
+ }
+ if (std::numeric_limits<Type>::max() < source)
+ {
+ throw WRONG_TYPE{"numeric overflow"};
+ }
+ i = Type(source);
+ }
+
+ template<typename Type>
+ void to_int(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsInt())
+ {
+ throw WRONG_TYPE{"integer"};
+ }
+ convert_numeric(val.GetInt(), i);
+ }
+ template<typename Type>
+ void to_int64(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsInt64())
+ {
+ throw WRONG_TYPE{"integer"};
+ }
+ convert_numeric(val.GetInt64(), i);
+ }
+
+ template<typename Type>
+ void to_uint(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsUint())
+ {
+ throw WRONG_TYPE{"unsigned integer"};
+ }
+ convert_numeric(val.GetUint(), i);
+ }
+ template<typename Type>
+ void to_uint64(const rapidjson::Value& val, Type& i)
+ {
+ if (!val.IsUint64())
+ {
+ throw WRONG_TYPE{"unsigned integer"};
+ }
+ convert_numeric(val.GetUint64(), i);
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val)
+{
+ val = rapidjson::Value(i.c_str(), doc.GetAllocator());
+}
+
+void fromJsonValue(const rapidjson::Value& val, std::string& str)
+{
+ if (!val.IsString())
+ {
+ throw WRONG_TYPE("string");
+ }
+
+ str = val.GetString();
+}
+
+void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val)
+{
+ val.SetBool(i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, bool& b)
+{
+ if (!val.IsBool())
+ {
+ throw WRONG_TYPE("boolean");
+ }
+ b = val.GetBool();
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned char& i)
+{
+ to_uint(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, char& i)
+{
+ to_int(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, signed char& i)
+{
+ to_int(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned short& i)
+{
+ to_uint(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, short& i)
+{
+ to_int(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const unsigned int i, rapidjson::Value& val)
+{
+ val = rapidjson::Value(i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned int& i)
+{
+ to_uint(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const int i, rapidjson::Value& val)
+{
+ val = rapidjson::Value(i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, int& i)
+{
+ to_int(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val)
+{
+ static_assert(!precision_loss<unsigned long long, std::uint64_t>(), "precision loss");
+ val = rapidjson::Value(std::uint64_t(i));
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned long long& i)
+{
+ to_uint64(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val)
+{
+ static_assert(!precision_loss<long long, std::int64_t>(), "precision loss");
+ val = rapidjson::Value(std::int64_t(i));
+}
+
+void fromJsonValue(const rapidjson::Value& val, long long& i)
+{
+ to_int64(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, unsigned long& i)
+{
+ to_uint64(val, i);
+}
+
+void fromJsonValue(const rapidjson::Value& val, long& i)
+{
+ to_int64(val, i);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, tx.version);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlock_time, tx.unlock_time);
+ INSERT_INTO_JSON_OBJECT(val, doc, vin, tx.vin);
+ INSERT_INTO_JSON_OBJECT(val, doc, vout, tx.vout);
+ INSERT_INTO_JSON_OBJECT(val, doc, extra, tx.extra);
+ INSERT_INTO_JSON_OBJECT(val, doc, signatures, tx.signatures);
+ INSERT_INTO_JSON_OBJECT(val, doc, rct_signatures, tx.rct_signatures);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tx.version, version);
+ GET_FROM_JSON_OBJECT(val, tx.unlock_time, unlock_time);
+ GET_FROM_JSON_OBJECT(val, tx.vin, vin);
+ GET_FROM_JSON_OBJECT(val, tx.vout, vout);
+ GET_FROM_JSON_OBJECT(val, tx.extra, extra);
+ GET_FROM_JSON_OBJECT(val, tx.signatures, signatures);
+ GET_FROM_JSON_OBJECT(val, tx.rct_signatures, rct_signatures);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, major_version, b.major_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, minor_version, b.minor_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, timestamp, b.timestamp);
+ INSERT_INTO_JSON_OBJECT(val, doc, prev_id, b.prev_id);
+ INSERT_INTO_JSON_OBJECT(val, doc, nonce, b.nonce);
+ INSERT_INTO_JSON_OBJECT(val, doc, miner_tx, b.miner_tx);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hashes, b.tx_hashes);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, b.major_version, major_version);
+ GET_FROM_JSON_OBJECT(val, b.minor_version, minor_version);
+ GET_FROM_JSON_OBJECT(val, b.timestamp, timestamp);
+ GET_FROM_JSON_OBJECT(val, b.prev_id, prev_id);
+ GET_FROM_JSON_OBJECT(val, b.nonce, nonce);
+ GET_FROM_JSON_OBJECT(val, b.miner_tx, miner_tx);
+ GET_FROM_JSON_OBJECT(val, b.tx_hashes, tx_hashes);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ if (txin.type() == typeid(cryptonote::txin_gen))
+ {
+ val.AddMember("type", "txin_gen", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_gen>(txin));
+ }
+ else if (txin.type() == typeid(cryptonote::txin_to_script))
+ {
+ val.AddMember("type", "txin_to_script", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_script>(txin));
+ }
+ else if (txin.type() == typeid(cryptonote::txin_to_scripthash))
+ {
+ val.AddMember("type", "txin_to_scripthash", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_scripthash>(txin));
+ }
+ else if (txin.type() == typeid(cryptonote::txin_to_key))
+ {
+ val.AddMember("type", "txin_to_key", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txin_to_key>(txin));
+ }
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "type")
+ OBJECT_HAS_MEMBER_OR_THROW(val, "value")
+ if (val["type"]== "txin_gen")
+ {
+ cryptonote::txin_gen tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+ else if (val["type"]== "txin_to_script")
+ {
+ cryptonote::txin_to_script tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+ else if (val["type"] == "txin_to_scripthash")
+ {
+ cryptonote::txin_to_scripthash tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+ else if (val["type"] == "txin_to_key")
+ {
+ cryptonote::txin_to_key tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txin = tmpVal;
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, txin.height);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.height, height);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev);
+ INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout);
+ INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.prev, prev);
+ GET_FROM_JSON_OBJECT(val, txin.prevout, prevout);
+ GET_FROM_JSON_OBJECT(val, txin.sigset, sigset);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, prev, txin.prev);
+ INSERT_INTO_JSON_OBJECT(val, doc, prevout, txin.prevout);
+ INSERT_INTO_JSON_OBJECT(val, doc, script, txin.script);
+ INSERT_INTO_JSON_OBJECT(val, doc, sigset, txin.sigset);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.prev, prev);
+ GET_FROM_JSON_OBJECT(val, txin.prevout, prevout);
+ GET_FROM_JSON_OBJECT(val, txin.script, script);
+ GET_FROM_JSON_OBJECT(val, txin.sigset, sigset);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, txin.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, key_offsets, txin.key_offsets);
+ INSERT_INTO_JSON_OBJECT(val, doc, k_image, txin.k_image);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txin.amount, amount);
+ GET_FROM_JSON_OBJECT(val, txin.key_offsets, key_offsets);
+ GET_FROM_JSON_OBJECT(val, txin.k_image, k_image);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ if (txout.type() == typeid(cryptonote::txout_to_script))
+ {
+ val.AddMember("type", "txout_to_script", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_script>(txout));
+ }
+ else if (txout.type() == typeid(cryptonote::txout_to_scripthash))
+ {
+ val.AddMember("type", "txout_to_scripthash", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_scripthash>(txout));
+ }
+ else if (txout.type() == typeid(cryptonote::txout_to_key))
+ {
+ val.AddMember("type", "txout_to_key", doc.GetAllocator());
+ INSERT_INTO_JSON_OBJECT(val, doc, value, boost::get<cryptonote::txout_to_key>(txout));
+ }
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "type")
+ OBJECT_HAS_MEMBER_OR_THROW(val, "value")
+ if (val["type"]== "txout_to_script")
+ {
+ cryptonote::txout_to_script tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txout = tmpVal;
+ }
+ else if (val["type"] == "txout_to_scripthash")
+ {
+ cryptonote::txout_to_scripthash tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txout = tmpVal;
+ }
+ else if (val["type"] == "txout_to_key")
+ {
+ cryptonote::txout_to_key tmpVal;
+ fromJsonValue(val["value"], tmpVal);
+ txout = tmpVal;
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, keys, txout.keys);
+ INSERT_INTO_JSON_OBJECT(val, doc, script, txout.script);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.keys, keys);
+ GET_FROM_JSON_OBJECT(val, txout.script, script);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, txout.hash);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.hash, hash);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, key, txout.key);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.key, key);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, txout.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, target, txout.target);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, txout.amount, amount);
+ GET_FROM_JSON_OBJECT(val, txout.target, target);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ auto& al = doc.GetAllocator();
+ INSERT_INTO_JSON_OBJECT(val, doc, incoming, info.incoming);
+ INSERT_INTO_JSON_OBJECT(val, doc, localhost, info.localhost);
+ INSERT_INTO_JSON_OBJECT(val, doc, local_ip, info.local_ip);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip);
+ INSERT_INTO_JSON_OBJECT(val, doc, port, info.port);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, peer_id, info.peer_id);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, recv_count, info.recv_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, recv_idle_time, info.recv_idle_time);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, send_count, info.send_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, send_idle_time, info.send_idle_time);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, state, info.state);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, live_time, info.live_time);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, avg_download, info.avg_download);
+ INSERT_INTO_JSON_OBJECT(val, doc, current_download, info.current_download);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, avg_upload, info.avg_upload);
+ INSERT_INTO_JSON_OBJECT(val, doc, current_upload, info.current_upload);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, info.incoming, incoming);
+ GET_FROM_JSON_OBJECT(val, info.localhost, localhost);
+ GET_FROM_JSON_OBJECT(val, info.local_ip, local_ip);
+
+ GET_FROM_JSON_OBJECT(val, info.ip, ip);
+ GET_FROM_JSON_OBJECT(val, info.port, port);
+
+ GET_FROM_JSON_OBJECT(val, info.peer_id, peer_id);
+
+ GET_FROM_JSON_OBJECT(val, info.recv_count, recv_count);
+ GET_FROM_JSON_OBJECT(val, info.recv_idle_time, recv_idle_time);
+
+ GET_FROM_JSON_OBJECT(val, info.send_count, send_count);
+ GET_FROM_JSON_OBJECT(val, info.send_idle_time, send_idle_time);
+
+ GET_FROM_JSON_OBJECT(val, info.state, state);
+
+ GET_FROM_JSON_OBJECT(val, info.live_time, live_time);
+
+ GET_FROM_JSON_OBJECT(val, info.avg_download, avg_download);
+ GET_FROM_JSON_OBJECT(val, info.current_download, current_download);
+
+ GET_FROM_JSON_OBJECT(val, info.avg_upload, avg_upload);
+ GET_FROM_JSON_OBJECT(val, info.current_upload, current_upload);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block);
+ INSERT_INTO_JSON_OBJECT(val, doc, txs, blk.txs);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, blk.block, block);
+ GET_FROM_JSON_OBJECT(val, blk.txs, txs);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, block, blk.block);
+ INSERT_INTO_JSON_OBJECT(val, doc, transactions, blk.transactions);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, blk.block, block);
+ GET_FROM_JSON_OBJECT(val, blk.transactions, transactions);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, tx_info.height);
+ INSERT_INTO_JSON_OBJECT(val, doc, in_pool, tx_info.in_pool);
+ INSERT_INTO_JSON_OBJECT(val, doc, transaction, tx_info.transaction);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tx_info.height, height);
+ GET_FROM_JSON_OBJECT(val, tx_info.in_pool, in_pool);
+ GET_FROM_JSON_OBJECT(val, tx_info.transaction, transaction);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount_index, out.amount_index);
+ INSERT_INTO_JSON_OBJECT(val, doc, key, out.key);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount_index, amount_index);
+ GET_FROM_JSON_OBJECT(val, out.key, key);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, outputs, out.outputs);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount, amount);
+ GET_FROM_JSON_OBJECT(val, out.outputs, outputs);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, id, peer.id);
+ INSERT_INTO_JSON_OBJECT(val, doc, ip, peer.ip);
+ INSERT_INTO_JSON_OBJECT(val, doc, port, peer.port);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_seen, peer.last_seen);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, peer.id, id);
+ GET_FROM_JSON_OBJECT(val, peer.ip, ip);
+ GET_FROM_JSON_OBJECT(val, peer.port, port);
+ GET_FROM_JSON_OBJECT(val, peer.last_seen, last_seen);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee);
+ INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, kept_by_block, tx.kept_by_block);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_hash, tx.last_failed_block_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_failed_block_height, tx.last_failed_block_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, receive_time, tx.receive_time);
+ INSERT_INTO_JSON_OBJECT(val, doc, last_relayed_time, tx.last_relayed_time);
+ INSERT_INTO_JSON_OBJECT(val, doc, relayed, tx.relayed);
+ INSERT_INTO_JSON_OBJECT(val, doc, do_not_relay, tx.do_not_relay);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tx.tx, tx);
+ GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size);
+ GET_FROM_JSON_OBJECT(val, tx.fee, fee);
+ GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash);
+ GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height);
+ GET_FROM_JSON_OBJECT(val, tx.kept_by_block, kept_by_block);
+ GET_FROM_JSON_OBJECT(val, tx.last_failed_block_hash, last_failed_block_hash);
+ GET_FROM_JSON_OBJECT(val, tx.last_failed_block_height, last_failed_block_height);
+ GET_FROM_JSON_OBJECT(val, tx.receive_time, receive_time);
+ GET_FROM_JSON_OBJECT(val, tx.last_relayed_time, last_relayed_time);
+ GET_FROM_JSON_OBJECT(val, tx.relayed, relayed);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, version, info.version);
+ INSERT_INTO_JSON_OBJECT(val, doc, enabled, info.enabled);
+ INSERT_INTO_JSON_OBJECT(val, doc, window, info.window);
+ INSERT_INTO_JSON_OBJECT(val, doc, votes, info.votes);
+ INSERT_INTO_JSON_OBJECT(val, doc, threshold, info.threshold);
+ INSERT_INTO_JSON_OBJECT(val, doc, voting, info.voting);
+ INSERT_INTO_JSON_OBJECT(val, doc, state, info.state);
+ INSERT_INTO_JSON_OBJECT(val, doc, earliest_height, info.earliest_height);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, info.version, version);
+ GET_FROM_JSON_OBJECT(val, info.enabled, enabled);
+ GET_FROM_JSON_OBJECT(val, info.window, window);
+ GET_FROM_JSON_OBJECT(val, info.votes, votes);
+ GET_FROM_JSON_OBJECT(val, info.threshold, threshold);
+ GET_FROM_JSON_OBJECT(val, info.voting, voting);
+ GET_FROM_JSON_OBJECT(val, info.state, state);
+ GET_FROM_JSON_OBJECT(val, info.earliest_height, earliest_height);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, total_count, out.total_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlocked_count, out.unlocked_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, recent_count, out.recent_count);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount, amount);
+ GET_FROM_JSON_OBJECT(val, out.total_count, total_count);
+ GET_FROM_JSON_OBJECT(val, out.unlocked_count, unlocked_count);
+ GET_FROM_JSON_OBJECT(val, out.recent_count, recent_count);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, out.amount);
+ INSERT_INTO_JSON_OBJECT(val, doc, index, out.index);
+}
+
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.amount, amount);
+ GET_FROM_JSON_OBJECT(val, out.index, index);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, key, out.key);
+ INSERT_INTO_JSON_OBJECT(val, doc, mask, out.mask);
+ INSERT_INTO_JSON_OBJECT(val, doc, unlocked, out.unlocked);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, out.key, key);
+ GET_FROM_JSON_OBJECT(val, out.mask, mask);
+ GET_FROM_JSON_OBJECT(val, out.unlocked, unlocked);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, code, err.code);
+ INSERT_INTO_JSON_OBJECT(val, doc, error_str, err.error_str);
+ INSERT_INTO_JSON_OBJECT(val, doc, message, err.message);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, error.code, code);
+ GET_FROM_JSON_OBJECT(val, error.error_str, error_str);
+ GET_FROM_JSON_OBJECT(val, error.message, message);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, major_version, response.major_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, minor_version, response.minor_version);
+ INSERT_INTO_JSON_OBJECT(val, doc, timestamp, response.timestamp);
+ INSERT_INTO_JSON_OBJECT(val, doc, prev_id, response.prev_id);
+ INSERT_INTO_JSON_OBJECT(val, doc, nonce, response.nonce);
+ INSERT_INTO_JSON_OBJECT(val, doc, height, response.height);
+ INSERT_INTO_JSON_OBJECT(val, doc, depth, response.depth);
+ INSERT_INTO_JSON_OBJECT(val, doc, hash, response.hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, difficulty, response.difficulty);
+ INSERT_INTO_JSON_OBJECT(val, doc, reward, response.reward);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, response.major_version, major_version);
+ GET_FROM_JSON_OBJECT(val, response.minor_version, minor_version);
+ GET_FROM_JSON_OBJECT(val, response.timestamp, timestamp);
+ GET_FROM_JSON_OBJECT(val, response.prev_id, prev_id);
+ GET_FROM_JSON_OBJECT(val, response.nonce, nonce);
+ GET_FROM_JSON_OBJECT(val, response.height, height);
+ GET_FROM_JSON_OBJECT(val, response.depth, depth);
+ GET_FROM_JSON_OBJECT(val, response.hash, hash);
+ GET_FROM_JSON_OBJECT(val, response.difficulty, difficulty);
+ GET_FROM_JSON_OBJECT(val, response.reward, reward);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, type, sig.type);
+ INSERT_INTO_JSON_OBJECT(val, doc, message, sig.message);
+ INSERT_INTO_JSON_OBJECT(val, doc, mixRing, sig.mixRing);
+ INSERT_INTO_JSON_OBJECT(val, doc, pseudoOuts, sig.pseudoOuts);
+ INSERT_INTO_JSON_OBJECT(val, doc, ecdhInfo, sig.ecdhInfo);
+ INSERT_INTO_JSON_OBJECT(val, doc, outPk, sig.outPk);
+ INSERT_INTO_JSON_OBJECT(val, doc, txnFee, sig.txnFee);
+ INSERT_INTO_JSON_OBJECT(val, doc, p, sig.p);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::rctSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.type, type);
+ GET_FROM_JSON_OBJECT(val, sig.message, message);
+ GET_FROM_JSON_OBJECT(val, sig.mixRing, mixRing);
+ GET_FROM_JSON_OBJECT(val, sig.pseudoOuts, pseudoOuts);
+ GET_FROM_JSON_OBJECT(val, sig.ecdhInfo, ecdhInfo);
+ GET_FROM_JSON_OBJECT(val, sig.outPk, outPk);
+ GET_FROM_JSON_OBJECT(val, sig.txnFee, txnFee);
+ GET_FROM_JSON_OBJECT(val, sig.p, p);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, dest, key.dest);
+ INSERT_INTO_JSON_OBJECT(val, doc, mask, key.mask);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+ GET_FROM_JSON_OBJECT(val, key.dest, dest);
+ GET_FROM_JSON_OBJECT(val, key.mask, mask);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, mask, tuple.mask);
+ INSERT_INTO_JSON_OBJECT(val, doc, amount, tuple.amount);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tuple.mask, mask);
+ GET_FROM_JSON_OBJECT(val, tuple.amount, amount);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs);
+ INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs);
+ GET_FROM_JSON_OBJECT(val, sig.MGs, MGs);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, asig, sig.asig);
+
+ std::vector<rct::key> keyVector(sig.Ci, std::end(sig.Ci));
+ INSERT_INTO_JSON_OBJECT(val, doc, Ci, keyVector);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.asig, asig);
+
+ std::vector<rct::key> keyVector;
+ cryptonote::json::fromJsonValue(val["Ci"], keyVector);
+ if (!(keyVector.size() == 64))
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+ for (size_t i=0; i < 64; i++)
+ {
+ sig.Ci[i] = keyVector[i];
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ std::vector<rct::key> keyVector(sig.s0, std::end(sig.s0));
+ INSERT_INTO_JSON_OBJECT(val, doc, s0, sig.s0);
+
+ keyVector.assign(sig.s1, std::end(sig.s1));
+ INSERT_INTO_JSON_OBJECT(val, doc, s1, sig.s1);
+
+ INSERT_INTO_JSON_OBJECT(val, doc, ee, sig.ee);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "s0")
+ std::vector<rct::key> keyVector;
+ cryptonote::json::fromJsonValue(val["s0"], keyVector);
+ if (!(keyVector.size() == 64))
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+ for (size_t i=0; i < 64; i++)
+ {
+ sig.s0[i] = keyVector[i];
+ }
+
+ OBJECT_HAS_MEMBER_OR_THROW(val, "s1")
+ keyVector.clear();
+ cryptonote::json::fromJsonValue(val["s1"], keyVector);
+ if (!(keyVector.size() == 64))
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+ for (size_t i=0; i < 64; i++)
+ {
+ sig.s1[i] = keyVector[i];
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.ee, ee);
+}
+
+void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, ss, sig.ss);
+ INSERT_INTO_JSON_OBJECT(val, doc, cc, sig.cc);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("key64 (rct::key[64])");
+ }
+
+ GET_FROM_JSON_OBJECT(val, sig.ss, ss);
+ GET_FROM_JSON_OBJECT(val, sig.cc, cc);
+}
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, height, info.height);
+ INSERT_INTO_JSON_OBJECT(val, doc, target_height, info.target_height);
+ INSERT_INTO_JSON_OBJECT(val, doc, difficulty, info.difficulty);
+ INSERT_INTO_JSON_OBJECT(val, doc, target, info.target);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_count, info.tx_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, tx_pool_size, info.tx_pool_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, alt_blocks_count, info.alt_blocks_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, outgoing_connections_count, info.outgoing_connections_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, incoming_connections_count, info.incoming_connections_count);
+ INSERT_INTO_JSON_OBJECT(val, doc, white_peerlist_size, info.white_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, grey_peerlist_size, info.grey_peerlist_size);
+ INSERT_INTO_JSON_OBJECT(val, doc, testnet, info.testnet);
+ INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash);
+ INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty);
+ INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit);
+ INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, info.height, height);
+ GET_FROM_JSON_OBJECT(val, info.target_height, target_height);
+ GET_FROM_JSON_OBJECT(val, info.difficulty, difficulty);
+ GET_FROM_JSON_OBJECT(val, info.target, target);
+ GET_FROM_JSON_OBJECT(val, info.tx_count, tx_count);
+ GET_FROM_JSON_OBJECT(val, info.tx_pool_size, tx_pool_size);
+ GET_FROM_JSON_OBJECT(val, info.alt_blocks_count, alt_blocks_count);
+ GET_FROM_JSON_OBJECT(val, info.outgoing_connections_count, outgoing_connections_count);
+ GET_FROM_JSON_OBJECT(val, info.incoming_connections_count, incoming_connections_count);
+ GET_FROM_JSON_OBJECT(val, info.white_peerlist_size, white_peerlist_size);
+ GET_FROM_JSON_OBJECT(val, info.grey_peerlist_size, grey_peerlist_size);
+ GET_FROM_JSON_OBJECT(val, info.testnet, testnet);
+ GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash);
+ GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty);
+ GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit);
+ GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
+}
+
+} // namespace json
+
+} // namespace cryptonote
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
new file mode 100644
index 000000000..7b9519c48
--- /dev/null
+++ b/src/serialization/json_object.h
@@ -0,0 +1,371 @@
+// Copyright (c) 2016-2017, 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 "rapidjson/document.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "rpc/message_data_structs.h"
+#include "cryptonote_protocol/cryptonote_protocol_defs.h"
+#include "common/sfinae_helpers.h"
+
+#define OBJECT_HAS_MEMBER_OR_THROW(val, key) \
+ do \
+ { \
+ if (!val.HasMember(key)) \
+ { \
+ throw cryptonote::json::MISSING_KEY(key); \
+ } \
+ } while (0);
+
+#define INSERT_INTO_JSON_OBJECT(jsonVal, doc, key, source) \
+ rapidjson::Value key##Val; \
+ cryptonote::json::toJsonValue(doc, source, key##Val); \
+ jsonVal.AddMember(#key, key##Val, doc.GetAllocator());
+
+#define GET_FROM_JSON_OBJECT(source, dst, key) \
+ OBJECT_HAS_MEMBER_OR_THROW(source, #key) \
+ decltype(dst) dstVal##key; \
+ cryptonote::json::fromJsonValue(source[#key], dstVal##key); \
+ dst = dstVal##key;
+
+namespace cryptonote
+{
+
+namespace json
+{
+
+struct JSON_ERROR : public std::exception
+{
+ protected:
+ JSON_ERROR() { }
+ std::string m;
+
+ public:
+ virtual ~JSON_ERROR() { }
+
+ const char* what() const throw()
+ {
+ return m.c_str();
+ }
+};
+
+struct MISSING_KEY : public JSON_ERROR
+{
+ MISSING_KEY(const char* key)
+ {
+ m = std::string("Key \"") + key + "\" missing from object.";
+ }
+};
+
+struct WRONG_TYPE : public JSON_ERROR
+{
+ WRONG_TYPE(const char* type)
+ {
+ m = std::string("Json value has incorrect type, expected: ") + type;
+ }
+};
+
+struct BAD_INPUT : public JSON_ERROR
+{
+ BAD_INPUT()
+ {
+ m = "An item failed to convert from json object to native object";
+ }
+};
+
+struct PARSE_FAIL : public JSON_ERROR
+{
+ PARSE_FAIL()
+ {
+ m = "Failed to parse the json request";
+ }
+};
+
+template<typename Type>
+inline constexpr bool is_to_hex()
+{
+ return std::is_pod<Type>() && !std::is_integral<Type>();
+}
+
+
+// POD to json value
+template <class Type>
+typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::Document& doc, const Type& pod, rapidjson::Value& value)
+{
+ value = rapidjson::Value(epee::string_tools::pod_to_hex(pod).c_str(), doc.GetAllocator());
+}
+
+template <class Type>
+typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::Value& val, Type& t)
+{
+ if (!val.IsString())
+ {
+ throw WRONG_TYPE("string");
+ }
+
+ //TODO: handle failure to convert hex string to POD type
+ bool success = epee::string_tools::hex_to_pod(val.GetString(), t);
+
+ if (!success)
+ {
+ throw BAD_INPUT();
+ }
+}
+
+void toJsonValue(rapidjson::Document& doc, const std::string& i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, std::string& str);
+
+void toJsonValue(rapidjson::Document& doc, bool i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, bool& b);
+
+// integers overloads for toJsonValue are not needed for standard promotions
+
+void fromJsonValue(const rapidjson::Value& val, unsigned char& i);
+
+void fromJsonValue(const rapidjson::Value& val, signed char& i);
+
+void fromJsonValue(const rapidjson::Value& val, char& i);
+
+void fromJsonValue(const rapidjson::Value& val, unsigned short& i);
+
+void fromJsonValue(const rapidjson::Value& val, short& i);
+
+void toJsonValue(rapidjson::Document& doc, const unsigned i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, unsigned& i);
+
+void toJsonValue(rapidjson::Document& doc, const int, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, int& i);
+
+
+void toJsonValue(rapidjson::Document& doc, const unsigned long long i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, unsigned long long& i);
+
+void toJsonValue(rapidjson::Document& doc, const long long i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, long long& i);
+
+inline void toJsonValue(rapidjson::Document& doc, const unsigned long i, rapidjson::Value& val) {
+ toJsonValue(doc, static_cast<unsigned long long>(i), val);
+}
+void fromJsonValue(const rapidjson::Value& val, unsigned long& i);
+
+inline void toJsonValue(rapidjson::Document& doc, const long i, rapidjson::Value& val) {
+ toJsonValue(doc, static_cast<long long>(i), val);
+}
+void fromJsonValue(const rapidjson::Value& val, long& i);
+
+// end integers
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::transaction& tx, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block& b, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block& b);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_v& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_v& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_gen& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_gen& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_script& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_script& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_scripthash& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txin_to_key& txin, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_target_v& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_script& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_script& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_scripthash& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_scripthash& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::txout_to_key& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_to_key& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_out& txout, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::block_with_transactions& blk, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::block_with_transactions& blk);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::transaction_info& tx_info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::transaction_info& tx_info);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_and_amount_index& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_and_amount_index& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::amount_with_random_outputs& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::amount_with_random_outputs& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::hard_fork_info& info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::hard_fork_info& info);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_count& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_count& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_amount_and_index& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_amount_and_index& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::output_key_mask_unlocked& out, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::output_key_mask_unlocked& out);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::error& err, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::error& error);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::BlockHeaderResponse& response, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::BlockHeaderResponse& response);
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSig& i, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& i, rct::rctSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::ctkey& key, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::ctkey& key);
+
+void toJsonValue(rapidjson::Document& doc, const rct::ecdhTuple& tuple, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::ecdhTuple& tuple);
+
+void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const rct::mgSig& sig, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::mgSig& sig);
+
+void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& info, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& info);
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val);
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map);
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val);
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec);
+
+
+// ideally would like to have the below functions in the .cpp file, but
+// unfortunately because of how templates work they have to be here.
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type toJsonValue(rapidjson::Document& doc, const Map& map, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ auto& al = doc.GetAllocator();
+
+ for (const auto& i : map)
+ {
+ rapidjson::Value k;
+ rapidjson::Value m;
+ toJsonValue(doc, i.first, k);
+ toJsonValue(doc, i.second, m);
+ val.AddMember(k, m, al);
+ }
+}
+
+template <typename Map>
+typename std::enable_if<sfinae::is_map_like<Map>::value, void>::type fromJsonValue(const rapidjson::Value& val, Map& map)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ auto itr = val.MemberBegin();
+
+ while (itr != val.MemberEnd())
+ {
+ typename Map::key_type k;
+ typename Map::mapped_type m;
+ fromJsonValue(itr->name, k);
+ fromJsonValue(itr->value, m);
+ map.emplace(k, m);
+ ++itr;
+ }
+}
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type toJsonValue(rapidjson::Document& doc, const Vec &vec, rapidjson::Value& val)
+{
+ val.SetArray();
+
+ for (const auto& t : vec)
+ {
+ rapidjson::Value v;
+ toJsonValue(doc, t, v);
+ val.PushBack(v, doc.GetAllocator());
+ }
+}
+
+template <typename Vec>
+typename std::enable_if<sfinae::is_vector_like<Vec>::value, void>::type fromJsonValue(const rapidjson::Value& val, Vec& vec)
+{
+ if (!val.IsArray())
+ {
+ throw WRONG_TYPE("json array");
+ }
+
+ for (rapidjson::SizeType i=0; i < val.Size(); i++)
+ {
+ typename Vec::value_type v;
+ fromJsonValue(val[i], v);
+ vec.push_back(v);
+ }
+}
+
+} // namespace json
+
+} // namespace cryptonote
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 857e2af6e..3fe68046b 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -768,6 +768,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), tr("Set an arbitrary string note for a txid"));
m_cmd_binder.set_handler("get_tx_note", boost::bind(&simple_wallet::get_tx_note, this, _1), tr("Get a string note for a txid"));
m_cmd_binder.set_handler("status", boost::bind(&simple_wallet::status, this, _1), tr("Show wallet status information"));
+ m_cmd_binder.set_handler("wallet_info", boost::bind(&simple_wallet::wallet_info, this, _1), tr("Show wallet information"));
m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::sign, this, _1), tr("Sign the contents of a file"));
m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::verify, this, _1), tr("Verify a signature on the contents of a file"));
m_cmd_binder.set_handler("export_key_images", boost::bind(&simple_wallet::export_key_images, this, _1), tr("Export a signed set of key images"));
@@ -2329,7 +2330,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
- size_t fake_outs_count;
+ size_t fake_outs_count = 0;
if(local_args.size() > 0) {
size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
@@ -2906,7 +2907,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a
std::vector<std::string> local_args = args_;
- size_t fake_outs_count;
+ size_t fake_outs_count = 0;
if(local_args.size() > 0) {
size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
@@ -4507,6 +4508,15 @@ bool simple_wallet::status(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::wallet_info(const std::vector<std::string> &args)
+{
+ message_writer() << tr("Filename: ") << m_wallet->get_wallet_file();
+ message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+ message_writer() << tr("Watch only: ") << (m_wallet->watch_only() ? tr("Yes") : tr("No"));
+ message_writer() << tr("Testnet: ") << (m_wallet->testnet() ? tr("Yes") : tr("No"));
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::sign(const std::vector<std::string> &args)
{
if (args.size() != 1)
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 079fae9f5..30c428810 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -165,6 +165,7 @@ namespace cryptonote
bool set_tx_note(const std::vector<std::string> &args);
bool get_tx_note(const std::vector<std::string> &args);
bool status(const std::vector<std::string> &args);
+ bool wallet_info(const std::vector<std::string> &args);
bool set_default_priority(const std::vector<std::string> &args);
bool sign(const std::vector<std::string> &args);
bool verify(const std::vector<std::string> &args);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 323a3a7fe..f72d281c7 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -45,6 +45,7 @@ using namespace epee;
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "common/boost_serialization_helper.h"
#include "common/command_line.h"
+#include "common/threadpool.h"
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
@@ -89,14 +90,6 @@ using namespace cryptonote;
#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f
-#define KILL_IOSERVICE() \
- do { \
- work.reset(); \
- while (!ioservice.stopped()) ioservice.poll(); \
- threadpool.join_all(); \
- ioservice.stop(); \
- } while(0)
-
#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002"
namespace
@@ -684,7 +677,9 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
std::deque<crypto::key_image> ki(tx.vout.size());
std::deque<uint64_t> amount(tx.vout.size());
std::deque<rct::key> mask(tx.vout.size());
- int threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
+ int threads = tpool.get_max_concurrency();
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
@@ -720,13 +715,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
++num_vouts_received;
// process the other outs from that tx
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
@@ -734,10 +722,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
- KILL_IOSERVICE();
+ waiter.wait();
for (size_t i = 1; i < tx.vout.size(); ++i)
{
if (error[i])
@@ -766,23 +754,18 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (tx.vout.size() > 1 && threads > 1)
{
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (int i = 0; i < threads; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
std::vector<uint64_t> money_transfered(tx.vout.size());
std::deque<bool> error(tx.vout.size());
std::deque<bool> received(tx.vout.size());
for (size_t i = 0; i < tx.vout.size(); ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
+ tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(keys.m_account_address.m_spend_public_key), std::cref(tx.vout[i]), std::cref(derivation), i,
std::ref(received[i]), std::ref(money_transfered[i]), std::ref(error[i])));
}
- KILL_IOSERVICE();
+ waiter.wait();
tx_money_got_in_outs = 0;
for (size_t i = 0; i < tx.vout.size(); ++i)
{
@@ -1251,7 +1234,8 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "size mismatch");
- int threads = tools::get_max_concurrency();
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ int threads = tpool.get_max_concurrency();
if (threads > 1)
{
std::vector<crypto::hash> round_block_hashes(threads);
@@ -1262,23 +1246,16 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
for (size_t b = 0; b < blocks_size; b += threads)
{
size_t round_size = std::min((size_t)threads, blocks_size - b);
-
- boost::asio::io_service ioservice;
- boost::thread_group threadpool;
- std::unique_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice));
- for (size_t i = 0; i < round_size; i++)
- {
- threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice));
- }
+ tools::threadpool::waiter waiter;
std::list<block_complete_entry>::const_iterator tmpblocki = blocki;
for (size_t i = 0; i < round_size; ++i)
{
- ioservice.dispatch(boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
+ tpool.submit(&waiter, boost::bind(&wallet2::parse_block_round, this, std::cref(tmpblocki->block),
std::ref(round_blocks[i]), std::ref(round_block_hashes[i]), std::ref(error[i])));
++tmpblocki;
}
- KILL_IOSERVICE();
+ waiter.wait();
tmpblocki = blocki;
for (size_t i = 0; i < round_size; ++i)
{
@@ -1698,7 +1675,8 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
size_t try_count = 0;
crypto::hash last_tx_hash_id = m_transfers.size() ? m_transfers.back().m_txid : null_hash;
std::list<crypto::hash> short_chain_history;
- boost::thread pull_thread;
+ tools::threadpool& tpool = tools::threadpool::getInstance();
+ tools::threadpool::waiter waiter;
uint64_t blocks_start_height;
std::list<cryptonote::block_complete_entry> blocks;
std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
@@ -1736,11 +1714,11 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
std::list<cryptonote::block_complete_entry> next_blocks;
std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> next_o_indices;
bool error = false;
- pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
+ tpool.submit(&waiter, [&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, next_o_indices, error);});
process_blocks(blocks_start_height, blocks, o_indices, added_blocks);
blocks_fetched += added_blocks;
- pull_thread.join();
+ waiter.wait();
if(blocks_start_height == next_blocks_start_height)
{
m_node_rpc_proxy.set_height(m_blockchain.size());
@@ -1762,8 +1740,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
catch (const std::exception&)
{
blocks_fetched += added_blocks;
- if (pull_thread.joinable())
- pull_thread.join();
+ waiter.wait();
if(try_count < 3)
{
LOG_PRINT_L1("Another try pull_blocks (try_count=" << try_count << ")...");