aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt26
-rw-r--r--CONTRIBUTING.md120
-rw-r--r--README.md17
-rw-r--r--cmake/FindMiniupnpc.cmake10
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h4
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl33
-rw-r--r--external/db_drivers/liblmdb/mdb.c4
-rw-r--r--external/miniupnpc/.gitignore4
-rw-r--r--external/miniupnpc/Changelog.txt12
-rw-r--r--external/miniupnpc/LICENSE2
-rw-r--r--external/miniupnpc/MANIFEST.in2
-rw-r--r--external/miniupnpc/Makefile20
-rw-r--r--external/miniupnpc/README7
-rw-r--r--external/miniupnpc/connecthostport.c10
-rw-r--r--external/miniupnpc/minihttptestserver.c6
-rw-r--r--external/miniupnpc/minisoap.c2
-rw-r--r--external/miniupnpc/minissdpc.c57
-rw-r--r--external/miniupnpc/miniupnpcmodule.c14
-rw-r--r--external/miniupnpc/miniwget.c20
-rw-r--r--external/miniupnpc/msvc/.gitignore4
-rw-r--r--external/miniupnpc/msvc/miniupnpc.vcxproj111
-rw-r--r--external/miniupnpc/msvc/miniupnpc.vcxproj.filters108
-rw-r--r--external/miniupnpc/msvc/miniupnpc_vs2015.sln28
-rw-r--r--external/miniupnpc/msvc/upnpc-static.vcxproj103
-rw-r--r--external/miniupnpc/msvc/upnpc-static.vcxproj.filters22
-rw-r--r--external/miniupnpc/portlistingparse.c4
-rw-r--r--external/miniupnpc/receivedata.c2
-rwxr-xr-xexternal/miniupnpc/setup.py27
-rw-r--r--external/miniupnpc/testdesc/new_LiveBox_desc.xml2
-rwxr-xr-xexternal/miniupnpc/testupnpigd.py40
-rw-r--r--external/miniupnpc/upnpc.c82
-rw-r--r--external/miniupnpc/upnpcommands.c89
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp4
-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/dns_utils.cpp4
-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/common/util.cpp76
-rw-r--r--src/common/util.h26
-rw-r--r--src/cryptonote_basic/checkpoints.cpp2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp17
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h2
-rw-r--r--src/cryptonote_basic/miner.cpp13
-rw-r--r--src/cryptonote_config.h2
-rw-r--r--src/cryptonote_core/blockchain.cpp281
-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.cpp89
-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.cpp7
-rw-r--r--src/p2p/net_node.h6
-rw-r--r--src/p2p/net_node.inl149
-rw-r--r--src/ringct/rctSigs.cpp49
-rw-r--r--src/rpc/CMakeLists.txt70
-rw-r--r--src/rpc/core_rpc_server.cpp33
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h4
-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.cpp1167
-rw-r--r--src/serialization/json_object.h371
-rw-r--r--src/simplewallet/simplewallet.cpp45
-rw-r--r--src/simplewallet/simplewallet.h3
-rw-r--r--src/wallet/api/wallet.cpp20
-rw-r--r--src/wallet/api/wallet.h2
-rw-r--r--src/wallet/api/wallet_manager.cpp4
-rw-r--r--src/wallet/api/wallet_manager.h2
-rw-r--r--src/wallet/wallet2.cpp242
-rw-r--r--src/wallet/wallet2.h2
-rw-r--r--src/wallet/wallet2_api.h2
-rw-r--r--src/wallet/wallet_args.cpp30
-rw-r--r--src/wallet/wallet_rpc_server.cpp49
-rw-r--r--src/wallet/wallet_rpc_server.h3
-rw-r--r--tests/core_proxy/core_proxy.h1
-rw-r--r--tests/unit_tests/CMakeLists.txt1
-rw-r--r--tests/unit_tests/ban.cpp1
-rw-r--r--tests/unit_tests/thread_group.cpp177
106 files changed, 7117 insertions, 1431 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d53e049e..486a80b4e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -407,7 +407,7 @@ else()
set(WARNINGS "${WARNINGS} -Wno-error=inline-asm")
endif()
else()
- set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized")
+ set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized -Wno-error=cpp")
endif()
if(MINGW)
set(WARNINGS "${WARNINGS} -Wno-error=unused-value -Wno-error=unused-but-set-variable")
@@ -689,17 +689,15 @@ endif()
include(version.cmake)
-function (treat_warnings_as_errors dirs)
- foreach(dir ${ARGV})
- set_property(DIRECTORY ${dir}
- APPEND PROPERTY COMPILE_FLAGS "-Werror")
- endforeach()
-endfunction()
+find_path(ZMQ_INCLUDE_PATH zmq.hpp)
+find_library(ZMQ_LIB zmq)
-add_subdirectory(contrib)
-add_subdirectory(src)
-
-treat_warnings_as_errors(contrib src)
+if(NOT ZMQ_INCLUDE_PATH)
+ message(FATAL_ERROR "Could not find required header zmq.hpp")
+endif()
+if(NOT ZMQ_LIB)
+ message(FATAL_ERROR "Could not find require libzmq")
+endif()
option(BUILD_TESTS "Build tests." OFF)
@@ -707,7 +705,13 @@ if(BUILD_TESTS)
add_subdirectory(tests)
endif()
+# warnings are cleared only for GCC on Linux
+if (NOT (MINGW OR APPLE OR FREEBSD OR OPENBSD OR DRAGONFLY))
+add_compile_options("${WARNINGS_AS_ERRORS_FLAG}") # applies only to targets that follow
+endif()
+add_subdirectory(contrib)
+add_subdirectory(src)
if(BUILD_DOCUMENTATION)
set(DOC_GRAPHS "YES" CACHE STRING "Create dependency graphs (needs graphviz)")
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 78d78f7bf..86eb45191 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -38,3 +38,123 @@ are conflicts (even trivially resolvable ones).
PGP signing commits is strongly encouraged. That should explain why
the previous paragraph is here.
+
+# [Code of Conduct (22/C4.1)](http://rfc.zeromq.org/spec:22)
+
+## License
+
+Copyright (c) 2009-2015 Pieter Hintjens.
+Copyright (c) 2017 The Monero Project.
+
+This Specification is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
+
+This Specification is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses>.
+
+## Language
+
+The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
+
+The "Monero Maintainer Team" is defined in this document as the following users:
+- fluffypony
+- moneromooo
+- hyc
+
+## Goals
+
+C4 is meant to provide a reusable optimal collaboration model for open source software projects. It has these specific goals:
+
+- To maximize the scale and diversity of the community around a project, by reducing the friction for new Contributors and creating a scaled participation model with strong positive feedbacks;
+- To relieve dependencies on key individuals by separating different skill sets so that there is a larger pool of competence in any required domain;
+- To allow the project to develop faster and more accurately, by increasing the diversity of the decision making process;
+- To support the natural life cycle of project versions from experimental through to stable, by allowing safe experimentation, rapid failure, and isolation of stable code;
+- To reduce the internal complexity of project repositories, thus making it easier for Contributors to participate and reducing the scope for error;
+- To enforce collective ownership of the project, which increases economic incentive to Contributors and reduces the risk of hijack by hostile entities.
+
+## Design
+
+### Preliminaries
+
+- The project SHALL use the git distributed revision control system.
+- The project SHALL be hosted on github.com or equivalent, herein called the "Platform".
+- The project SHALL use the Platform issue tracker.
+ - Non-GitHub example:
+ - "Platform" could be a vanilla git repo and Trac hosted on the same machine/network.
+ - The Platform issue tracker would be Trac.
+- The project SHOULD have clearly documented guidelines for code style.
+- A "Contributor" is a person who wishes to provide a patch, being a set of commits that solve some clearly identified problem.
+- A "Maintainer" is a person who merges patches to the project. Maintainers are not developers; their job is to enforce process.
+- Contributors SHALL NOT have commit access to the repository unless they are also Maintainers.
+- Maintainers SHALL have commit access to the repository.
+- Everyone, without distinction or discrimination, SHALL have an equal right to become a Contributor under the terms of this contract.
+
+### Licensing and Ownership
+
+- The project SHALL use a share-alike license, such as BSD-3, the GPLv3 or a variant thereof (LGPL, AGPL), or the MPLv2.
+- All contributions to the project source code ("patches") SHALL use the same license as the project.
+- All patches are owned by their authors. There SHALL NOT be any copyright assignment process.
+- The copyrights in the project SHALL be owned collectively by all its Contributors.
+- Each Contributor SHALL be responsible for identifying themselves in the project Contributor list.
+
+### Patch Requirements
+
+- Maintainers MUST have a Platform account and SHOULD use their real names or a well-known alias.
+- Contributors SHOULD have a Platform account and MAY use their real names or a well-known alias.
+- A patch SHOULD be a minimal and accurate answer to exactly one identified and agreed problem.
+- A patch MUST adhere to the code style guidelines of the project if these are defined.
+- A patch MUST adhere to the "Evolution of Public Contracts" guidelines defined below.
+- A patch SHALL NOT include non-trivial code from other projects unless the Contributor is the original author of that code.
+- A patch MUST compile cleanly and pass project self-tests on at least the principle target platform.
+- A patch commit message SHOULD consist of a single short (less than 50 character) line summarizing the change, optionally followed by a blank line and then a more thorough description.
+- A "Correct Patch" is one that satisfies the above requirements.
+
+### Development Process
+
+- Change on the project SHALL be governed by the pattern of accurately identifying problems and applying minimal, accurate solutions to these problems.
+- To request changes, a user SHOULD log an issue on the project Platform issue tracker.
+- The user or Contributor SHOULD write the issue by describing the problem they face or observe.
+- The user or Contributor SHOULD seek consensus on the accuracy of their observation, and the value of solving the problem.
+- Users SHALL NOT log feature requests, ideas, or suggestions unrelated to Monero code or Monero's dependency code or Monero's potential/future dependency code or research which successfully implements Monero.
+- Users SHALL NOT log any solutions to problems (verifiable or hypothetical) of which are not explicitly documented and/or not provable and/or cannot be reasonably proven.
+- Thus, the release history of the project SHALL be a list of meaningful issues logged and solved.
+- To work on an issue, a Contributor SHALL fork the project repository and then work on their forked repository.
+- To submit a patch, a Contributor SHALL create a Platform pull request back to the project.
+- A Contributor SHALL NOT commit changes directly to the project.
+- To discuss a patch, people MAY comment on the Platform pull request, on the commit, or elsewhere.
+- To accept or reject a patch, a Maintainer SHALL use the Platform interface.
+- Maintainers SHOULD NOT merge their own patches except in exceptional cases, such as non-responsiveness from other Maintainers for an extended period (more than 30 days) or unless urgent as defined by the Monero Maintainers Team.
+- Maintainers SHALL NOT make value judgments on correct patches unless the Maintainer (as may happen in rare circumstances) is a core code developer.
+- Maintainers MUST NOT merge pull requests in less than 168 hours (1 week) unless deemed urgent by at least 2 people from the Monero Maintainer Team.
+- The Contributor MAY tag an issue as "Ready" after making a pull request for the issue.
+- The user who created an issue SHOULD close the issue after checking the patch is successful.
+- Maintainers SHOULD ask for improvements to incorrect patches and SHOULD reject incorrect patches if the Contributor does not respond constructively.
+- Any Contributor who has value judgments on a correct patch SHOULD express these via their own patches.
+- Maintainers MAY commit changes to non-source documentation directly to the project.
+
+### Creating Stable Releases
+
+- The project SHALL have one branch ("master") that always holds the latest in-progress version and SHOULD always build.
+- The project SHALL NOT use topic branches for any reason. Personal forks MAY use topic branches.
+- To make a stable release someone SHALL fork the repository by copying it and thus become maintainer of this repository.
+- Forking a project for stabilization MAY be done unilaterally and without agreement of project maintainers.
+- A patch to a stabilization project declared "stable" SHALL be accompanied by a reproducible test case.
+
+### Evolution of Public Contracts
+
+- All Public Contracts (APIs or protocols) SHALL be documented.
+- All Public Contracts SHOULD have space for extensibility and experimentation.
+- A patch that modifies a stable Public Contract SHOULD not break existing applications unless there is overriding consensus on the value of doing this.
+- A patch that introduces new features to a Public Contract SHOULD do so using new names.
+- Old names SHOULD be deprecated in a systematic fashion by marking new names as "experimental" until they are stable, then marking the old names as "deprecated".
+- When sufficient time has passed, old deprecated names SHOULD be marked "legacy" and eventually removed.
+- Old names SHALL NOT be reused by new features.
+- When old names are removed, their implementations MUST provoke an exception (assertion) if used by applications.
+
+### Project Administration
+
+- The project founders SHALL act as Administrators to manage the set of project Maintainers.
+- The Administrators SHALL ensure their own succession over time by promoting the most effective Maintainers.
+- A new Contributor who makes a correct patch SHALL be invited to become a Maintainer.
+- Administrators MAY remove Maintainers who are inactive for an extended period of time, or who repeatedly fail to apply this process accurately.
+- Administrators SHOULD block or ban "bad actors" who cause stress and pain to others in the project. This should be done after public discussion, with a chance for all parties to speak. A bad actor is someone who repeatedly ignores the rules and culture of the project, who is needlessly argumentative or hostile, or who is offensive, and who is unable to self-correct their behavior when asked to do so by others.
diff --git a/README.md b/README.md
index 3f9b772d1..2087a5639 100644
--- a/README.md
+++ b/README.md
@@ -95,12 +95,13 @@ Monero uses a fixed-schedule hard fork mechanism to implement new features. This
Dates are provided in the format YYYY-MM-DD.
-| Fork Date | Consensus version | Minimum Monero Version | Recommended Monero Version | Details |
-| ----------------- | ----------------- | ---------------------- | -------------------------- | ------------------ |
-| 2016-09-21 | v3 | v0.9.4 | v0.10.0 | Splits coinbase into denominations |
-| 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions |
-| 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm |
-| 2017-09-21 | v6 | Not determined as of 2017-03-27 | Not determined as of 2017-03-27 | Allow only RingCT transactions |
+| Block Height | Fork Date | Consensus version | Minimum Monero Version | Recommended Monero Version | Details |
+| ----------------- | ----------------- | ---------------------- | -------------------------- | ------------------ | ---------- |
+| 1009827 | 2016-03-22 | v2 | v0.9.4 | v0.9.4 | Allow only >= ringsize 3, blocktime = 120 seconds, fee-free blocksize 60 kb |
+| 1141317 | 2016-09-21 | v3 | v0.9.4 | v0.10.0 | Splits coinbase into denominations |
+| 1220516 | 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions |
+| 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm |
+| 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 |
## Installing Monero from a Package
@@ -426,9 +427,11 @@ Type `thread apply all bt` within gdb in order to obtain the stack trace
Enter `ulimit -c unlimited` on the command line to enable unlimited filesizes for core dumps
+Enter `echo core | sudo tee /proc/sys/kernel/core_pattern` to stop cores from being hijacked by other tools
+
Run the build.
-When it terminates with an output along the lines of "Segmentation fault (core dumped)", there should be a core dump file in the same directory as monerod.
+When it terminates with an output along the lines of "Segmentation fault (core dumped)", there should be a core dump file in the same directory as monerod. It may be named just `core`, or `core.xxxx` with numbers appended.
You can now analyse this core dump with `gdb` as follows:
diff --git a/cmake/FindMiniupnpc.cmake b/cmake/FindMiniupnpc.cmake
index 77675db48..ad2004afc 100644
--- a/cmake/FindMiniupnpc.cmake
+++ b/cmake/FindMiniupnpc.cmake
@@ -46,13 +46,13 @@ IF(MINIUPNPC_FOUND)
file(STRINGS "${MINIUPNP_INCLUDE_DIR}/miniupnpc.h" MINIUPNPC_API_VERSION_STR REGEX "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+[0-9]+")
if(MINIUPNPC_API_VERSION_STR MATCHES "^#define[\t ]+MINIUPNPC_API_VERSION[\t ]+([0-9]+)")
set(MINIUPNPC_API_VERSION "${CMAKE_MATCH_1}")
+ if (${MINIUPNPC_API_VERSION} GREATER "10" OR ${MINIUPNPC_API_VERSION} EQUAL "10")
+ message(STATUS "Found miniupnpc API version " ${MINIUPNPC_API_VERSION})
+ set(MINIUPNP_FOUND true)
+ set(MINIUPNPC_VERSION_1_7_OR_HIGHER true)
+ endif()
endif()
- if (${MINIUPNPC_API_VERSION} GREATER "10" OR ${MINIUPNPC_API_VERSION} EQUAL "10")
- message(STATUS "Found miniupnpc API version " ${MINIUPNPC_API_VERSION})
- set(MINIUPNP_FOUND true)
- set(MINIUPNPC_VERSION_1_7_OR_HIGHER true)
- endif()
ENDIF()
mark_as_advanced(MINIUPNP_INCLUDE_DIR MINIUPNP_LIBRARY MINIUPNP_STATIC_LIBRARY)
diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h
index ca58d5467..03f143fe4 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.h
+++ b/contrib/epee/include/net/abstract_tcp_server2.h
@@ -281,8 +281,6 @@ namespace net_utils
bool is_thread_worker();
- bool cleanup_connections();
-
/// The io_service used to perform asynchronous operations.
std::unique_ptr<boost::asio::io_service> m_io_service_local_instance;
boost::asio::io_service& io_service_;
@@ -309,7 +307,7 @@ namespace net_utils
connection_ptr new_connection_;
boost::mutex connections_mutex;
- std::deque<std::pair<boost::system_time, connection_ptr>> connections_;
+ std::set<connection_ptr> connections_;
}; // class <>boosted_tcp_server
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
index 61276e761..76988a26e 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -54,8 +54,6 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net"
-#define CONNECTION_CLEANUP_TIME 30 // seconds
-
PRAGMA_WARNING_PUSH
namespace epee
{
@@ -808,7 +806,6 @@ POP_WARNINGS
m_threads_count = threads_count;
m_main_thread_id = boost::this_thread::get_id();
MLOG_SET_THREAD_NAME("[SRV_MAIN]");
- add_idle_handler(boost::bind(&boosted_tcp_server::cleanup_connections, this), 5000);
while(!m_stop_signal_sent)
{
@@ -898,7 +895,7 @@ POP_WARNINGS
connections_mutex.lock();
for (auto &c: connections_)
{
- c.second->cancel();
+ c->cancel();
}
connections_.clear();
connections_mutex.unlock();
@@ -907,19 +904,6 @@ POP_WARNINGS
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
- bool boosted_tcp_server<t_protocol_handler>::cleanup_connections()
- {
- connections_mutex.lock();
- boost::system_time cutoff = boost::get_system_time() - boost::posix_time::seconds(CONNECTION_CLEANUP_TIME);
- while (!connections_.empty() && connections_.front().first < cutoff)
- {
- connections_.pop_front();
- }
- connections_mutex.unlock();
- return true;
- }
- //---------------------------------------------------------------------------------
- template<class t_protocol_handler>
bool boosted_tcp_server<t_protocol_handler>::is_stop_signal_sent()
{
return m_stop_signal_sent;
@@ -958,9 +942,10 @@ POP_WARNINGS
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) );
connections_mutex.lock();
- connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l));
+ connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
connections_mutex.unlock();
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
//////////////////////////////////////////////////////////////////////////
@@ -1038,6 +1023,10 @@ POP_WARNINGS
_dbg3("Connected success to " << adr << ':' << port);
+ // start adds the connection to the config object's list, so we don't need to have it locally anymore
+ connections_mutex.lock();
+ connections_.erase(new_connection_l);
+ connections_mutex.unlock();
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r)
{
@@ -1062,9 +1051,10 @@ POP_WARNINGS
TRY_ENTRY();
connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sock_count, m_sock_number, m_pfilter, m_connection_type) );
connections_mutex.lock();
- connections_.push_back(std::make_pair(boost::get_system_time(), new_connection_l));
+ connections_.insert(new_connection_l);
MDEBUG("connections_ size now " << connections_.size());
connections_mutex.unlock();
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ CRITICAL_REGION_LOCAL(connections_mutex); connections_.erase(new_connection_l); });
boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
//////////////////////////////////////////////////////////////////////////
@@ -1113,6 +1103,11 @@ POP_WARNINGS
{
_dbg3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
" from " << lep.address().to_string() << ':' << lep.port());
+
+ // start adds the connection to the config object's list, so we don't need to have it locally anymore
+ connections_mutex.lock();
+ connections_.erase(new_connection_l);
+ connections_mutex.unlock();
bool r = new_connection_l->start(false, 1 < m_threads_count);
if (r)
{
diff --git a/external/db_drivers/liblmdb/mdb.c b/external/db_drivers/liblmdb/mdb.c
index 377512ebe..b3de9702f 100644
--- a/external/db_drivers/liblmdb/mdb.c
+++ b/external/db_drivers/liblmdb/mdb.c
@@ -6288,6 +6288,10 @@ release:
if (rc)
return rc;
}
+#ifdef MDB_VL32
+ if (mc->mc_ovpg == mp)
+ mc->mc_ovpg = NULL;
+#endif
mc->mc_db->md_overflow_pages -= ovpages;
return 0;
}
diff --git a/external/miniupnpc/.gitignore b/external/miniupnpc/.gitignore
index b1209ccd8..6bc38af20 100644
--- a/external/miniupnpc/.gitignore
+++ b/external/miniupnpc/.gitignore
@@ -4,6 +4,9 @@ build/
*.a
*.so
*.dll
+*.dll.def
+*.exe
+*.lib
*.dylib
Makefile.bak
miniupnpcstrings.h
@@ -30,3 +33,4 @@ testigddescparse
validateigddescparse
dist/
miniupnpc.egg-info/
+init
diff --git a/external/miniupnpc/Changelog.txt b/external/miniupnpc/Changelog.txt
index 078bebce3..a3ef51809 100644
--- a/external/miniupnpc/Changelog.txt
+++ b/external/miniupnpc/Changelog.txt
@@ -1,6 +1,16 @@
-$Id: Changelog.txt,v 1.223 2016/04/19 21:06:20 nanard Exp $
+$Id: Changelog.txt,v 1.226 2016/12/16 08:57:19 nanard Exp $
miniUPnP client Changelog.
+2017/05/05:
+ Fix CVE-2017-8798 Thanks to tin/Team OSTStrom
+
+2016/11/11:
+ check strlen before memcmp in XML parsing portlistingparse.c
+ fix build under SOLARIS and CYGWIN
+
+2016/10/11:
+ Add python 3 compatibility to IGD test
+
VERSION 2.0 : released 2016/04/19
2016/01/24:
diff --git a/external/miniupnpc/LICENSE b/external/miniupnpc/LICENSE
index cb5a06044..081673370 100644
--- a/external/miniupnpc/LICENSE
+++ b/external/miniupnpc/LICENSE
@@ -1,5 +1,5 @@
MiniUPnPc
-Copyright (c) 2005-2015, Thomas BERNARD
+Copyright (c) 2005-2016, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/external/miniupnpc/MANIFEST.in b/external/miniupnpc/MANIFEST.in
index 54b86f95e..543ffd6ac 100644
--- a/external/miniupnpc/MANIFEST.in
+++ b/external/miniupnpc/MANIFEST.in
@@ -1,4 +1,6 @@
include README
+include VERSION
+include LICENSE
include miniupnpcmodule.c
include setup.py
include *.h
diff --git a/external/miniupnpc/Makefile b/external/miniupnpc/Makefile
index b7826caa1..178fbf5d3 100644
--- a/external/miniupnpc/Makefile
+++ b/external/miniupnpc/Makefile
@@ -1,9 +1,9 @@
-# $Id: Makefile,v 1.133 2016/01/24 17:24:35 nanard Exp $
+# $Id: Makefile,v 1.134 2016/10/07 09:04:36 nanard Exp $
# MiniUPnP Project
# http://miniupnp.free.fr/
# http://miniupnp.tuxfamily.org/
# https://github.com/miniupnp/miniupnp
-# (c) 2005-2015 Thomas Bernard
+# (c) 2005-2017 Thomas Bernard
# to install use :
# $ make DESTDIR=/tmp/dummylocation install
# or
@@ -48,7 +48,7 @@ CFLAGS += -D_XOPEN_SOURCE=600
endif
endif
#CFLAGS += -ansi
-# -DNO_GETADDRINFO
+#CFLAGS += -DNO_GETADDRINFO
INSTALL = install
SH = /bin/sh
JAVA = java
@@ -65,7 +65,9 @@ JNAERATORARGS = -mode StandaloneJar -runtime JNAerator -library miniupnpc
JNAERATORBASEURL = https://repo1.maven.org/maven2/com/nativelibs4java/jnaerator/0.12
ifeq (SunOS, $(OS))
- LDFLAGS=-lsocket -lnsl -lresolv
+ LDLIBS=-lsocket -lnsl -lresolv
+ CFLAGS += -D__EXTENSIONS__
+ CFLAGS += -std=c99
endif
# APIVERSION is used to build SONAME
@@ -85,7 +87,9 @@ LIBOBJS = miniwget.o minixml.o igd_desc_parse.o minisoap.o \
connecthostport.o portlistingparse.o receivedata.o upnpdev.o
ifneq ($(OS), AmigaOS)
+ifeq (,$(findstring CYGWIN,$(OS)))
CFLAGS := -fPIC $(CFLAGS)
+endif
LIBOBJS := $(LIBOBJS) minissdpc.o
endif
@@ -169,18 +173,18 @@ check: validateminixml validateminiwget validateupnpreplyparse \
everything: all $(EXECUTABLES_ADDTESTS)
pythonmodule: $(LIBRARY) miniupnpcmodule.c setup.py
- python setup.py build
+ MAKE=$(MAKE) python setup.py build
touch $@
installpythonmodule: pythonmodule
- python setup.py install
+ MAKE=$(MAKE) python setup.py install
pythonmodule3: $(LIBRARY) miniupnpcmodule.c setup.py
- python3 setup.py build
+ MAKE=$(MAKE) python3 setup.py build
touch $@
installpythonmodule3: pythonmodule3
- python3 setup.py install
+ MAKE=$(MAKE) python3 setup.py install
validateminixml: minixmlvalid
@echo "minixml validation test"
diff --git a/external/miniupnpc/README b/external/miniupnpc/README
index 91535dbc8..0d3b8054c 100644
--- a/external/miniupnpc/README
+++ b/external/miniupnpc/README
@@ -1,9 +1,8 @@
Project: miniupnp
-Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
+Project web page: http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
github: https://github.com/miniupnp/miniupnp
-freecode: http://freecode.com/projects/miniupnp
Author: Thomas Bernard
-Copyright (c) 2005-2016 Thomas Bernard
+Copyright (c) 2005-2017 Thomas Bernard
This software is subject to the conditions detailed in the
LICENSE file provided within this distribution.
@@ -58,7 +57,7 @@ If you are using libminiupnpc in your application, please
send me an email !
For any question, you can use the web forum :
-http://miniupnp.tuxfamily.org/forum/
+https://miniupnp.tuxfamily.org/forum/
Bugs should be reported on github :
https://github.com/miniupnp/miniupnp/issues
diff --git a/external/miniupnpc/connecthostport.c b/external/miniupnpc/connecthostport.c
index 1f2a032ee..d28aaf5e0 100644
--- a/external/miniupnpc/connecthostport.c
+++ b/external/miniupnpc/connecthostport.c
@@ -1,7 +1,7 @@
/* $Id: connecthostport.c,v 1.15 2015/10/09 16:26:19 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2010-2015 Thomas Bernard
+ * Copyright (c) 2010-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -36,15 +36,13 @@
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
* during the connect() call */
#define MINIUPNPC_IGNORE_EINTR
-#ifndef USE_GETHOSTBYNAME
#include <sys/socket.h>
#include <sys/select.h>
-#endif /* #ifndef USE_GETHOSTBYNAME */
#endif /* #else _WIN32 */
/* definition of PRINT_SOCKET_ERROR */
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
@@ -100,13 +98,13 @@ int connecthostport(const char * host, unsigned short port,
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
}
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
}
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
dest.sin_family = AF_INET;
diff --git a/external/miniupnpc/minihttptestserver.c b/external/miniupnpc/minihttptestserver.c
index 1848bde26..e4cdc203c 100644
--- a/external/miniupnpc/minihttptestserver.c
+++ b/external/miniupnpc/minihttptestserver.c
@@ -1,7 +1,7 @@
/* $Id: minihttptestserver.c,v 1.19 2015/11/17 09:07:17 nanard Exp $ */
/* Project : miniUPnP
* Author : Thomas Bernard
- * Copyright (c) 2011-2015 Thomas Bernard
+ * Copyright (c) 2011-2016 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
@@ -611,7 +611,7 @@ int main(int argc, char * * argv) {
if(pid < 0) {
perror("wait");
} else {
- printf("child(%d) terminated with status %d\n", pid, status);
+ printf("child(%d) terminated with status %d\n", (int)pid, status);
}
--child_to_wait_for;
}
@@ -648,7 +648,7 @@ int main(int argc, char * * argv) {
if(pid < 0) {
perror("wait");
} else {
- printf("child(%d) terminated with status %d\n", pid, status);
+ printf("child(%d) terminated with status %d\n", (int)pid, status);
}
--child_to_wait_for;
}
diff --git a/external/miniupnpc/minisoap.c b/external/miniupnpc/minisoap.c
index 5c9a11438..76225f4b6 100644
--- a/external/miniupnpc/minisoap.c
+++ b/external/miniupnpc/minisoap.c
@@ -25,7 +25,7 @@
#include <stdlib.h>
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
diff --git a/external/miniupnpc/minissdpc.c b/external/miniupnpc/minissdpc.c
index dc4f94702..8eee2e927 100644
--- a/external/miniupnpc/minissdpc.c
+++ b/external/miniupnpc/minissdpc.c
@@ -1,9 +1,9 @@
-/* $Id: minissdpc.c,v 1.28 2015/09/18 13:05:39 nanard Exp $ */
+/* $Id: minissdpc.c,v 1.32 2016/10/07 09:04:36 nanard Exp $ */
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Web : http://miniupnp.free.fr/
* Author : Thomas BERNARD
- * copyright (c) 2005-2015 Thomas Bernard
+ * copyright (c) 2005-2017 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
/*#include <syslog.h>*/
@@ -62,7 +62,7 @@ struct sockaddr_un {
#endif
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
@@ -73,6 +73,9 @@ struct sockaddr_un {
#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
#include <sys/ioctl.h>
+#if defined(__sun)
+#include <sys/sockio.h>
+#endif
#endif
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
@@ -169,7 +172,7 @@ connectToMiniSSDPD(const char * socketpath)
{
int s;
struct sockaddr_un addr;
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
struct timeval timeout;
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
@@ -180,23 +183,25 @@ connectToMiniSSDPD(const char * socketpath)
perror("socket(unix)");
return MINISSDPC_SOCKET_ERROR;
}
-#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
+#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
/* setting a 3 seconds timeout */
+ /* not supported for AF_UNIX sockets under Solaris */
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- perror("setsockopt");
+ perror("setsockopt SO_RCVTIMEO unix");
}
timeout.tv_sec = 3;
timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
{
- perror("setsockopt");
+ perror("setsockopt SO_SNDTIMEO unix");
}
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
if(!socketpath)
socketpath = "/var/run/minissdpd.sock";
+ memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
/* TODO : check if we need to handle the EINTR */
@@ -498,6 +503,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
unsigned long _ttl = (unsigned long)ttl;
#endif
int linklocal = 1;
+ int sentok;
if(error)
*error = MINISSDPC_UNKNOWN_ERROR;
@@ -608,14 +614,22 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
return NULL;
}
+ if(ipv6) {
+ int mcastHops = ttl;
+ if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
+ {
+ PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
+ }
+ } else {
#ifdef _WIN32
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
#else /* _WIN32 */
- if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
+ if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
#endif /* _WIN32 */
- {
- /* not a fatal error */
- PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ {
+ /* not a fatal error */
+ PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
+ }
}
if(multicastif)
@@ -628,7 +642,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
}
#else
#ifdef DEBUG
@@ -643,7 +657,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
} else {
#ifdef HAS_IP_MREQN
@@ -653,7 +667,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
reqn.imr_ifindex = if_nametoindex(multicastif);
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
#elif !defined(_WIN32)
struct ifreq ifr;
@@ -667,7 +681,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
{
- PRINT_SOCKET_ERROR("setsockopt");
+ PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
}
#else /* _WIN32 */
#ifdef DEBUG
@@ -700,6 +714,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
}
/* receiving SSDP response packet */
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
+ sentok = 0;
/* sending the SSDP M-SEARCH packet */
n = snprintf(bufr, sizeof(bufr),
MSearchMsgFmt,
@@ -743,7 +758,8 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(error)
*error = MINISSDPC_SOCKET_ERROR;
PRINT_SOCKET_ERROR("sendto");
- break;
+ } else {
+ sentok = 1;
}
#else /* #ifdef NO_GETADDRINFO */
memset(&hints, 0, sizeof(hints));
@@ -775,19 +791,20 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
#endif
PRINT_SOCKET_ERROR("sendto");
continue;
+ } else {
+ sentok = 1;
}
}
freeaddrinfo(servinfo);
- if(n < 0) {
+ if(!sentok) {
if(error)
*error = MINISSDPC_SOCKET_ERROR;
- break;
}
#endif /* #ifdef NO_GETADDRINFO */
/* Waiting for SSDP REPLY packet to M-SEARCH
* if searchalltypes is set, enter the loop only
* when the last deviceType is reached */
- if(!searchalltypes || !deviceTypes[deviceIndex + 1]) do {
+ if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) do {
n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
if (n < 0) {
/* error */
diff --git a/external/miniupnpc/miniupnpcmodule.c b/external/miniupnpc/miniupnpcmodule.c
index fd970476c..8ffbf51a1 100644
--- a/external/miniupnpc/miniupnpcmodule.c
+++ b/external/miniupnpc/miniupnpcmodule.c
@@ -2,7 +2,7 @@
/* Project : miniupnp
* Author : Thomas BERNARD
* website : http://miniupnp.tuxfamily.org/
- * copyright (c) 2007-2014 Thomas Bernard
+ * copyright (c) 2007-2016 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENCE file. */
#include <Python.h>
@@ -12,6 +12,10 @@
#include "upnpcommands.h"
#include "upnperrors.h"
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
/* for compatibility with Python < 2.4 */
#ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
@@ -303,7 +307,7 @@ UPnP_addportmapping(UPnPObject *self, PyObject *args)
const char * remoteHost;
const char * leaseDuration = "0";
int r;
- if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto,
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto,
&host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
@@ -345,7 +349,7 @@ UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
const char * remoteHost;
const char * leaseDuration = "0";
int r;
- if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
+ if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
return NULL;
Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort);
@@ -669,6 +673,10 @@ initminiupnpc(void)
PyObject* m;
#ifdef _WIN32
+ /* initialize Winsock. */
+ WSADATA wsaData;
+ int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
+
UPnPType.tp_new = PyType_GenericNew;
#endif
if (PyType_Ready(&UPnPType) < 0)
diff --git a/external/miniupnpc/miniwget.c b/external/miniupnpc/miniwget.c
index ca88a1e9d..1eda57c5e 100644
--- a/external/miniupnpc/miniwget.c
+++ b/external/miniupnpc/miniwget.c
@@ -2,7 +2,7 @@
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -83,8 +83,10 @@ getHTTPResponse(int s, int * size, int * status_code)
unsigned int content_buf_used = 0;
char chunksize_buf[32];
unsigned int chunksize_buf_index;
+#ifdef DEBUG
char * reason_phrase = NULL;
int reason_phrase_len = 0;
+#endif
if(status_code) *status_code = -1;
header_buf = malloc(header_buf_len);
@@ -109,7 +111,7 @@ getHTTPResponse(int s, int * size, int * status_code)
chunksize_buf[0] = '\0';
chunksize_buf_index = 0;
- while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
+ while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0)
{
if(endofheaders == 0)
{
@@ -181,8 +183,10 @@ getHTTPResponse(int s, int * size, int * status_code)
*status_code = atoi(header_buf + sp + 1);
else
{
+#ifdef DEBUG
reason_phrase = header_buf + sp + 1;
reason_phrase_len = i - sp - 1;
+#endif
break;
}
}
@@ -280,11 +284,12 @@ getHTTPResponse(int s, int * size, int * status_code)
goto end_of_stream;
}
}
- bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
+ /* it is guaranteed that (n >= i) */
+ bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i);
if((content_buf_used + bytestocopy) > content_buf_len)
{
char * tmp;
- if(content_length >= (int)(content_buf_used + bytestocopy)) {
+ if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + bytestocopy;
@@ -309,14 +314,15 @@ getHTTPResponse(int s, int * size, int * status_code)
{
/* not chunked */
if(content_length > 0
- && (int)(content_buf_used + n) > content_length) {
+ && (content_buf_used + n) > (unsigned int)content_length) {
/* skipping additional bytes */
n = content_length - content_buf_used;
}
if(content_buf_used + n > content_buf_len)
{
char * tmp;
- if(content_length >= (int)(content_buf_used + n)) {
+ if(content_length >= 0
+ && (unsigned int)content_length >= (content_buf_used + n)) {
content_buf_len = content_length;
} else {
content_buf_len = content_buf_used + n;
@@ -336,7 +342,7 @@ getHTTPResponse(int s, int * size, int * status_code)
}
}
/* use the Content-Length header value if available */
- if(content_length > 0 && (int)content_buf_used >= content_length)
+ if(content_length > 0 && content_buf_used >= (unsigned int)content_length)
{
#ifdef DEBUG
printf("End of HTTP content\n");
diff --git a/external/miniupnpc/msvc/.gitignore b/external/miniupnpc/msvc/.gitignore
new file mode 100644
index 000000000..6d88bf128
--- /dev/null
+++ b/external/miniupnpc/msvc/.gitignore
@@ -0,0 +1,4 @@
+*.db
+.vs
+Debug/
+Release/
diff --git a/external/miniupnpc/msvc/miniupnpc.vcxproj b/external/miniupnpc/msvc/miniupnpc.vcxproj
new file mode 100644
index 000000000..2725136e3
--- /dev/null
+++ b/external/miniupnpc/msvc/miniupnpc.vcxproj
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{D28CE435-CB33-4BAE-8A52-C6EF915956F5}</ProjectGuid>
+ <RootNamespace>miniupnpc</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\connecthostport.c" />
+ <ClCompile Include="..\igd_desc_parse.c" />
+ <ClCompile Include="..\minisoap.c" />
+ <ClCompile Include="..\minissdpc.c" />
+ <ClCompile Include="..\miniupnpc.c" />
+ <ClCompile Include="..\miniwget.c" />
+ <ClCompile Include="..\minixml.c" />
+ <ClCompile Include="..\portlistingparse.c" />
+ <ClCompile Include="..\receivedata.c" />
+ <ClCompile Include="..\upnpcommands.c" />
+ <ClCompile Include="..\upnpdev.c" />
+ <ClCompile Include="..\upnperrors.c" />
+ <ClCompile Include="..\upnpreplyparse.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\connecthostport.h" />
+ <ClInclude Include="..\declspec.h" />
+ <ClInclude Include="..\igd_desc_parse.h" />
+ <ClInclude Include="..\minisoap.h" />
+ <ClInclude Include="..\minissdpc.h" />
+ <ClInclude Include="..\miniupnpc.h" />
+ <ClInclude Include="..\miniupnpcstrings.h" />
+ <ClInclude Include="..\miniupnpctypes.h" />
+ <ClInclude Include="..\miniwget.h" />
+ <ClInclude Include="..\minixml.h" />
+ <ClInclude Include="..\portlistingparse.h" />
+ <ClInclude Include="..\receivedata.h" />
+ <ClInclude Include="..\upnpcommands.h" />
+ <ClInclude Include="..\upnpdev.h" />
+ <ClInclude Include="..\upnperrors.h" />
+ <ClInclude Include="..\upnpreplyparse.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/external/miniupnpc/msvc/miniupnpc.vcxproj.filters b/external/miniupnpc/msvc/miniupnpc.vcxproj.filters
new file mode 100644
index 000000000..01a4dbeb2
--- /dev/null
+++ b/external/miniupnpc/msvc/miniupnpc.vcxproj.filters
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Fichiers sources">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Fichiers d%27en-tête">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Fichiers de ressources">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\connecthostport.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\igd_desc_parse.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\minisoap.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\minissdpc.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\miniupnpc.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\miniwget.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\minixml.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\portlistingparse.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\receivedata.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\upnpcommands.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\upnpdev.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\upnperrors.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ <ClCompile Include="..\upnpreplyparse.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\connecthostport.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\declspec.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\igd_desc_parse.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\minisoap.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\minissdpc.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\miniupnpc.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\miniupnpcstrings.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\miniupnpctypes.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\miniwget.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\minixml.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\portlistingparse.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\receivedata.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\upnpcommands.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\upnpdev.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\upnperrors.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ <ClInclude Include="..\upnpreplyparse.h">
+ <Filter>Fichiers d%27en-tête</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/external/miniupnpc/msvc/miniupnpc_vs2015.sln b/external/miniupnpc/msvc/miniupnpc_vs2015.sln
new file mode 100644
index 000000000..27a43f6c3
--- /dev/null
+++ b/external/miniupnpc/msvc/miniupnpc_vs2015.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc", "miniupnpc.vcxproj", "{D28CE435-CB33-4BAE-8A52-C6EF915956F5}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "upnpc-static", "upnpc-static.vcxproj", "{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.ActiveCfg = Debug|Win32
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Debug|Win32.Build.0 = Debug|Win32
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.ActiveCfg = Release|Win32
+ {D28CE435-CB33-4BAE-8A52-C6EF915956F5}.Release|Win32.Build.0 = Release|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Debug|Win32.Build.0 = Debug|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.ActiveCfg = Release|Win32
+ {469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/external/miniupnpc/msvc/upnpc-static.vcxproj b/external/miniupnpc/msvc/upnpc-static.vcxproj
new file mode 100644
index 000000000..44dea81e8
--- /dev/null
+++ b/external/miniupnpc/msvc/upnpc-static.vcxproj
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{469E1CF6-08A2-4B7B-A2AA-5BDB089857C1}</ProjectGuid>
+ <RootNamespace>upnpcstatic</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir>$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;MINIUPNP_STATICLIB;DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;Debug\miniupnpc.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader />
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;Release\miniupnpc.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\upnpc.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="miniupnpc.vcxproj">
+ <Project>{d28ce435-cb33-4bae-8a52-c6ef915956f5}</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/external/miniupnpc/msvc/upnpc-static.vcxproj.filters b/external/miniupnpc/msvc/upnpc-static.vcxproj.filters
new file mode 100644
index 000000000..2e75de02f
--- /dev/null
+++ b/external/miniupnpc/msvc/upnpc-static.vcxproj.filters
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Fichiers sources">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Fichiers d%27en-tête">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Fichiers de ressources">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\upnpc.c">
+ <Filter>Fichiers sources</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/external/miniupnpc/portlistingparse.c b/external/miniupnpc/portlistingparse.c
index 0e0927803..55859f271 100644
--- a/external/miniupnpc/portlistingparse.c
+++ b/external/miniupnpc/portlistingparse.c
@@ -1,7 +1,7 @@
/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
/* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
- * (c) 2011-2015 Thomas Bernard
+ * (c) 2011-2016 Thomas Bernard
* This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */
#include <string.h>
@@ -55,7 +55,7 @@ startelt(void * d, const char * name, int l)
pdata->curelt = PortMappingEltNone;
for(i = 0; elements[i].str; i++)
{
- if(memcmp(name, elements[i].str, l) == 0)
+ if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
{
pdata->curelt = elements[i].code;
break;
diff --git a/external/miniupnpc/receivedata.c b/external/miniupnpc/receivedata.c
index ef85a3db4..d003eb69d 100644
--- a/external/miniupnpc/receivedata.c
+++ b/external/miniupnpc/receivedata.c
@@ -28,7 +28,7 @@
#endif /* _WIN32 */
#ifdef _WIN32
-#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
+#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError());
#else
#define PRINT_SOCKET_ERROR(x) perror(x)
#endif
diff --git a/external/miniupnpc/setup.py b/external/miniupnpc/setup.py
index aa632db82..24a676d85 100755
--- a/external/miniupnpc/setup.py
+++ b/external/miniupnpc/setup.py
@@ -1,19 +1,25 @@
#! /usr/bin/python
# vim: tabstop=8 shiftwidth=8 expandtab
# $Id: setup.py,v 1.9 2012/05/23 08:50:10 nanard Exp $
-# the MiniUPnP Project (c) 2007-2014 Thomas Bernard
+# the MiniUPnP Project (c) 2007-2017 Thomas Bernard
# http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/
#
# python script to build the miniupnpc module under unix
#
-# replace libminiupnpc.a by libminiupnpc.so for shared library usage
-try:
- from setuptools import setup, Extension
-except ImportError:
- from distutils.core import setup, Extension
-from distutils import sysconfig
-sysconfig.get_config_vars()["OPT"] = ''
-sysconfig.get_config_vars()["CFLAGS"] = ''
+# Uses MAKE environment variable (defaulting to 'make')
+
+from setuptools import setup, Extension
+from setuptools.command import build_ext
+import subprocess
+import os
+
+EXT = ['libminiupnpc.a']
+
+class make_then_build_ext(build_ext.build_ext):
+ def run(self):
+ subprocess.check_call([os.environ.get('MAKE', 'make')] + EXT)
+ build_ext.build_ext.run(self)
+
setup(name="miniupnpc",
version=open('VERSION').read().strip(),
author='Thomas BERNARD',
@@ -21,8 +27,9 @@ setup(name="miniupnpc",
license=open('LICENSE').read(),
url='http://miniupnp.free.fr/',
description='miniUPnP client',
+ cmdclass={'build_ext': make_then_build_ext},
ext_modules=[
Extension(name="miniupnpc", sources=["miniupnpcmodule.c"],
- extra_objects=["libminiupnpc.a"])
+ extra_objects=EXT)
])
diff --git a/external/miniupnpc/testdesc/new_LiveBox_desc.xml b/external/miniupnpc/testdesc/new_LiveBox_desc.xml
index 9d5160bb8..620eb55af 100644
--- a/external/miniupnpc/testdesc/new_LiveBox_desc.xml
+++ b/external/miniupnpc/testdesc/new_LiveBox_desc.xml
@@ -87,4 +87,4 @@
</device>
</deviceList>
</device>
-</root>
+</root> \ No newline at end of file
diff --git a/external/miniupnpc/testupnpigd.py b/external/miniupnpc/testupnpigd.py
index 6d167a4ce..33019bd99 100755
--- a/external/miniupnpc/testupnpigd.py
+++ b/external/miniupnpc/testupnpigd.py
@@ -8,7 +8,11 @@
# import the python miniupnpc module
import miniupnpc
import socket
-import BaseHTTPServer
+
+try:
+ from http.server import BaseHTTPRequestHandler, HTTPServer
+except ImportError:
+ from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
# function definition
def list_redirections():
@@ -17,11 +21,11 @@ def list_redirections():
p = u.getgenericportmapping(i)
if p==None:
break
- print i, p
+ print(i, p)
i = i + 1
#define the handler class for HTTP connections
-class handler_class(BaseHTTPServer.BaseHTTPRequestHandler):
+class handler_class(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.end_headers()
@@ -37,20 +41,20 @@ u = miniupnpc.UPnP()
u.discoverdelay = 200;
try:
- print 'Discovering... delay=%ums' % u.discoverdelay
+ print('Discovering... delay=%ums' % u.discoverdelay)
ndevices = u.discover()
- print ndevices, 'device(s) detected'
+ print(ndevices, 'device(s) detected')
# select an igd
u.selectigd()
# display information about the IGD and the internet connection
- print 'local ip address :', u.lanaddr
+ print('local ip address :', u.lanaddr)
externalipaddress = u.externalipaddress()
- print 'external ip address :', externalipaddress
- print u.statusinfo(), u.connectiontype()
+ print('external ip address :', externalipaddress)
+ print(u.statusinfo(), u.connectiontype())
#instanciate a HTTPd object. The port is assigned by the system.
- httpd = BaseHTTPServer.HTTPServer((u.lanaddr, 0), handler_class)
+ httpd = HTTPServer((u.lanaddr, 0), handler_class)
eport = httpd.server_port
# find a free port for the redirection
@@ -59,26 +63,26 @@ try:
eport = eport + 1
r = u.getspecificportmapping(eport, 'TCP')
- print 'trying to redirect %s port %u TCP => %s port %u TCP' % (externalipaddress, eport, u.lanaddr, httpd.server_port)
+ print('trying to redirect %s port %u TCP => %s port %u TCP' % (externalipaddress, eport, u.lanaddr, httpd.server_port))
b = u.addportmapping(eport, 'TCP', u.lanaddr, httpd.server_port,
'UPnP IGD Tester port %u' % eport, '')
if b:
- print 'Success. Now waiting for some HTTP request on http://%s:%u' % (externalipaddress ,eport)
+ print('Success. Now waiting for some HTTP request on http://%s:%u' % (externalipaddress ,eport))
try:
httpd.handle_request()
httpd.server_close()
- except KeyboardInterrupt, details:
- print "CTRL-C exception!", details
+ except KeyboardInterrupt as details:
+ print("CTRL-C exception!", details)
b = u.deleteportmapping(eport, 'TCP')
if b:
- print 'Successfully deleted port mapping'
+ print('Successfully deleted port mapping')
else:
- print 'Failed to remove port mapping'
+ print('Failed to remove port mapping')
else:
- print 'Failed'
+ print('Failed')
httpd.server_close()
-except Exception, e:
- print 'Exception :', e
+except Exception as e:
+ print('Exception :', e)
diff --git a/external/miniupnpc/upnpc.c b/external/miniupnpc/upnpc.c
index 94f131c87..e719ecec5 100644
--- a/external/miniupnpc/upnpc.c
+++ b/external/miniupnpc/upnpc.c
@@ -1,7 +1,7 @@
-/* $Id: upnpc.c,v 1.114 2016/01/22 15:04:23 nanard Exp $ */
+/* $Id: upnpc.c,v 1.117 2017/05/26 15:26:55 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2016 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -242,7 +242,7 @@ static void NewListRedirections(struct UPNPUrls * urls,
* 2 - get extenal ip address
* 3 - Add port mapping
* 4 - get this port mapping from the IGD */
-static void SetRedirectAndTest(struct UPNPUrls * urls,
+static int SetRedirectAndTest(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * iaddr,
const char * iport,
@@ -262,13 +262,13 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
if(!iaddr || !iport || !eport || !proto)
{
fprintf(stderr, "Wrong arguments\n");
- return;
+ return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "invalid protocol\n");
- return;
+ return -1;
}
r = UPNP_GetExternalIPAddress(urls->controlURL,
@@ -292,9 +292,11 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
eport, iport, iaddr, description,
proto, 0, leaseDuration);
- if(r!=UPNPCOMMAND_SUCCESS)
+ if(r!=UPNPCOMMAND_SUCCESS) {
printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
eport, iport, iaddr, r, strupnperror(r));
+ return -2;
+ }
}
r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
@@ -302,17 +304,19 @@ static void SetRedirectAndTest(struct UPNPUrls * urls,
eport, proto, NULL/*remoteHost*/,
intClient, intPort, NULL/*desc*/,
NULL/*enabled*/, duration);
- if(r!=UPNPCOMMAND_SUCCESS)
+ if(r!=UPNPCOMMAND_SUCCESS) {
printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
r, strupnperror(r));
- else {
+ return -2;
+ } else {
printf("InternalIP:Port = %s:%s\n", intClient, intPort);
printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
externalIPAddress, eport, proto, intClient, intPort, duration);
}
+ return 0;
}
-static void
+static int
RemoveRedirect(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * eport,
@@ -323,19 +327,25 @@ RemoveRedirect(struct UPNPUrls * urls,
if(!proto || !eport)
{
fprintf(stderr, "invalid arguments\n");
- return;
+ return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "protocol invalid\n");
- return;
+ return -1;
}
r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
- printf("UPNP_DeletePortMapping() returned : %d\n", r);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMapping() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMapping() returned : %d\n", r);
+ }
+ return 0;
}
-static void
+static int
RemoveRedirectRange(struct UPNPUrls * urls,
struct IGDdatas * data,
const char * ePortStart, char const * ePortEnd,
@@ -349,16 +359,22 @@ RemoveRedirectRange(struct UPNPUrls * urls,
if(!proto || !ePortStart || !ePortEnd)
{
fprintf(stderr, "invalid arguments\n");
- return;
+ return -1;
}
proto = protofix(proto);
if(!proto)
{
fprintf(stderr, "protocol invalid\n");
- return;
+ return -1;
}
r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
- printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
+ if(r!=UPNPCOMMAND_SUCCESS) {
+ printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r);
+ return -2;
+ }else {
+ printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
+ }
+ return 0;
}
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
@@ -562,8 +578,8 @@ int main(int argc, char ** argv)
}
#endif
printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
- printf(" (c) 2005-2016 Thomas Bernard.\n");
- printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
+ printf(" (c) 2005-2017 Thomas Bernard.\n");
+ printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n"
"for more information.\n");
/* command line processing */
for(i=1; i<argc; i++)
@@ -711,29 +727,33 @@ int main(int argc, char ** argv)
NewListRedirections(&urls, &data);
break;
case 'a':
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)?commandargv[4]:"0",
- description, 0);
+ description, 0) < 0)
+ retcode = 2;
break;
case 'd':
- RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
- commandargc > 2 ? commandargv[2] : NULL);
+ if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
+ commandargc > 2 ? commandargv[2] : NULL) < 0)
+ retcode = 2;
break;
case 'n': /* aNy */
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
(commandargc > 4)?commandargv[4]:"0",
- description, 1);
+ description, 1) < 0)
+ retcode = 2;
break;
case 'N':
if (commandargc < 3)
fprintf(stderr, "too few arguments\n");
- RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
- commandargc > 3 ? commandargv[3] : NULL);
+ if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
+ commandargc > 3 ? commandargv[3] : NULL) < 0)
+ retcode = 2;
break;
case 's':
GetConnectionStatus(&urls, &data);
@@ -749,17 +769,19 @@ int main(int argc, char ** argv)
break;
} else if(is_int(commandargv[i+1])){
/* 2nd parameter is an integer : <port> <external_port> <protocol> */
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
lanaddr, commandargv[i],
commandargv[i+1], commandargv[i+2], "0",
- description, 0);
+ description, 0) < 0)
+ retcode = 2;
i+=3; /* 3 parameters parsed */
} else {
/* 2nd parameter not an integer : <port> <protocol> */
- SetRedirectAndTest(&urls, &data,
+ if (SetRedirectAndTest(&urls, &data,
lanaddr, commandargv[i],
commandargv[i], commandargv[i+1], "0",
- description, 0);
+ description, 0) < 0)
+ retcode = 2;
i+=2; /* 2 parameters parsed */
}
}
diff --git a/external/miniupnpc/upnpcommands.c b/external/miniupnpc/upnpcommands.c
index 76cf9e391..9f704496f 100644
--- a/external/miniupnpc/upnpcommands.c
+++ b/external/miniupnpc/upnpcommands.c
@@ -1,7 +1,8 @@
/* $Id: upnpcommands.c,v 1.46 2015/07/15 12:19:00 nanard Exp $ */
-/* Project : miniupnp
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2015 Thomas Bernard
+ * Copyright (c) 2005-2017 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
@@ -372,10 +373,11 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
AddPortMappingArgs[7].elt = "NewLeaseDuration";
AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddPortMapping", AddPortMappingArgs,
- &bufsize))) {
- free(AddPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -392,7 +394,6 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(AddPortMappingArgs);
return ret;
}
@@ -436,10 +437,11 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
AddPortMappingArgs[6].val = desc?desc:"libminiupnpc";
AddPortMappingArgs[7].elt = "NewLeaseDuration";
AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "AddAnyPortMapping", AddPortMappingArgs,
- &bufsize))) {
- free(AddPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "AddAnyPortMapping", AddPortMappingArgs,
+ &bufsize);
+ free(AddPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -461,7 +463,6 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
}
}
ClearNameValueList(&pdata);
- free(AddPortMappingArgs);
return ret;
}
@@ -490,10 +491,11 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
DeletePortMappingArgs[1].val = extPort;
DeletePortMappingArgs[2].elt = "NewProtocol";
DeletePortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePortMapping",
- DeletePortMappingArgs, &bufsize))) {
- free(DeletePortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMapping",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -507,7 +509,6 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePortMappingArgs);
return ret;
}
@@ -539,10 +540,11 @@ UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
DeletePortMappingArgs[3].elt = "NewManage";
DeletePortMappingArgs[3].val = manage;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "DeletePortMappingRange",
- DeletePortMappingArgs, &bufsize))) {
- free(DeletePortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "DeletePortMappingRange",
+ DeletePortMappingArgs, &bufsize);
+ free(DeletePortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -555,7 +557,6 @@ UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePortMappingArgs);
return ret;
}
@@ -587,10 +588,11 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
return UPNPCOMMAND_MEM_ALLOC_ERROR;
GetPortMappingArgs[0].elt = "NewPortMappingIndex";
GetPortMappingArgs[0].val = index;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetGenericPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetGenericPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
ParseNameValue(buffer, bufsize, &pdata);
@@ -652,7 +654,6 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
sscanf(p, "%d", &r);
}
ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
return r;
}
@@ -728,10 +729,11 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
GetPortMappingArgs[1].val = extPort;
GetPortMappingArgs[2].elt = "NewProtocol";
GetPortMappingArgs[2].val = proto;
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetSpecificPortMappingEntry",
- GetPortMappingArgs, &bufsize))) {
- free(GetPortMappingArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetSpecificPortMappingEntry",
+ GetPortMappingArgs, &bufsize);
+ free(GetPortMappingArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
/*DisplayNameValueList(buffer, bufsize);*/
@@ -779,7 +781,6 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
}
ClearNameValueList(&pdata);
- free(GetPortMappingArgs);
return ret;
}
@@ -824,13 +825,13 @@ UPNP_GetListOfPortMappings(const char * controlURL,
GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts";
GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000";
- if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype,
- "GetListOfPortMappings",
- GetListOfPortMappingsArgs, &bufsize))) {
- free(GetListOfPortMappingsArgs);
+ buffer = simpleUPnPcommand(-1, controlURL, servicetype,
+ "GetListOfPortMappings",
+ GetListOfPortMappingsArgs, &bufsize);
+ free(GetListOfPortMappingsArgs);
+ if(!buffer) {
return UPNPCOMMAND_HTTP_ERROR;
}
- free(GetListOfPortMappingsArgs);
/*DisplayNameValueList(buffer, bufsize);*/
ParseNameValue(buffer, bufsize, &pdata);
@@ -954,6 +955,7 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
GetOutboundPinholeTimeoutArgs[4].val = intClient;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize);
+ free(GetOutboundPinholeTimeoutArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -972,7 +974,6 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
*opTimeout = my_atoui(p);
}
ClearNameValueList(&pdata);
- free(GetOutboundPinholeTimeoutArgs);
return ret;
}
@@ -1031,6 +1032,7 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
AddPinholeArgs[5].val = leaseTime;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"AddPinhole", AddPinholeArgs, &bufsize);
+ free(AddPinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1053,7 +1055,6 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(AddPinholeArgs);
return ret;
}
@@ -1081,6 +1082,7 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
UpdatePinholeArgs[1].val = leaseTime;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"UpdatePinhole", UpdatePinholeArgs, &bufsize);
+ free(UpdatePinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1097,7 +1099,6 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(UpdatePinholeArgs);
return ret;
}
@@ -1122,6 +1123,7 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char
DeletePinholeArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"DeletePinhole", DeletePinholeArgs, &bufsize);
+ free(DeletePinholeArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
/*DisplayNameValueList(buffer, bufsize);*/
@@ -1138,7 +1140,6 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char
ret = UPNPCOMMAND_SUCCESS;
}
ClearNameValueList(&pdata);
- free(DeletePinholeArgs);
return ret;
}
@@ -1163,8 +1164,11 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
CheckPinholeWorkingArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize);
+ free(CheckPinholeWorkingArgs);
if(!buffer)
+ {
return UPNPCOMMAND_HTTP_ERROR;
+ }
ParseNameValue(buffer, bufsize, &pdata);
free(buffer); buffer = NULL;
@@ -1185,7 +1189,6 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
}
ClearNameValueList(&pdata);
- free(CheckPinholeWorkingArgs);
return ret;
}
@@ -1210,6 +1213,7 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
GetPinholePacketsArgs[0].val = uniqueID;
buffer = simpleUPnPcommand(-1, controlURL, servicetype,
"GetPinholePackets", GetPinholePacketsArgs, &bufsize);
+ free(GetPinholePacketsArgs);
if(!buffer)
return UPNPCOMMAND_HTTP_ERROR;
ParseNameValue(buffer, bufsize, &pdata);
@@ -1230,7 +1234,6 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
}
ClearNameValueList(&pdata);
- free(GetPinholePacketsArgs);
return ret;
}
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/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index b6978bdc4..c6e24ef98 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -2420,8 +2420,8 @@ bool BlockchainLMDB::for_blocks_range(const uint64_t& h1, const uint64_t& h2, st
MDB_cursor_op op;
if (h1)
{
- MDB_val_set(k, h1);
- op = MDB_SET;
+ k = MDB_val{sizeof(h1), (void*)&h1};
+ op = MDB_SET;
} else
{
op = MDB_FIRST;
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/dns_utils.cpp b/src/common/dns_utils.cpp
index e7ff11c5c..9c306505e 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -27,8 +27,6 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/dns_utils.h"
-#include "common/i18n.h"
-#include "cryptonote_basic/cryptonote_basic_impl.h"
// check local first (in the event of static or in-source compilation of libunbound)
#include "unbound.h"
@@ -326,8 +324,6 @@ bool DNSResolver::check_address_syntax(const char *addr) const
namespace dns_utils
{
-const char *tr(const char *str) { return i18n_translate(str, "tools::dns_utils"); }
-
//-----------------------------------------------------------------------
// TODO: parse the string in a less stupid way, probably with regex
std::string address_from_txt_record(const std::string& s)
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/common/util.cpp b/src/common/util.cpp
index 046961b06..74a6babf1 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -39,11 +39,13 @@ using namespace epee;
#include "net/http_client.h" // epee::net_utils::...
#ifdef WIN32
-#include <windows.h>
-#include <shlobj.h>
-#include <strsafe.h>
+ #include <windows.h>
+ #include <shlobj.h>
+ #include <strsafe.h>
#else
-#include <sys/utsname.h>
+ #include <sys/file.h>
+ #include <sys/utsname.h>
+ #include <sys/stat.h>
#endif
#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
@@ -53,7 +55,12 @@ namespace tools
{
std::function<void(int)> signal_handler::m_handler;
- std::unique_ptr<std::FILE, tools::close_file> create_private_file(const std::string& name)
+ private_file::private_file() noexcept : m_handle(), m_filename() {}
+
+ private_file::private_file(std::FILE* handle, std::string&& filename) noexcept
+ : m_handle(handle), m_filename(std::move(filename)) {}
+
+ private_file private_file::create(std::string name)
{
#ifdef WIN32
struct close_handle
@@ -70,17 +77,17 @@ namespace tools
const bool fail = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
process.reset(temp);
if (fail)
- return nullptr;
+ return {};
}
DWORD sid_size = 0;
GetTokenInformation(process.get(), TokenOwner, nullptr, 0, std::addressof(sid_size));
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
- return nullptr;
+ return {};
std::unique_ptr<char[]> sid{new char[sid_size]};
if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
- return nullptr;
+ return {};
const PSID psid = reinterpret_cast<const PTOKEN_OWNER>(sid.get())->Owner;
const DWORD daclSize =
@@ -88,17 +95,17 @@ namespace tools
const std::unique_ptr<char[]> dacl{new char[daclSize]};
if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION))
- return nullptr;
+ return {};
if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid))
- return nullptr;
+ return {};
SECURITY_DESCRIPTOR descriptor{};
if (!InitializeSecurityDescriptor(std::addressof(descriptor), SECURITY_DESCRIPTOR_REVISION))
- return nullptr;
+ return {};
if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
- return nullptr;
+ return {};
SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), std::addressof(descriptor), false};
std::unique_ptr<void, close_handle> file{
@@ -106,7 +113,7 @@ namespace tools
name.c_str(),
GENERIC_WRITE, FILE_SHARE_READ,
std::addressof(attributes),
- CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY,
+ CREATE_NEW, (FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE),
nullptr
)
};
@@ -121,22 +128,49 @@ namespace tools
{
_close(fd);
}
- return {real_file, tools::close_file{}};
+ return {real_file, std::move(name)};
}
}
#else
- const int fd = open(name.c_str(), (O_RDWR | O_EXCL | O_CREAT), S_IRUSR);
- if (0 <= fd)
+ const int fdr = open(name.c_str(), (O_RDONLY | O_CREAT), S_IRUSR);
+ if (0 <= fdr)
{
- std::FILE* file = fdopen(fd, "w");
- if (!file)
+ struct stat rstats = {};
+ if (fstat(fdr, std::addressof(rstats)) != 0)
{
- close(fd);
+ close(fdr);
+ return {};
+ }
+ fchmod(fdr, (S_IRUSR | S_IWUSR));
+ const int fdw = open(name.c_str(), O_RDWR);
+ fchmod(fdr, rstats.st_mode);
+ close(fdr);
+
+ if (0 <= fdw)
+ {
+ struct stat wstats = {};
+ if (fstat(fdw, std::addressof(wstats)) == 0 &&
+ rstats.st_dev == wstats.st_dev && rstats.st_ino == wstats.st_ino &&
+ flock(fdw, (LOCK_EX | LOCK_NB)) == 0 && ftruncate(fdw, 0) == 0)
+ {
+ std::FILE* file = fdopen(fdw, "w");
+ if (file) return {file, std::move(name)};
+ }
+ close(fdw);
}
- return {file, tools::close_file{}};
}
#endif
- return nullptr;
+ return {};
+ }
+
+ private_file::~private_file() noexcept
+ {
+ try
+ {
+ boost::system::error_code ec{};
+ boost::filesystem::remove(filename(), ec);
+ }
+ catch (...) {}
}
#ifdef WIN32
diff --git a/src/common/util.h b/src/common/util.h
index 2452bc9d5..48bdbbc28 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -60,8 +60,30 @@ namespace tools
}
};
- //! \return File only readable by owner. nullptr if `filename` exists.
- std::unique_ptr<std::FILE, close_file> create_private_file(const std::string& filename);
+ //! A file restricted to process owner AND process. Deletes file on destruction.
+ class private_file {
+ std::unique_ptr<std::FILE, close_file> m_handle;
+ std::string m_filename;
+
+ private_file(std::FILE* handle, std::string&& filename) noexcept;
+ public:
+
+ //! `handle() == nullptr && filename.empty()`.
+ private_file() noexcept;
+
+ /*! \return File only readable by owner and only used by this process
+ OR `private_file{}` on error. */
+ static private_file create(std::string filename);
+
+ private_file(private_file&&) = default;
+ private_file& operator=(private_file&&) = default;
+
+ //! Deletes `filename()` and closes `handle()`.
+ ~private_file() noexcept;
+
+ std::FILE* handle() const noexcept { return m_handle.get(); }
+ const std::string& filename() const noexcept { return m_filename; }
+ };
/*! \brief Returns the default data directory.
*
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/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 745dfb72e..e73f5d778 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -869,4 +869,21 @@ namespace cryptonote
block_hashes_calculated = block_hashes_calculated_count;
block_hashes_cached = block_hashes_cached_count;
}
+ //---------------------------------------------------------------
+ crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase)
+ {
+ crypto::hash hash;
+ crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash);
+ sc_add((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data);
+ return key;
+ }
+ //---------------------------------------------------------------
+ crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase)
+ {
+ crypto::hash hash;
+ crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash);
+ sc_sub((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data);
+ return key;
+ }
+
}
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index d8ccf8eec..00080fb98 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -212,6 +212,8 @@ namespace cryptonote
bool is_valid_decomposed_amount(uint64_t amount);
void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached);
+ crypto::secret_key encrypt_key(const crypto::secret_key &key, const std::string &passphrase);
+ crypto::secret_key decrypt_key(const crypto::secret_key &key, const std::string &passphrase);
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \
CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \
specific_type& variable_name = boost::get<specific_type>(variant_var);
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..274c8cd07 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)
{
@@ -3249,7 +3276,7 @@ leave:
// XXX old code adds miner tx here
- int tx_index = 0;
+ size_t tx_index = 0;
// Iterate over the block's transaction hashes, grabbing each
// from the tx_pool and validating them. Each is then added
// to txs. Keys spent in each are added to <keys> by the double spend check.
@@ -3331,7 +3358,7 @@ leave:
{
// ND: if fast_check is enabled for blocks, there is no need to check
// the transaction inputs, but do some sanity checks anyway.
- if (memcmp(&m_blocks_txs_check[tx_index++], &tx_id, sizeof(tx_id)) != 0)
+ if (tx_index >= m_blocks_txs_check.size() || memcmp(&m_blocks_txs_check[tx_index++], &tx_id, sizeof(tx_id)) != 0)
{
MERROR_VER("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs.");
//TODO: why is this done? make sure that keeping invalid blocks makes sense.
@@ -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..c31441a99 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);
@@ -1031,12 +1090,13 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.clear();
m_spent_key_images.clear();
- return m_blockchain.for_all_txpool_txes([this](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
+ std::vector<crypto::hash> remove;
+ bool r = m_blockchain.for_all_txpool_txes([this, &remove](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd) {
cryptonote::transaction tx;
if (!parse_and_validate_tx_from_blob(*bd, tx))
{
- MERROR("Failed to parse tx from txpool");
- return false;
+ MWARNING("Failed to parse tx from txpool, removing");
+ remove.push_back(txid);
}
if (!insert_key_images(tx, meta.kept_by_block))
{
@@ -1046,6 +1106,25 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid);
return true;
}, true);
+ if (!r)
+ return false;
+ if (!remove.empty())
+ {
+ LockedTXN lock(m_blockchain);
+ for (const auto &txid: remove)
+ {
+ try
+ {
+ m_blockchain.remove_txpool_tx(txid);
+ }
+ catch (const std::exception &e)
+ {
+ MWARNING("Failed to remove corrupt transaction: " << txid);
+ // ignore error
+ }
+ }
+ }
+ return true;
}
//---------------------------------------------------------------------------------
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..167e24ed3 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
@@ -781,7 +782,7 @@ bool t_rpc_command_executor::is_key_image_spent(const crypto::key_image &ki) {
if (1 == res.spent_status.size())
{
// first as hex
- tools::success_msg_writer() << ki << ": " << (res.spent_status.front() ? "spent" : "unspent");
+ tools::success_msg_writer() << ki << ": " << (res.spent_status.front() ? "spent" : "unspent") << (res.spent_status.front() == cryptonote::COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_POOL ? " (in pool)" : "");
}
else
{
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..7da123633 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();
}
//-----------------------------------------------------------------------------------
@@ -1127,6 +1090,8 @@ namespace nodetool
if (use_white_list) {
local_peers_count = m_peerlist.get_white_peers_count();
+ if (!local_peers_count)
+ return false;
max_random_index = std::min<uint64_t>(local_peers_count -1, 20);
random_index = get_random_index_with_fixed_probability(max_random_index);
} else {
@@ -1389,7 +1354,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;
@@ -1953,6 +1918,9 @@ namespace nodetool
{
peerlist_entry pe = AUTO_VAL_INIT(pe);
+ if (m_net_server.is_stop_signal_sent())
+ return false;
+
if (!m_peerlist.get_random_gray_peer(pe)) {
return false;
}
@@ -1973,4 +1941,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..35b76f3d6 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();
@@ -478,18 +478,30 @@ namespace cryptonote
bool r = m_core.get_pool_transactions(pool_txs);
if(r)
{
- for (std::list<transaction>::const_iterator i = pool_txs.begin(); i != pool_txs.end(); ++i)
+ // sort to match original request
+ std::list<transaction> sorted_txs;
+ std::list<cryptonote::transaction>::const_iterator i;
+ for (const crypto::hash &h: vh)
{
- crypto::hash tx_hash = get_transaction_hash(*i);
- std::list<crypto::hash>::iterator mi = std::find(missed_txs.begin(), missed_txs.end(), tx_hash);
- if (mi != missed_txs.end())
+ if (std::find(missed_txs.begin(), missed_txs.end(), h) == missed_txs.end())
+ {
+ // core returns the ones it finds in the right order
+ if (get_transaction_hash(txs.front()) != h)
+ {
+ res.status = "Failed: tx hash mismatch";
+ return true;
+ }
+ sorted_txs.push_back(std::move(txs.front()));
+ txs.pop_front();
+ }
+ else if ((i = std::find_if(pool_txs.begin(), pool_txs.end(), [h](cryptonote::transaction &tx) { return h == cryptonote::get_transaction_hash(tx); })) != pool_txs.end())
{
- pool_tx_hashes.insert(tx_hash);
- missed_txs.erase(mi);
- txs.push_back(*i);
+ sorted_txs.push_back(*i);
+ missed_txs.remove(h);
++found_in_pool;
}
}
+ txs = sorted_txs;
}
LOG_PRINT_L2("Found " << found_in_pool << "/" << vh.size() << " transactions in the pool");
}
@@ -510,11 +522,12 @@ namespace cryptonote
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
if (e.in_pool)
{
- e.block_height = std::numeric_limits<uint64_t>::max();
+ e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
}
else
{
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
+ e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height);
}
// fill up old style responses too, in case an old wallet asks
@@ -948,7 +961,7 @@ namespace cryptonote
LOG_ERROR("Failed to find tx pub key in blockblob");
return false;
}
- res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
+ res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
if(res.reserved_offset + req.reserve_size > block_blob.size())
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
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/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 88327dd75..99430bf20 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -49,7 +49,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 1
-#define CORE_RPC_VERSION_MINOR 13
+#define CORE_RPC_VERSION_MINOR 14
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -215,6 +215,7 @@ namespace cryptonote
std::string as_json;
bool in_pool;
uint64_t block_height;
+ uint64_t block_timestamp;
std::vector<uint64_t> output_indices;
BEGIN_KV_SERIALIZE_MAP()
@@ -223,6 +224,7 @@ namespace cryptonote
KV_SERIALIZE(as_json)
KV_SERIALIZE(in_pool)
KV_SERIALIZE(block_height)
+ KV_SERIALIZE(block_timestamp)
KV_SERIALIZE(output_indices)
END_KV_SERIALIZE_MAP()
};
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..e35389f9c
--- /dev/null
+++ b/src/serialization/json_object.cpp
@@ -0,0 +1,1167 @@
+// 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 <type_traits>
+#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::is_same<Type, char>() && std::is_same<Source, int>()) ||
+ std::numeric_limits<Source>::is_signed == std::numeric_limits<Type>::is_signed,
+ "comparisons below may have undefined behavior"
+ );
+ 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..481668299 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -290,7 +290,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
return true;
}
-bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+bool simple_wallet::print_seed(bool encrypted)
{
bool success = false;
std::string electrum_words;
@@ -311,7 +311,16 @@ bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<st
m_wallet->set_seed_language(mnemonic_language);
}
- success = m_wallet->get_seed(electrum_words);
+ std::string seed_pass;
+ if (encrypted)
+ {
+ auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
+ if (std::cin.eof() || !pwd_container)
+ return true;
+ seed_pass = pwd_container->password();
+ }
+
+ success = m_wallet->get_seed(electrum_words, seed_pass);
}
if (success)
@@ -325,6 +334,16 @@ bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<st
return true;
}
+bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ return print_seed(false);
+}
+
+bool simple_wallet::encrypted_seed(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ return print_seed(true);
+}
+
bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if (m_wallet->watch_only())
@@ -757,6 +776,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Display private spend key"));
m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display Electrum-style mnemonic seed"));
m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-ring-size <n> - set default ring size (default is 5); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-outputs-count [n] - try to keep at least that many outputs of value at least min-outputs-value; min-outputs-value [n] - try to keep at least min-outputs-count outputs of at least that value; merge-destinations <1|0> - whether to merge multiple payments to the same destination address; confirm-backlog <1|0> - whether to warn if there is transaction backlog"));
+ m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display encrypted Electrum-style mnemonic seed"));
m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>"));
m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>"));
@@ -768,6 +788,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"));
@@ -1026,6 +1047,13 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("Electrum-style word list failed verification");
return false;
}
+
+ auto pwd_container = tools::password_container::prompt(false, tr("Enter seed encryption passphrase, empty if none"));
+ if (std::cin.eof() || !pwd_container)
+ return false;
+ std::string seed_pass = pwd_container->password();
+ if (!seed_pass.empty())
+ m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
}
if (!m_generate_from_view_key.empty())
{
@@ -2329,7 +2357,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 +2934,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 +4535,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..3b29e3ca2 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -97,6 +97,7 @@ namespace cryptonote
bool viewkey(const std::vector<std::string> &args = std::vector<std::string>());
bool spendkey(const std::vector<std::string> &args = std::vector<std::string>());
bool seed(const std::vector<std::string> &args = std::vector<std::string>());
+ bool encrypted_seed(const std::vector<std::string> &args = std::vector<std::string>());
/*!
* \brief Sets seed language.
@@ -165,6 +166,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);
@@ -185,6 +187,7 @@ namespace cryptonote
bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs);
bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
std::string get_prompt() const;
+ bool print_seed(bool encrypted);
/*!
* \brief Prints the seed with a nice message
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 7afc1f449..9cd72b543 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -303,7 +303,7 @@ WalletImpl::~WalletImpl()
// Pause refresh thread - prevents refresh from starting again
pauseRefresh();
// Close wallet - stores cache and stops ongoing refresh operation
- close();
+ close(false); // do not store wallet as part of the closing activities
// Stop refresh thread
stopRefresh();
delete m_wallet2Callback;
@@ -566,19 +566,21 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed)
return m_status == Status_Ok;
}
-bool WalletImpl::close()
+bool WalletImpl::close(bool store)
{
bool result = false;
LOG_PRINT_L1("closing wallet...");
try {
- // Do not store wallet with invalid status
- // Status Critical refers to errors on opening or creating wallets.
- if (status() != Status_Critical)
- m_wallet->store();
- else
- LOG_ERROR("Status_Critical - not storing wallet");
- LOG_PRINT_L1("wallet::store done");
+ if (store) {
+ // Do not store wallet with invalid status
+ // Status Critical refers to errors on opening or creating wallets.
+ if (status() != Status_Critical)
+ m_wallet->store();
+ else
+ LOG_ERROR("Status_Critical - not storing wallet");
+ LOG_PRINT_L1("wallet::store done");
+ }
LOG_PRINT_L1("Calling wallet::stop...");
m_wallet->stop();
LOG_PRINT_L1("wallet::stop done");
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 8190c7873..36ffd4fc0 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -63,7 +63,7 @@ public:
const std::string &address_string,
const std::string &viewkey_string,
const std::string &spendkey_string = "");
- bool close();
+ bool close(bool store = true);
std::string seed() const;
std::string getSeedLanguage() const;
void setSeedLanguage(const std::string &arg);
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index a23533530..4b988a417 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -102,10 +102,10 @@ Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path,
return wallet;
}
-bool WalletManagerImpl::closeWallet(Wallet *wallet)
+bool WalletManagerImpl::closeWallet(Wallet *wallet, bool store)
{
WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
- bool result = wallet_->close();
+ bool result = wallet_->close(store);
if (!result) {
m_errorString = wallet_->errorString();
} else {
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index aa6ea439e..8455f0f16 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -48,7 +48,7 @@ public:
const std::string &addressString,
const std::string &viewKeyString,
const std::string &spendKeyString = "");
- virtual bool closeWallet(Wallet *wallet);
+ virtual bool closeWallet(Wallet *wallet, bool store = true);
bool walletExists(const std::string &path);
bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const;
std::vector<std::string> findWallets(const std::string &path);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 323a3a7fe..805703027 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
@@ -296,6 +289,13 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
return false;
}
restore_deterministic_wallet = true;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_passphrase, std::string, String, false, std::string());
+ if (field_seed_passphrase_found)
+ {
+ if (!field_seed_passphrase.empty())
+ recovery_key = cryptonote::decrypt_key(recovery_key, field_seed_passphrase);
+ }
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string());
@@ -527,7 +527,7 @@ bool wallet2::is_deterministic() const
return keys_deterministic;
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::get_seed(std::string& electrum_words) const
+bool wallet2::get_seed(std::string& electrum_words, const std::string &passphrase) const
{
bool keys_deterministic = is_deterministic();
if (!keys_deterministic)
@@ -541,7 +541,10 @@ bool wallet2::get_seed(std::string& electrum_words) const
return false;
}
- crypto::ElectrumWords::bytes_to_words(get_account().get_keys().m_spend_secret_key, electrum_words, seed_language);
+ crypto::secret_key key = get_account().get_keys().m_spend_secret_key;
+ if (!passphrase.empty())
+ key = cryptonote::encrypt_key(key, passphrase);
+ crypto::ElectrumWords::bytes_to_words(key, electrum_words, seed_language);
return true;
}
@@ -684,7 +687,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 +725,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 +732,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 +764,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 +1244,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 +1256,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)
{
@@ -1552,23 +1539,22 @@ void wallet2::update_pool_state(bool refreshed)
{
if (res.txs.size() == txids.size())
{
- size_t n = 0;
- for (const auto &txid: txids)
+ for (const auto &tx_entry: res.txs)
{
- // might have just been put in a block
- if (res.txs[n].in_pool)
+ if (tx_entry.in_pool)
{
cryptonote::transaction tx;
cryptonote::blobdata bd;
crypto::hash tx_hash, tx_prefix_hash;
- if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[n].as_hex, bd))
+ if (epee::string_tools::parse_hexstr_to_binbuff(tx_entry.as_hex, bd))
{
if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
{
- if (tx_hash == txid)
+ const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash);
+ if (i != txids.end())
{
- process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
- m_scanned_pool_txs[0].insert(txid);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ m_scanned_pool_txs[0].insert(tx_hash);
if (m_scanned_pool_txs[0].size() > 5000)
{
std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
@@ -1577,7 +1563,7 @@ void wallet2::update_pool_state(bool refreshed)
}
else
{
- LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool");
+ MERROR("Got txid " << tx_hash << " which we did not ask for");
}
}
else
@@ -1587,14 +1573,13 @@ void wallet2::update_pool_state(bool refreshed)
}
else
{
- LOG_PRINT_L0("Failed to parse tx " << txid);
+ LOG_PRINT_L0("Failed to parse transaction from daemon");
}
}
else
{
- LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more");
+ LOG_PRINT_L1("Transaction from daemon was in pool, but is no more");
}
- ++n;
}
}
else
@@ -1698,7 +1683,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 +1722,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 +1748,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 << ")...");
@@ -5404,6 +5389,9 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
}
spent = 0;
unspent = 0;
+ std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
+ std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
+ // was created by sweep_all, so we can't know the spent height and other detailed info.
for(size_t i = 0; i < m_transfers.size(); ++i)
{
transfer_details &td = m_transfers[i];
@@ -5414,8 +5402,146 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
unspent += amount;
LOG_PRINT_L2("Transfer " << i << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
<< (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
+
+ if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
+ {
+ bool is_spent_tx_found = false;
+ for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
+ {
+ bool is_spent_tx = false;
+ for(const cryptonote::txin_v& in : it->m_tx.vin)
+ {
+ if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
+ {
+ is_spent_tx = true;
+ break;
+ }
+ }
+ if (is_spent_tx)
+ {
+ is_spent_tx_found = true;
+ spent_txids.insert(it->m_txid);
+ break;
+ }
+ }
+
+ if (!is_spent_tx_found)
+ swept_transfers.push_back(i);
+ }
}
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
+
+ if (check_spent)
+ {
+ // query outgoing txes
+ COMMAND_RPC_GET_TRANSACTIONS::request gettxs_req;
+ COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res;
+ gettxs_req.decode_as_json = false;
+ for (const crypto::hash& spent_txid : spent_txids)
+ gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
+
+ // process each outgoing tx
+ auto spent_txid = spent_txids.begin();
+ for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
+ {
+ THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
+
+ // parse tx
+ cryptonote::blobdata bd;
+ THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(e.as_hex, bd), error::wallet_internal_error, "parse_hexstr_to_binbuff failed");
+ cryptonote::transaction spent_tx;
+ crypto::hash spnet_txid_parsed, spent_txid_prefix;
+ THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, spent_tx, spnet_txid_parsed, spent_txid_prefix), error::wallet_internal_error, "parse_and_validate_tx_from_blob failed");
+ THROW_WALLET_EXCEPTION_IF(*spent_txid != spnet_txid_parsed, error::wallet_internal_error, "parsed txid mismatch");
+
+ // get received (change) amount
+ uint64_t tx_money_got_in_outs = 0;
+ const cryptonote::account_keys& keys = m_account.get_keys();
+ const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
+ crypto::key_derivation derivation;
+ generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ size_t output_index = 0;
+ for (const cryptonote::tx_out& out : spent_tx.vout)
+ {
+ uint64_t money_transfered = 0;
+ bool error = false, received = false;
+ check_acc_out_precomp(keys.m_account_address.m_spend_public_key, out, derivation, output_index, received, money_transfered, error);
+ THROW_WALLET_EXCEPTION_IF(error, error::wallet_internal_error, "check_acc_out_precomp failed");
+ if (received)
+ {
+ if (money_transfered == 0)
+ {
+ rct::key mask;
+ money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, output_index, mask);
+ }
+ tx_money_got_in_outs += money_transfered;
+ }
+ ++output_index;
+ }
+
+ // get spent amount
+ uint64_t tx_money_spent_in_ins = 0;
+ for (const cryptonote::txin_v& in : spent_tx.vin)
+ {
+ if (in.type() != typeid(cryptonote::txin_to_key))
+ continue;
+ auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
+ if (it != m_key_images.end())
+ {
+ const transfer_details& td = m_transfers[it->second];
+ uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
+ if (amount > 0)
+ {
+ THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error,
+ std::string("Inconsistent amount in tx input: got ") + print_money(amount) +
+ std::string(", expected ") + print_money(td.amount()));
+ }
+ amount = td.amount();
+ tx_money_spent_in_ins += amount;
+
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << *spent_txid);
+ set_spent(it->second, e.block_height);
+ if (m_callback)
+ m_callback->on_money_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx);
+ }
+ }
+
+ // create outgoing payment
+ process_outgoing(*spent_txid, spent_tx, e.block_height, e.block_timestamp, tx_money_spent_in_ins, tx_money_got_in_outs);
+
+ // erase corresponding incoming payment
+ for (auto j = m_payments.begin(); j != m_payments.end(); ++j)
+ {
+ if (j->second.m_tx_hash == *spent_txid)
+ {
+ m_payments.erase(j);
+ break;
+ }
+ }
+
+ ++spent_txid;
+ }
+
+ for (size_t n : swept_transfers)
+ {
+ const transfer_details& td = m_transfers[n];
+ confirmed_transfer_details pd;
+ pd.m_change = (uint64_t)-1; // cahnge is unknown
+ pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
+ std::string err;
+ pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest
+ crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random
+ m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
+ }
+ }
+
return m_transfers[signed_key_images.size() - 1].m_block_height;
}
wallet2::payment_container wallet2::export_payments() const
@@ -5559,7 +5685,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret
crypto::secret_key_to_public_key(skey, pkey);
const crypto::signature &signature = *(const crypto::signature*)&ciphertext[ciphertext.size() - sizeof(crypto::signature)];
THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature),
- error::wallet_internal_error, "Failed to authenticate criphertext");
+ error::wallet_internal_error, "Failed to authenticate ciphertext");
}
crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]);
return plaintext;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index adf03abcc..971e98351 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -363,7 +363,7 @@ namespace tools
* \brief Checks if deterministic wallet
*/
bool is_deterministic() const;
- bool get_seed(std::string& electrum_words) const;
+ bool get_seed(std::string& electrum_words, const std::string &passphrase = std::string()) const;
/*!
* \brief Gets the seed language
*/
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 8da8c62eb..7a5e01af7 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -663,7 +663,7 @@ struct WalletManager
* \param wallet previously opened / created wallet instance
* \return None
*/
- virtual bool closeWallet(Wallet *wallet) = 0;
+ virtual bool closeWallet(Wallet *wallet, bool store = true) = 0;
/*
* ! checks if wallet with the given name already exists
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 34c5a2a5d..81dc7e549 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -114,6 +114,21 @@ namespace wallet_args
auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options);
po::store(parser.run(), vm);
+ if (command_line::get_arg(vm, command_line::arg_help))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
+ tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
+ "daemon to work correctly.") << ENDL;
+ tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage;
+ tools::msg_writer() << desc_all;
+ return false;
+ }
+ else if (command_line::get_arg(vm, command_line::arg_version))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ return false;
+ }
+
if(command_line::has_arg(vm, arg_config_file))
{
std::string config = command_line::get_arg(vm, arg_config_file);
@@ -147,21 +162,6 @@ namespace wallet_args
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
}
- if (command_line::get_arg(vm, command_line::arg_help))
- {
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
- tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n"
- "daemon to work correctly.") << ENDL;
- tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage;
- tools::msg_writer() << desc_all;
- return boost::none;
- }
- else if (command_line::get_arg(vm, command_line::arg_version))
- {
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
- return boost::none;
- }
-
if(command_line::has_arg(vm, arg_max_concurrency))
tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 773d12775..9368b8cb6 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -37,7 +37,6 @@ using namespace epee;
#include "wallet/wallet_args.h"
#include "common/command_line.h"
#include "common/i18n.h"
-#include "common/util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "wallet_rpc_server_commands_defs.h"
@@ -70,18 +69,12 @@ namespace tools
}
//------------------------------------------------------------------------------------------------------------------------------
- wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false)
+ wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_file(), m_stop(false), m_trusted_daemon(false)
{
}
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
- try
- {
- boost::system::error_code ec{};
- boost::filesystem::remove(rpc_login_filename, ec);
- }
- catch (...) {}
}
//------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::set_wallet(wallet2 *cr)
@@ -182,34 +175,32 @@ namespace tools
default_rpc_username,
string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size())
);
+
+ std::string temp = "monero-wallet-rpc." + bind_port + ".login";
+ rpc_login_file = tools::private_file::create(temp);
+ if (!rpc_login_file.handle())
+ {
+ LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
+ return false;
+ }
+ std::fputs(http_login->username.c_str(), rpc_login_file.handle());
+ std::fputc(':', rpc_login_file.handle());
+ std::fputs(http_login->password.c_str(), rpc_login_file.handle());
+ std::fflush(rpc_login_file.handle());
+ if (std::ferror(rpc_login_file.handle()))
+ {
+ LOG_ERROR(tr("Error writing to file ") << temp);
+ return false;
+ }
+ LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
}
- else
+ else // chosen user/pass
{
http_login.emplace(
std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()
);
}
assert(bool(http_login));
-
- std::string temp = "monero-wallet-rpc." + bind_port + ".login";
- const auto cookie = tools::create_private_file(temp);
- if (!cookie)
- {
- LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
- return false;
- }
- rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup
- temp = rpc_login_filename;
- std::fputs(http_login->username.c_str(), cookie.get());
- std::fputc(':', cookie.get());
- std::fputs(http_login->password.c_str(), cookie.get());
- std::fflush(cookie.get());
- if (std::ferror(cookie.get()))
- {
- LOG_ERROR(tr("Error writing to file ") << temp);
- return false;
- }
- LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
} // end auth enabled
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index dd54222b0..e5ed0a846 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -33,6 +33,7 @@
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <string>
+#include "common/util.h"
#include "net/http_server_impl_base.h"
#include "wallet_rpc_server_commands_defs.h"
#include "wallet2.h"
@@ -154,7 +155,7 @@ namespace tools
wallet2 *m_wallet;
std::string m_wallet_dir;
- std::string rpc_login_filename;
+ tools::private_file rpc_login_file;
std::atomic<bool> m_stop;
bool m_trusted_daemon;
epee::net_utils::http::http_simple_client m_http_client;
diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h
index c4fb462e3..85518612a 100644
--- a/tests/core_proxy/core_proxy.h
+++ b/tests/core_proxy/core_proxy.h
@@ -100,5 +100,6 @@ namespace tests
uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; }
uint8_t get_hard_fork_version(uint64_t height) const { return 0; }
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
+ bool fluffy_blocks_enabled() const { return false; }
};
}
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 2f62dc2aa..f5e08b102 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -55,7 +55,6 @@ set(unit_tests_sources
test_tx_utils.cpp
test_peerlist.cpp
test_protocol_pack.cpp
- thread_group.cpp
hardfork.cpp
unbound.cpp
uri.cpp
diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp
index bcffc85c9..82ff058b1 100644
--- a/tests/unit_tests/ban.cpp
+++ b/tests/unit_tests/ban.cpp
@@ -77,6 +77,7 @@ public:
uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; }
uint8_t get_hard_fork_version(uint64_t height) const { return 0; }
cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; }
+ bool fluffy_blocks_enabled() const { return false; }
};
typedef nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<test_core>> Server;
diff --git a/tests/unit_tests/thread_group.cpp b/tests/unit_tests/thread_group.cpp
deleted file mode 100644
index c0ba3d38b..000000000
--- a/tests/unit_tests/thread_group.cpp
+++ /dev/null
@@ -1,177 +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 "gtest/gtest.h"
-
-#include <atomic>
-#include "common/task_region.h"
-#include "common/thread_group.h"
-
-TEST(ThreadGroup, NoThreads)
-{
- tools::task_region(tools::thread_group(0), [] (tools::task_region_handle& region) {
- std::atomic<bool> completed{false};
- region.run([&] { completed = true; });
- EXPECT_TRUE(completed);
- });
- {
- tools::thread_group group(0);
- std::atomic<bool> completed{false};
- group.dispatch([&] { completed = true; });
- EXPECT_TRUE(completed);
- }
-}
-
-TEST(ThreadGroup, OneThread)
-{
- tools::thread_group group(1);
-
- for (unsigned i = 0; i < 3; ++i) {
- std::atomic<bool> completed{false};
- tools::task_region(group, [&] (tools::task_region_handle& region) {
- region.run([&] { completed = true; });
- });
- EXPECT_TRUE(completed);
- }
-}
-
-
-TEST(ThreadGroup, UseActiveThreadOnSync)
-{
- tools::thread_group group(1);
-
- for (unsigned i = 0; i < 3; ++i) {
- std::atomic<bool> completed{false};
- tools::task_region(group, [&] (tools::task_region_handle& region) {
- region.run([&] { while (!completed); });
- region.run([&] { completed = true; });
- });
- EXPECT_TRUE(completed);
- }
-}
-
-TEST(ThreadGroup, InOrder)
-{
- tools::thread_group group(1);
-
- for (unsigned i = 0; i < 3; ++i) {
- std::atomic<unsigned> count{0};
- std::atomic<bool> completed{false};
- tools::task_region(group, [&] (tools::task_region_handle& region) {
- region.run([&] { while (!completed); });
- region.run([&] { if (count == 0) completed = true; });
- region.run([&] { ++count; });
- });
- EXPECT_TRUE(completed);
- EXPECT_EQ(1u, count);
- }
-}
-
-TEST(ThreadGroup, TwoThreads)
-{
- tools::thread_group group(2);
-
- for (unsigned i = 0; i < 3; ++i) {
- std::atomic<bool> completed{false};
- tools::task_region(group, [&] (tools::task_region_handle& region) {
- region.run([&] { while (!completed); });
- region.run([&] { while (!completed); });
- region.run([&] { completed = true; });
- });
- EXPECT_TRUE(completed);
- }
-}
-
-TEST(ThreadGroup, Nested) {
- struct fib {
- unsigned operator()(tools::thread_group& group, unsigned value) const {
- if (value == 0 || value == 1) {
- return value;
- }
- unsigned left = 0;
- unsigned right = 0;
- tools::task_region(group, [&, value] (tools::task_region_handle& region) {
- region.run([&, value] { left = fib{}(group, value - 1); });
- region.run([&, value] { right = fib{}(group, value - 2); } );
- });
- return left + right;
- }
-
- unsigned operator()(tools::thread_group&& group, unsigned value) const {
- return (*this)(group, value);
- }
- };
- // be careful of depth on asynchronous version
- EXPECT_EQ(6765, fib{}(tools::thread_group(0), 20));
- EXPECT_EQ(377, fib{}(tools::thread_group(1), 14));
-}
-
-TEST(ThreadGroup, Many)
-{
- tools::thread_group group(1);
-
- for (unsigned i = 0; i < 3; ++i) {
- std::atomic<unsigned> count{0};
- tools::task_region(group, [&] (tools::task_region_handle& region) {
- for (unsigned tasks = 0; tasks < 1000; ++tasks) {
- region.run([&] { ++count; });
- }
- });
- EXPECT_EQ(1000u, count);
- }
-}
-
-TEST(ThreadGroup, ThrowInTaskRegion)
-{
- class test_exception final : std::exception {
- public:
- explicit test_exception() : std::exception() {}
-
- virtual const char* what() const noexcept override {
- return "test_exception";
- }
- };
-
- tools::thread_group group(1);
-
- for (unsigned i = 0; i < 3; ++i) {
- std::atomic<unsigned> count{0};
- EXPECT_THROW(
- [&] {
- tools::task_region(group, [&] (tools::task_region_handle& region) {
- for (unsigned tasks = 0; tasks < 1000; ++tasks) {
- region.run([&] { ++count; });
- }
- throw test_exception();
- });
- }(),
- test_exception
- );
- EXPECT_GE(1000u, count);
- }
-}