aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt47
-rw-r--r--Doxyfile14
-rw-r--r--README.md8
-rw-r--r--contrib/epee/include/console_handler.h16
-rw-r--r--contrib/epee/include/misc_os_dependent.h8
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h6
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl33
-rw-r--r--contrib/epee/include/net/levin_protocol_handler_async.h1
-rw-r--r--contrib/epee/include/storages/portable_storage_template_helper.h4
-rw-r--r--contrib/epee/include/syncobj.h12
-rw-r--r--contrib/otshell_utils/lib_common1.hpp9
-rw-r--r--contrib/otshell_utils/utils.cpp10
-rw-r--r--contrib/otshell_utils/utils.hpp12
-rw-r--r--external/CMakeLists.txt3
-rw-r--r--external/miniupnpc/CMakeLists.txt4
-rw-r--r--external/miniupnpc/Changelog.txt10
-rw-r--r--external/miniupnpc/Makefile4
-rw-r--r--external/miniupnpc/Makefile.mingw6
-rw-r--r--external/miniupnpc/apiversions.txt7
-rw-r--r--external/miniupnpc/minissdpc.c25
-rw-r--r--external/miniupnpc/miniupnpc.c73
-rw-r--r--external/miniupnpc/miniupnpc.h6
-rw-r--r--external/miniupnpc/miniwget.c65
-rw-r--r--external/miniupnpc/miniwget.h10
-rw-r--r--external/miniupnpc/testdesc/new_LiveBox_desc.xml2
-rw-r--r--external/miniupnpc/testminiwget.c12
-rw-r--r--external/miniupnpc/upnpc.c18
-rw-r--r--external/miniupnpc/upnpcommands.c4
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/blockchain_db/CMakeLists.txt1
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp8
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h17
-rw-r--r--src/blockchain_db/blockchain_db.h1092
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp63
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h10
-rw-r--r--src/blockchain_utilities/blockchain_utilities.h9
-rw-r--r--src/blocks/checkpoints.datbin26560037 -> 32000004 bytes
-rw-r--r--src/common/json_util.h53
-rw-r--r--src/common/util.h4
-rw-r--r--src/connectivity_tool/conn_tool.cpp377
-rw-r--r--src/crypto/crypto.cpp13
-rw-r--r--src/crypto/crypto.h7
-rw-r--r--src/crypto/crypto_ops_builder/README.md21
-rw-r--r--src/crypto/crypto_ops_builder/ref10CommentedCombined/description4
-rw-r--r--src/crypto/crypto_ops_builder/ref10CommentedCombined/designers4
-rw-r--r--src/cryptonote_core/CMakeLists.txt2
-rw-r--r--src/cryptonote_core/blockchain.cpp94
-rw-r--r--src/cryptonote_core/blockchain.h911
-rw-r--r--src/cryptonote_core/blockchain_storage.cpp8
-rw-r--r--src/cryptonote_core/checkpoints.cpp251
-rw-r--r--src/cryptonote_core/checkpoints.h181
-rw-r--r--src/cryptonote_core/checkpoints_create.cpp280
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp20
-rw-r--r--src/cryptonote_core/cryptonote_core.h650
-rw-r--r--src/cryptonote_core/cryptonote_format_utils.cpp21
-rw-r--r--src/cryptonote_core/difficulty.cpp6
-rw-r--r--src/cryptonote_core/difficulty.h12
-rw-r--r--src/cryptonote_core/tx_pool.cpp50
-rw-r--r--src/cryptonote_core/tx_pool.h416
-rw-r--r--src/cryptonote_core/verification_context.h7
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl2
-rw-r--r--src/daemon/command_parser_executor.cpp18
-rw-r--r--src/daemon/command_parser_executor.h2
-rw-r--r--src/daemon/command_server.cpp5
-rw-r--r--src/daemon/core.h1
-rw-r--r--src/daemon/rpc_command_executor.cpp40
-rw-r--r--src/daemon/rpc_command_executor.h2
-rw-r--r--src/daemonizer/windows_service.cpp4
-rw-r--r--src/daemonizer/windows_service_runner.h2
-rw-r--r--src/miner/CMakeLists.txt1
-rw-r--r--src/p2p/data_logger.cpp18
-rw-r--r--src/p2p/data_logger.hpp9
-rw-r--r--src/p2p/net_node.h2
-rw-r--r--src/p2p/net_node.inl2
-rw-r--r--src/p2p/net_node_common.h4
-rw-r--r--src/p2p/network_throttle-detail.cpp2
-rw-r--r--src/p2p/network_throttle.cpp18
-rw-r--r--src/p2p/network_throttle.hpp12
-rw-r--r--src/rpc/core_rpc_server.cpp87
-rw-r--r--src/rpc/core_rpc_server.h2
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h61
-rw-r--r--src/simplewallet/simplewallet.cpp398
-rw-r--r--src/simplewallet/simplewallet.h10
-rw-r--r--src/version.h.in2
-rw-r--r--src/wallet/CMakeLists.txt11
-rw-r--r--src/wallet/wallet2.cpp298
-rw-r--r--src/wallet/wallet2.h35
-rw-r--r--src/wallet/wallet2_api.cpp346
-rw-r--r--src/wallet/wallet2_api.h124
-rw-r--r--src/wallet/wallet_errors.h20
-rw-r--r--src/wallet/wallet_rpc_server.cpp18
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h2
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/core_tests/chaingen.h2
-rw-r--r--tests/crypto/CMakeLists.txt3
-rw-r--r--tests/libwallet_api_tests/CMakeLists.txt (renamed from src/connectivity_tool/CMakeLists.txt)42
-rw-r--r--tests/libwallet_api_tests/main.cpp212
-rw-r--r--tests/unit_tests/CMakeLists.txt4
-rw-r--r--tests/unit_tests/hardfork.cpp1
-rw-r--r--tests/unit_tests/unbound.cpp (renamed from src/cryptonote_core/checkpoints_create.h)31
-rw-r--r--utils/gpg_keys/aabramov.asc31
-rw-r--r--utils/gpg_keys/hyc.asc44
-rw-r--r--utils/munin_plugins/alt_blocks_count2
-rw-r--r--utils/munin_plugins/difficulty2
-rw-r--r--utils/munin_plugins/grey_peerlist_size2
-rw-r--r--utils/munin_plugins/height2
-rw-r--r--utils/munin_plugins/incoming_connections_count2
-rw-r--r--utils/munin_plugins/outgoing_connections_count2
-rw-r--r--utils/munin_plugins/tx_count2
-rw-r--r--utils/munin_plugins/tx_pool_size2
-rw-r--r--utils/munin_plugins/white_peerlist_size2
112 files changed, 5497 insertions, 1474 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3e95f48ff..9674404bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -206,41 +206,32 @@ elseif()
endif()
if (DATABASE STREQUAL "lmdb")
+ message(STATUS "Using LMDB as default DB type")
set(BLOCKCHAIN_DB DB_LMDB)
-
- # temporarily allow mingw to compile with berkeley_db,
- # regardless if building static or not
- if(NOT STATIC OR MINGW)
- find_package(BerkeleyDB)
-
- if(BERKELEY_DB_OVERRIDE AND NOT BERKELEY_DB)
- message(STATUS "BERKELEY_DB setting has been overridden and disabled")
- elseif(NOT BERKELEY_DB_LIBRARIES)
- message(STATUS "BerkeleyDB not found and has been disabled.")
+ add_definitions("-DDEFAULT_DB_TYPE=\"lmdb\"")
+elseif (DATABASE STREQUAL "berkeleydb")
+ find_package(BerkeleyDB)
+ if(NOT BERKELEY_DB)
+ die("Found BerkeleyDB includes, but could not find BerkeleyDB library. Please make sure you have installed libdb and libdb-dev / libdb++-dev or the equivalent.")
+ else()
+ message(STATUS "Found BerkeleyDB include (db.h) in ${BERKELEY_DB_INCLUDE_DIR}")
+ if(BERKELEY_DB_LIBRARIES)
+ message(STATUS "Found BerkeleyDB shared library")
+ set(BDB_STATIC false CACHE BOOL "BDB Static flag")
+ set(BDB_INCLUDE ${BERKELEY_DB_INCLUDE_DIR} CACHE STRING "BDB include path")
+ set(BDB_LIBRARY ${BERKELEY_DB_LIBRARIES} CACHE STRING "BDB library name")
+ set(BDB_LIBRARY_DIRS "" CACHE STRING "BDB Library dirs")
+ set(BERKELEY_DB 1)
else()
- message(STATUS "Found BerkeleyDB include (db.h) in ${BERKELEY_DB_INCLUDE_DIR}")
- if(BERKELEY_DB_LIBRARIES)
- message(STATUS "Found BerkeleyDB shared library")
- set(BDB_STATIC false CACHE BOOL "BDB Static flag")
- set(BDB_INCLUDE ${BERKELEY_DB_INCLUDE_DIR} CACHE STRING "BDB include path")
- set(BDB_LIBRARY ${BERKELEY_DB_LIBRARIES} CACHE STRING "BDB library name")
- set(BDB_LIBRARY_DIRS "" CACHE STRING "BDB Library dirs")
- set(BERKELEY_DB 1)
- else()
- message(STATUS "Found BerkeleyDB includes, but could not find BerkeleyDB library. Please make sure you have installed libdb and libdb-dev or the equivalent")
- endif()
+ die("Found BerkeleyDB includes, but could not find BerkeleyDB library. Please make sure you have installed libdb and libdb-dev / libdb++-dev or the equivalent.")
endif()
endif()
- if (BERKELEY_DB AND (ARCH_WIDTH STREQUAL "32" OR ARM6 OR ARM7))
- message(STATUS "Using Berkeley DB as default DB type")
- add_definitions("-DDEFAULT_DB_TYPE=\"berkeley\"")
- else()
- message(STATUS "Using LMDB as default DB type")
- add_definitions("-DDEFAULT_DB_TYPE=\"lmdb\"")
- endif()
+ message(STATUS "Using Berkeley DB as default DB type")
+ add_definitions("-DDEFAULT_DB_TYPE=\"berkeley\"")
elseif (DATABASE STREQUAL "memory")
set(BLOCKCHAIN_DB DB_MEMORY)
+ message(STATUS "Using Serialised In Memory as default DB type")
add_definitions("-DDEFAULT_DB_TYPE=\"memory\"")
else()
die("Invalid database type: ${DATABASE}")
diff --git a/Doxyfile b/Doxyfile
index a70ef812b..93a5c6e76 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -409,13 +409,13 @@ LOOKUP_CACHE_SIZE = 0
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
-EXTRACT_ALL = NO
+EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
# be included in the documentation.
# The default value is: NO.
-EXTRACT_PRIVATE = NO
+EXTRACT_PRIVATE = YES
# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
# scope will be included in the documentation.
@@ -427,7 +427,7 @@ EXTRACT_PACKAGE = NO
# included in the documentation.
# The default value is: NO.
-EXTRACT_STATIC = NO
+EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO
@@ -452,7 +452,7 @@ EXTRACT_LOCAL_METHODS = NO
# are hidden.
# The default value is: NO.
-EXTRACT_ANON_NSPACES = NO
+EXTRACT_ANON_NSPACES = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
@@ -1902,7 +1902,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-MACRO_EXPANSION = NO
+MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -1910,7 +1910,7 @@ MACRO_EXPANSION = NO
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-EXPAND_ONLY_PREDEF = NO
+EXPAND_ONLY_PREDEF = YES
# If the SEARCH_INCLUDES tag is set to YES the includes files in the
# INCLUDE_PATH will be searched if a #include is found.
@@ -1942,7 +1942,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED =
+PREDEFINED = "BLOCKCHAIN_DB=2" \ # DB_LMDB
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
diff --git a/README.md b/README.md
index bd4b66878..4fa21d650 100644
--- a/README.md
+++ b/README.md
@@ -35,9 +35,9 @@ Monero development can be supported directly through donations.
Both Monero and Bitcoin donations can be made to donate.getmonero.org if using a client that supports the [OpenAlias](https://openalias.org) standard
-The Monero donation address is: 46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em (viewkey: e422831985c9205238ef84daf6805526c14d96fd7b059fe68c7ab98e495e5703)
+The Monero donation address is: 44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A (viewkey: f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501)
-The Bitcoin donation address is: 1FhnVJi2V1k4MqXm2nHoEbY5LV7FPai7bb
+The Bitcoin donation address is: 1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H
Core development funding and/or some supporting services are also graciously provided by sponsors:
@@ -193,9 +193,9 @@ See README.i18n
## Using Tor
-While Monero isn't made to integrate with Tor, it can be used wrapped with torsocks, if you add --p2p-bind-ip 127.0.0.1 to the bitmonerod command line. You also want to set DNS requests to go over TCP, so they'll be routed through Tor, by setting DNS_PUBLIC=tcp. You may also disable IGD (UPnP port forwarding negotiation), which is pointless with Tor. Example:
+While Monero isn't made to integrate with Tor, it can be used wrapped with torsocks, if you add --p2p-bind-ip 127.0.0.1 to the bitmonerod command line. You also want to set DNS requests to go over TCP, so they'll be routed through Tor, by setting DNS_PUBLIC=tcp. You may also disable IGD (UPnP port forwarding negotiation), which is pointless with Tor. To allow local connections from the wallet, add TORSOCKS_ALLOW_INBOUND=1. Example:
-DNS_PUBLIC=tcp torsocks bitmonerod --p2p-bind-ip 127.0.0.1 --no-igd
+DNS_PUBLIC=tcp TORSOCKS_ALLOW_INBOUND=1 torsocks bitmonerod --p2p-bind-ip 127.0.0.1 --no-igd
## Using readline
diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h
index d76d7930c..95b986ff5 100644
--- a/contrib/epee/include/console_handler.h
+++ b/contrib/epee/include/console_handler.h
@@ -65,7 +65,7 @@ namespace epee
if (state_eos == m_read_status)
return false;
- std::unique_lock<std::mutex> lock(m_response_mutex);
+ boost::unique_lock<boost::mutex> lock(m_response_mutex);
while (state_init == m_read_status)
{
m_response_cv.wait(lock);
@@ -104,7 +104,7 @@ namespace epee
private:
bool start_read()
{
- std::unique_lock<std::mutex> lock(m_request_mutex);
+ boost::unique_lock<boost::mutex> lock(m_request_mutex);
if (!m_run.load(std::memory_order_relaxed) || m_has_read_request)
return false;
@@ -115,7 +115,7 @@ namespace epee
bool wait_read()
{
- std::unique_lock<std::mutex> lock(m_request_mutex);
+ boost::unique_lock<boost::mutex> lock(m_request_mutex);
while (m_run.load(std::memory_order_relaxed) && !m_has_read_request)
{
m_request_cv.wait(lock);
@@ -188,7 +188,7 @@ namespace epee
}
else
{
- std::unique_lock<std::mutex> lock(m_response_mutex);
+ boost::unique_lock<boost::mutex> lock(m_response_mutex);
if (m_run.load(std::memory_order_relaxed))
{
m_line = std::move(line);
@@ -220,10 +220,10 @@ namespace epee
bool m_has_read_request;
t_state m_read_status;
- std::mutex m_request_mutex;
- std::mutex m_response_mutex;
- std::condition_variable m_request_cv;
- std::condition_variable m_response_cv;
+ boost::mutex m_request_mutex;
+ boost::mutex m_response_mutex;
+ boost::condition_variable m_request_cv;
+ boost::condition_variable m_response_cv;
};
diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h
index 4d9c991e4..2abca0446 100644
--- a/contrib/epee/include/misc_os_dependent.h
+++ b/contrib/epee/include/misc_os_dependent.h
@@ -52,6 +52,12 @@ namespace misc_utils
{
#if defined(_MSC_VER)
return ::GetTickCount64();
+#elif defined(WIN32)
+# if defined(WIN64)
+ return GetTickCount64();
+# else
+ return GetTickCount();
+# endif
#elif defined(__MACH__)
clock_serv_t cclock;
mach_timespec_t mts;
@@ -98,7 +104,7 @@ namespace misc_utils
inline std::string get_thread_string_id()
{
-#if defined(_MSC_VER)
+#if defined(_WIN32)
return boost::lexical_cast<std::string>(GetCurrentThreadId());
#elif defined(__GNUC__)
return boost::lexical_cast<std::string>(pthread_self());
diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h
index f3dd7291c..80f3f6db0 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.h
+++ b/contrib/epee/include/net/abstract_tcp_server2.h
@@ -153,8 +153,8 @@ namespace net_utils
// for calculate speed (last 60 sec)
network_throttle m_throttle_speed_in;
network_throttle m_throttle_speed_out;
- std::mutex m_throttle_speed_in_mutex;
- std::mutex m_throttle_speed_out_mutex;
+ boost::mutex m_throttle_speed_in_mutex;
+ boost::mutex m_throttle_speed_out_mutex;
public:
void setRpcStation();
@@ -307,7 +307,7 @@ namespace net_utils
/// The next connection to be accepted
connection_ptr new_connection_;
- std::mutex connections_mutex;
+ boost::mutex connections_mutex;
std::deque<std::pair<boost::system_time, 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 4fb6fa75d..b3d4e5fdb 100644
--- a/contrib/epee/include/net/abstract_tcp_server2.inl
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -41,7 +41,7 @@
#include <boost/utility/value_init.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> // TODO
-#include <boost/thread/thread.hpp> // TODO
+#include <boost/thread/v2/thread.hpp> // TODO
#include "misc_language.h"
#include "pragma_comp_defs.h"
@@ -252,11 +252,24 @@ PRAGMA_WARNING_DISABLE_VS(4355)
template<class t_protocol_handler>
void connection<t_protocol_handler>::save_dbg_log()
{
+ std::string address, port;
+ boost::system::error_code e;
+
+ boost::asio::ip::tcp::endpoint endpoint = socket_.remote_endpoint(e);
+ if (e)
+ {
+ address = "<not connected>";
+ port = "<not connected>";
+ }
+ else
+ {
+ address = endpoint.address().to_string();
+ port = boost::lexical_cast<std::string>(endpoint.port());
+ }
_mark_c("net/kind" ,
- " connection type " << to_string( m_connection_type ) << " "
- << socket_.local_endpoint().address().to_string() << ":" << socket_.local_endpoint().port()
- << " <--> " << socket_.remote_endpoint().address().to_string() << ":" << socket_.remote_endpoint().port()
- );
+ " connection type " << to_string( m_connection_type ) << " "
+ << socket_.local_endpoint().address().to_string() << ":" << socket_.local_endpoint().port()
+ << " <--> " << address << ":" << port);
}
//---------------------------------------------------------------------------------
template<class t_protocol_handler>
@@ -294,7 +307,7 @@ PRAGMA_WARNING_DISABLE_VS(4355)
if (delay > 0) {
long int ms = (long int)(delay * 100);
epee::net_utils::data_logger::get_instance().add_data("sleep_down", ms);
- std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+ boost::this_thread::sleep_for(boost::chrono::milliseconds(ms));
}
} while(delay > 0);
} // any form of sleeping
@@ -1005,6 +1018,12 @@ POP_WARNINGS
while(local_shared_context->ec == boost::asio::error::would_block)
{
bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout));
+ if (m_stop_signal_sent)
+ {
+ if (sock_.is_open())
+ sock_.close();
+ return false;
+ }
if(local_shared_context->ec == boost::asio::error::would_block && !r)
{
//timeout
@@ -1018,6 +1037,8 @@ POP_WARNINGS
if (ec || !sock_.is_open())
{
_dbg3("Some problems at connect, message: " << ec.message());
+ if (sock_.is_open())
+ sock_.close();
return false;
}
diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h
index 7331faa35..be231a7e1 100644
--- a/contrib/epee/include/net/levin_protocol_handler_async.h
+++ b/contrib/epee/include/net/levin_protocol_handler_async.h
@@ -695,6 +695,7 @@ void async_protocol_handler_config<t_connection_context>::del_out_connections(si
{
close(*out_connections.begin());
del_connection(m_connects.at(*out_connections.begin()));
+ out_connections.erase(out_connections.begin());
--count;
}
diff --git a/contrib/epee/include/storages/portable_storage_template_helper.h b/contrib/epee/include/storages/portable_storage_template_helper.h
index 88515b5c3..bbd8413fc 100644
--- a/contrib/epee/include/storages/portable_storage_template_helper.h
+++ b/contrib/epee/include/storages/portable_storage_template_helper.h
@@ -72,7 +72,7 @@ namespace epee
{
std::string json_buff;
store_t_to_json(str_in, json_buff, indent, insert_newlines);
- return std::move(json_buff);
+ return json_buff;
}
//-----------------------------------------------------------------------------------------------------------
template<class t_struct>
@@ -117,7 +117,7 @@ namespace epee
{
std::string binary_buff;
store_t_to_binary(str_in, binary_buff, indent);
- return std::move(binary_buff);
+ return binary_buff;
}
}
}
diff --git a/contrib/epee/include/syncobj.h b/contrib/epee/include/syncobj.h
index 0833b4114..8912fc018 100644
--- a/contrib/epee/include/syncobj.h
+++ b/contrib/epee/include/syncobj.h
@@ -30,11 +30,11 @@
#ifndef __WINH_OBJ_H__
#define __WINH_OBJ_H__
+#include <boost/chrono/duration.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
-#include <thread>
-#include <chrono>
+#include <boost/thread/v2/thread.hpp>
namespace epee
{
@@ -224,10 +224,10 @@ namespace epee
#define SHARED_CRITICAL_REGION_BEGIN(x) { shared_guard critical_region_var(x)
#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { exclusive_guard critical_region_var(x)
-#define CRITICAL_REGION_LOCAL(x) {std::this_thread::sleep_for(std::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var(x)
-#define CRITICAL_REGION_BEGIN(x) { std::this_thread::sleep_for(std::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var(x)
-#define CRITICAL_REGION_LOCAL1(x) {std::this_thread::sleep_for(std::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var1(x)
-#define CRITICAL_REGION_BEGIN1(x) { std::this_thread::sleep_for(std::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var1(x)
+#define CRITICAL_REGION_LOCAL(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var(x)
+#define CRITICAL_REGION_BEGIN(x) { boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var(x)
+#define CRITICAL_REGION_LOCAL1(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var1(x)
+#define CRITICAL_REGION_BEGIN1(x) { boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var1(x)
#define CRITICAL_REGION_END() }
diff --git a/contrib/otshell_utils/lib_common1.hpp b/contrib/otshell_utils/lib_common1.hpp
index 456e63fbb..2cb466beb 100644
--- a/contrib/otshell_utils/lib_common1.hpp
+++ b/contrib/otshell_utils/lib_common1.hpp
@@ -19,9 +19,11 @@
#include <functional>
#include <memory>
-#include <thread>
#include <atomic>
-#include <mutex>
+#include <boost/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/recursive_mutex.hpp>
+#include <boost/thread/lock_guard.hpp>
// list of thigs from libraries that we pull into namespace nOT::nNewcli
@@ -45,8 +47,7 @@
using std::shared_ptr; \
using std::weak_ptr; \
using std::enable_shared_from_this; \
- using std::mutex; \
- using std::lock_guard; \
+ using boost::lock_guard; \
#endif
diff --git a/contrib/otshell_utils/utils.cpp b/contrib/otshell_utils/utils.cpp
index e1d7d9a14..ef9b37f03 100644
--- a/contrib/otshell_utils/utils.cpp
+++ b/contrib/otshell_utils/utils.cpp
@@ -12,6 +12,7 @@
#include <fstream>
#include <iostream>
#include <iomanip>
+#include <chrono>
#include "utils.hpp"
@@ -32,6 +33,7 @@
#if defined(OS_TYPE_WINDOWS)
#include <windows.h>
+ #include <process.h>
#elif defined(OS_TYPE_POSIX)
#include <sys/types.h>
#include <sys/stat.h>
@@ -128,7 +130,7 @@ std::string get_current_time() {
cNullstream g_nullstream; // extern a stream that does nothing (eats/discards data)
-std::recursive_mutex gLoggerGuard; // extern
+boost::recursive_mutex gLoggerGuard; // extern
std::atomic<int> gLoggerGuardDepth; // extern
std::atomic<int> & gLoggerGuardDepth_Get() {
@@ -302,7 +304,7 @@ mPid2Number_Biggest(0)
mIsBroken=false; // ok, constr. succeeded, so string is not broken now
// this is here, because it could be using logging itself to log creation of first thread/PID etc
- Thread2Number( std::this_thread::get_id() ); // convert current id to short number, useful to reserve a number so that main thread is usually called 1
+ Thread2Number( boost::this_thread::get_id() ); // convert current id to short number, useful to reserve a number so that main thread is usually called 1
Pid2Number( getpid() ); // add this proces ID as first one
}
@@ -347,7 +349,7 @@ std::ostream & cLogger::write_stream(int level, const std::string & channel ) {
output << windows_stream(level);
#endif
output << icon(level) << ' ';
- std::thread::id this_id = std::this_thread::get_id();
+ boost::thread::id this_id = boost::this_thread::get_id();
output << "{" << Thread2Number(this_id) << "}";
auto nicePid = Pid2Number(getpid());
if (nicePid>0) output << " {p" << nicePid << "}";
@@ -516,7 +518,7 @@ std::string cLogger::endline() const {
#endif
}
-int cLogger::Thread2Number(const std::thread::id id) {
+int cLogger::Thread2Number(const boost::thread::id id) {
auto found = mThread2Number.find( id );
if (found == mThread2Number.end()) { // new one
mThread2Number_Biggest++;
diff --git a/contrib/otshell_utils/utils.hpp b/contrib/otshell_utils/utils.hpp
index 297f13ac0..98508b565 100644
--- a/contrib/otshell_utils/utils.hpp
+++ b/contrib/otshell_utils/utils.hpp
@@ -29,7 +29,7 @@
// #define opt_debug_debug
#ifdef opt_debug_debug
- #define _dbg_dbg(X) do { std::cerr<<"_dbg_dbg: " << OT_CODE_STAMP << " {thread=" << std::this_thread::get_id()<<"} " \
+ #define _dbg_dbg(X) do { std::cerr<<"_dbg_dbg: " << OT_CODE_STAMP << " {thread=" << boost::this_thread::get_id()<<"} " \
<< " {pid="<<getpid()<<"} " << ": " << X << std::endl; } while(0)
#else
#define _dbg_dbg(X) do { } while(0)
@@ -79,7 +79,7 @@ extern cNullstream g_nullstream; // a stream that does nothing (eats/discards da
// TODO make _dbg_ignore thread-safe everywhere
-extern std::recursive_mutex gLoggerGuard; // the mutex guarding logging/debugging code e.g. protecting streams, files, etc
+extern boost::recursive_mutex gLoggerGuard; // the mutex guarding logging/debugging code e.g. protecting streams, files, etc
std::atomic<int> & gLoggerGuardDepth_Get(); // getter for the global singleton of counter (it guarantees initializing it to 0). This counter shows the current recursion (re-entrant) level of debug macros.
@@ -91,7 +91,7 @@ std::atomic<int> & gLoggerGuardDepth_Get(); // getter for the global singleton o
_dbg_dbg("WRITE DEBUG: LEVEL="<<LEVEL<<" VAR: " << VAR ); \
auto level=LEVEL; short int part=0; \
try { \
- std::lock_guard<std::recursive_mutex> mutex_guard( nOT::nUtils::gLoggerGuard ); \
+ boost::lock_guard<boost::recursive_mutex> mutex_guard( nOT::nUtils::gLoggerGuard ); \
part=1; \
try { \
++nOT::nUtils::gLoggerGuardDepth_Get(); \
@@ -111,7 +111,7 @@ std::atomic<int> & gLoggerGuardDepth_Get(); // getter for the global singleton o
_dbg_dbg("WRITE DEBUG: LEVEL="<<LEVEL<<" CHANNEL="<<CHANNEL<<" VAR: " << VAR ); \
auto level=LEVEL; short int part=0; \
try { \
- std::lock_guard<std::recursive_mutex> mutex_guard( nOT::nUtils::gLoggerGuard ); \
+ boost::lock_guard<boost::recursive_mutex> mutex_guard( nOT::nUtils::gLoggerGuard ); \
part=1; \
try { \
++nOT::nUtils::gLoggerGuardDepth_Get(); \
@@ -253,9 +253,9 @@ class cLogger {
void OpenNewChannel_(const std::string & channel); ///< internal function, will throw in case of problems
std::string GetLogBaseDir() const;
- std::map< std::thread::id , int > mThread2Number; ///< change long thread IDs into a short nice number to show
+ std::map< boost::thread::id , int > mThread2Number; ///< change long thread IDs into a short nice number to show
int mThread2Number_Biggest; ///< current biggest value held there (biggest key) - works as growing-only counter basically
- int Thread2Number(const std::thread::id id); ///< convert the system's thread id into a nice short our id; make one if new thread
+ int Thread2Number(const boost::thread::id id); ///< convert the system's thread id into a nice short our id; make one if new thread
std::map< t_anypid , int > mPid2Number; ///< change long proces PID into a short nice number to show
int mPid2Number_Biggest; ///< current biggest value held there (biggest key) - works as growing-only counter basically
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 33e843e63..f0e65ac06 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -37,9 +37,6 @@
find_package(Miniupnpc QUIET)
-# FreeBSD doesn't play well with the local copy, so default to using shared
-set(USE_SHARED_MINIUPNPC false)
-
# If we have the correct shared version and we're not building static, use it
if(STATIC)
set(USE_SHARED_MINIUPNPC false)
diff --git a/external/miniupnpc/CMakeLists.txt b/external/miniupnpc/CMakeLists.txt
index 1e7645a9d..fd75f9ff5 100644
--- a/external/miniupnpc/CMakeLists.txt
+++ b/external/miniupnpc/CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.6)
project (miniupnpc C)
set (MINIUPNPC_VERSION 1.9)
-set (MINIUPNPC_API_VERSION 15)
+set (MINIUPNPC_API_VERSION 16)
# - we comment out this block as we don't support these other build types
if(0)
@@ -33,7 +33,7 @@ endif (NO_GETADDRINFO)
if (NOT WIN32)
add_definitions (-DMINIUPNPC_SET_SOCKET_TIMEOUT)
- add_definitions (-D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112L)
+ add_definitions (-D_BSD_SOURCE -D_DEFAULT_SOURCE)
else (NOT WIN32)
add_definitions (-D_WIN32_WINNT=0x0501) # XP or higher for getnameinfo and friends
endif (NOT WIN32)
diff --git a/external/miniupnpc/Changelog.txt b/external/miniupnpc/Changelog.txt
index bef61f59e..bf6343bd2 100644
--- a/external/miniupnpc/Changelog.txt
+++ b/external/miniupnpc/Changelog.txt
@@ -1,6 +1,14 @@
-$Id: Changelog.txt,v 1.219 2015/10/26 17:05:06 nanard Exp $
+$Id: Changelog.txt,v 1.222 2016/01/24 17:24:35 nanard Exp $
miniUPnP client Changelog.
+2016/01/24:
+ change miniwget to return HTTP status code
+ increments API_VERSION to 16
+
+2016/01/22:
+ Improve UPNPIGD_IsConnected() to check if WAN address is not private.
+ parse HTTP response status line in miniwget.c
+
2015/10/26:
snprintf() overflow check. check overflow in simpleUPnPcommand2()
diff --git a/external/miniupnpc/Makefile b/external/miniupnpc/Makefile
index 21845f8ae..99a69301c 100644
--- a/external/miniupnpc/Makefile
+++ b/external/miniupnpc/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.126 2015/08/28 12:14:18 nanard Exp $
+# $Id: Makefile,v 1.133 2016/01/24 17:24:35 nanard Exp $
# MiniUPnP Project
# http://miniupnp.free.fr/
# http://miniupnp.tuxfamily.org/
@@ -66,7 +66,7 @@ ifeq (SunOS, $(OS))
endif
# APIVERSION is used to build SONAME
-APIVERSION = 15
+APIVERSION = 16
SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \
upnpc.c upnpcommands.c upnpreplyparse.c testminixml.c \
diff --git a/external/miniupnpc/Makefile.mingw b/external/miniupnpc/Makefile.mingw
index 54c62de1b..1b0d44aab 100644
--- a/external/miniupnpc/Makefile.mingw
+++ b/external/miniupnpc/Makefile.mingw
@@ -18,7 +18,8 @@ OBJS=miniwget.o minixml.o igd_desc_parse.o minisoap.o \
upnpdev.o
OBJSDLL=$(addprefix dll/, $(OBJS))
-all: init upnpc-static upnpc-shared testminixml libminiupnpc.a miniupnpc.dll
+all: init upnpc-static upnpc-shared testminixml libminiupnpc.a \
+ miniupnpc.dll listdevices
init:
mkdir dll
@@ -66,6 +67,9 @@ upnpc-static: upnpc.o libminiupnpc.a
upnpc-shared: dll/upnpc.o miniupnpc.lib
$(CC) -o $@ $^ $(LDLIBS)
+listdevices: listdevices.o libminiupnpc.a
+ $(CC) -o $@ $^ $(LDLIBS)
+
wingenminiupnpcstrings: wingenminiupnpcstrings.o
wingenminiupnpcstrings.o: wingenminiupnpcstrings.c
diff --git a/external/miniupnpc/apiversions.txt b/external/miniupnpc/apiversions.txt
index daac18faa..9464a8675 100644
--- a/external/miniupnpc/apiversions.txt
+++ b/external/miniupnpc/apiversions.txt
@@ -1,7 +1,12 @@
-$Id: apiversions.txt,v 1.7 2015/07/23 20:40:08 nanard Exp $
+$Id: apiversions.txt,v 1.9 2016/01/24 17:24:36 nanard Exp $
Differences in API between miniUPnPc versions
+API version 16
+ added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr()
+ updated macro :
+ #define MINIUPNPC_API_VERSION 16
+
API version 15
changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
to "localport". When 0 or 1, behaviour is not changed, but it can take
diff --git a/external/miniupnpc/minissdpc.c b/external/miniupnpc/minissdpc.c
index 7b1c317b2..a6380dfd4 100644
--- a/external/miniupnpc/minissdpc.c
+++ b/external/miniupnpc/minissdpc.c
@@ -1,5 +1,6 @@
/* $Id: minissdpc.c,v 1.28 2015/09/18 13:05:39 nanard Exp $ */
-/* Project : miniupnp
+/* vim: tabstop=4 shiftwidth=4 noexpandtab
+ * Project : miniupnp
* Web : http://miniupnp.free.fr/
* Author : Thomas BERNARD
* copyright (c) 2005-2015 Thomas Bernard
@@ -67,6 +68,10 @@ struct sockaddr_un {
#define HAS_IP_MREQN
#endif
+#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
+#include <sys/ioctl.h>
+#endif
+
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
/* Several versions of glibc don't define this structure,
* define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
@@ -647,11 +652,25 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
{
PRINT_SOCKET_ERROR("setsockopt");
}
-#else
+#elif !defined(_WIN32)
+ struct ifreq ifr;
+ int ifrlen = sizeof(ifr);
+ strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
+ ifr.ifr_name[IFNAMSIZ-1] = '\0';
+ if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
+ {
+ PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
+ }
+ 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");
+ }
+#else /* _WIN32 */
#ifdef DEBUG
printf("Setting of multicast interface not supported with interface name.\n");
#endif
-#endif
+#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */
}
}
}
diff --git a/external/miniupnpc/miniupnpc.c b/external/miniupnpc/miniupnpc.c
index fc665a0a4..5eb9500e3 100644
--- a/external/miniupnpc/miniupnpc.c
+++ b/external/miniupnpc/miniupnpc.c
@@ -1,9 +1,9 @@
-/* $Id: miniupnpc.c,v 1.135 2015/07/23 20:40:08 nanard Exp $ */
-/* vim: tabstop=4 shiftwidth=4 noexpandtab */
-/* Project : miniupnp
+/* $Id: miniupnpc.c,v 1.148 2016/01/24 17:24: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-2016 Thomas Bernard
* This software is subjet to the conditions detailed in the
* provided LICENSE file. */
#include <stdlib.h>
@@ -72,6 +72,25 @@
#define SERVICEPREFIX "u"
#define SERVICEPREFIX2 'u'
+/* check if an ip address is a private (LAN) address
+ * see https://tools.ietf.org/html/rfc1918 */
+static int is_rfc1918addr(const char * addr)
+{
+ /* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */
+ if(COMPARE(addr, "192.168."))
+ return 1;
+ /* 10.0.0.0 - 10.255.255.255 (10/8 prefix) */
+ if(COMPARE(addr, "10."))
+ return 1;
+ /* 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) */
+ if(COMPARE(addr, "172.")) {
+ int i = atoi(addr + 4);
+ if((16 <= i) && (i <= 31))
+ return 1;
+ }
+ return 0;
+}
+
/* root description parsing */
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
{
@@ -107,6 +126,7 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service,
int soapbodylen;
char * buf;
int n;
+ int status_code;
*bufsize = 0;
snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
@@ -210,11 +230,15 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service,
return NULL;
}
- buf = getHTTPResponse(s, bufsize);
+ buf = getHTTPResponse(s, bufsize, &status_code);
#ifdef DEBUG
if(*bufsize > 0 && buf)
{
- printf("SOAP Response :\n%.*s\n", *bufsize, buf);
+ printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf);
+ }
+ else
+ {
+ printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
}
#endif
closesocket(s);
@@ -526,7 +550,7 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
* 3 = an UPnP device has been found but was not recognized as an IGD
*
* In any positive non zero return case, the urls and data structures
- * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
+ * passed as parameters are set. Dont forget to call FreeUPNPUrls(urls) to
* free allocated memory.
*/
MINIUPNP_LIBSPEC int
@@ -547,6 +571,8 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
int n_igd = 0;
char extIpAddr[16];
char myLanAddr[40];
+ int status_code = -1;
+
if(!devlist)
{
#ifdef DEBUG
@@ -570,7 +596,7 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
myLanAddr, sizeof(myLanAddr),
- dev->scope_id);
+ dev->scope_id, &status_code);
#ifdef DEBUG
if(!desc[i].xml)
{
@@ -604,20 +630,25 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
parserootdesc(desc[i].xml, desc[i].size, data);
if(desc[i].is_igd || state >= 3 )
{
+ int is_connected;
+
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
/* in state 2 and 3 we dont test if device is connected ! */
if(state >= 2)
goto free_and_return;
+ is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n",
- urls->controlURL,
- UPNPIGD_IsConnected(urls, data));
+ urls->controlURL, is_connected);
#endif
/* checks that status is connected AND there is a external IP address assigned */
- if(UPNPIGD_IsConnected(urls, data)
- && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
- goto free_and_return;
+ if(is_connected &&
+ (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
+ if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
+ && (0 != strcmp(extIpAddr, "0.0.0.0")))
+ goto free_and_return;
+ }
FreeUPNPUrls(urls);
if(data->second.servicetype[0] != '\0') {
#ifdef DEBUG
@@ -629,14 +660,17 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
+ is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n",
- urls->controlURL,
- UPNPIGD_IsConnected(urls, data));
+ urls->controlURL, is_connected);
#endif
- if(UPNPIGD_IsConnected(urls, data)
- && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0))
- goto free_and_return;
+ if(is_connected &&
+ (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
+ if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
+ && (0 != strcmp(extIpAddr, "0.0.0.0")))
+ goto free_and_return;
+ }
FreeUPNPUrls(urls);
}
}
@@ -670,8 +704,9 @@ UPNP_GetIGDFromUrl(const char * rootdescurl,
{
char * descXML;
int descXMLsize = 0;
+
descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
- lanaddr, lanaddrlen, 0);
+ lanaddr, lanaddrlen, 0, NULL);
if(descXML) {
memset(data, 0, sizeof(struct IGDdatas));
memset(urls, 0, sizeof(struct UPNPUrls));
diff --git a/external/miniupnpc/miniupnpc.h b/external/miniupnpc/miniupnpc.h
index a694978c6..5e8248850 100644
--- a/external/miniupnpc/miniupnpc.h
+++ b/external/miniupnpc/miniupnpc.h
@@ -1,8 +1,8 @@
-/* $Id: miniupnpc.h,v 1.44 2015/07/23 20:40:10 nanard Exp $ */
+/* $Id: miniupnpc.h,v 1.49 2016/01/24 17:24:36 nanard Exp $ */
/* Project: miniupnp
* http://miniupnp.free.fr/
* Author: Thomas Bernard
- * Copyright (c) 2005-2015 Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
* This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */
#ifndef MINIUPNPC_H_INCLUDED
@@ -20,7 +20,7 @@
/* versions : */
#define MINIUPNPC_VERSION "1.9"
-#define MINIUPNPC_API_VERSION 15
+#define MINIUPNPC_API_VERSION 16
/* Source port:
Using "1" as an alias for 1900 for backwards compatability
diff --git a/external/miniupnpc/miniwget.c b/external/miniupnpc/miniwget.c
index bc6e19bea..ca88a1e9d 100644
--- a/external/miniupnpc/miniwget.c
+++ b/external/miniupnpc/miniwget.c
@@ -1,8 +1,8 @@
-/* $Id: miniwget.c,v 1.70 2015/07/15 12:41:13 nanard Exp $ */
+/* $Id: miniwget.c,v 1.75 2016/01/24 17:24:36 nanard Exp $ */
/* Project : miniupnp
* Website : http://miniupnp.free.fr/
* Author : Thomas Bernard
- * Copyright (c) 2005-2015 Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -65,7 +65,7 @@
* to the length parameter.
*/
void *
-getHTTPResponse(int s, int * size)
+getHTTPResponse(int s, int * size, int * status_code)
{
char buf[2048];
int n;
@@ -83,7 +83,10 @@ getHTTPResponse(int s, int * size)
unsigned int content_buf_used = 0;
char chunksize_buf[32];
unsigned int chunksize_buf_index;
+ char * reason_phrase = NULL;
+ int reason_phrase_len = 0;
+ if(status_code) *status_code = -1;
header_buf = malloc(header_buf_len);
if(header_buf == NULL)
{
@@ -155,7 +158,7 @@ getHTTPResponse(int s, int * size)
continue;
/* parse header lines */
for(i = 0; i < endofheaders - 1; i++) {
- if(colon <= linestart && header_buf[i]==':')
+ if(linestart > 0 && colon <= linestart && header_buf[i]==':')
{
colon = i;
while(i < (endofheaders-1)
@@ -166,7 +169,29 @@ getHTTPResponse(int s, int * size)
/* detecting end of line */
else if(header_buf[i]=='\r' || header_buf[i]=='\n')
{
- if(colon > linestart && valuestart > colon)
+ if(linestart == 0 && status_code)
+ {
+ /* Status line
+ * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
+ int sp;
+ for(sp = 0; sp < i; sp++)
+ if(header_buf[sp] == ' ')
+ {
+ if(*status_code < 0)
+ *status_code = atoi(header_buf + sp + 1);
+ else
+ {
+ reason_phrase = header_buf + sp + 1;
+ reason_phrase_len = i - sp - 1;
+ break;
+ }
+ }
+#ifdef DEBUG
+ printf("HTTP status code = %d, Reason phrase = %.*s\n",
+ *status_code, reason_phrase_len, reason_phrase);
+#endif
+ }
+ else if(colon > linestart && valuestart > colon)
{
#ifdef DEBUG
printf("header='%.*s', value='%.*s'\n",
@@ -337,7 +362,8 @@ static void *
miniwget3(const char * host,
unsigned short port, const char * path,
int * size, char * addr_str, int addr_str_len,
- const char * httpversion, unsigned int scope_id)
+ const char * httpversion, unsigned int scope_id,
+ int * status_code)
{
char buf[2048];
int s;
@@ -435,7 +461,7 @@ miniwget3(const char * host,
sent += n;
}
}
- content = getHTTPResponse(s, size);
+ content = getHTTPResponse(s, size, status_code);
closesocket(s);
return content;
}
@@ -444,18 +470,20 @@ miniwget3(const char * host,
* Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
static void *
miniwget2(const char * host,
- unsigned short port, const char * path,
- int * size, char * addr_str, int addr_str_len,
- unsigned int scope_id)
+ unsigned short port, const char * path,
+ int * size, char * addr_str, int addr_str_len,
+ unsigned int scope_id, int * status_code)
{
char * respbuffer;
#if 1
respbuffer = miniwget3(host, port, path, size,
- addr_str, addr_str_len, "1.1", scope_id);
+ addr_str, addr_str_len, "1.1",
+ scope_id, status_code);
#else
respbuffer = miniwget3(host, port, path, size,
- addr_str, addr_str_len, "1.0", scope_id);
+ addr_str, addr_str_len, "1.0",
+ scope_id, status_code);
if (*size == 0)
{
#ifdef DEBUG
@@ -463,7 +491,8 @@ miniwget2(const char * host,
#endif
free(respbuffer);
respbuffer = miniwget3(host, port, path, size,
- addr_str, addr_str_len, "1.1", scope_id);
+ addr_str, addr_str_len, "1.1",
+ scope_id, status_code);
}
#endif
return respbuffer;
@@ -588,7 +617,8 @@ parseURL(const char * url,
}
void *
-miniwget(const char * url, int * size, unsigned int scope_id)
+miniwget(const char * url, int * size,
+ unsigned int scope_id, int * status_code)
{
unsigned short port;
char * path;
@@ -601,12 +631,13 @@ miniwget(const char * url, int * size, unsigned int scope_id)
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id);
#endif
- return miniwget2(hostname, port, path, size, 0, 0, scope_id);
+ return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code);
}
void *
miniwget_getaddr(const char * url, int * size,
- char * addr, int addrlen, unsigned int scope_id)
+ char * addr, int addrlen, unsigned int scope_id,
+ int * status_code)
{
unsigned short port;
char * path;
@@ -621,6 +652,6 @@ miniwget_getaddr(const char * url, int * size,
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id);
#endif
- return miniwget2(hostname, port, path, size, addr, addrlen, scope_id);
+ return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code);
}
diff --git a/external/miniupnpc/miniwget.h b/external/miniupnpc/miniwget.h
index e231fc27a..0701494d0 100644
--- a/external/miniupnpc/miniwget.h
+++ b/external/miniupnpc/miniwget.h
@@ -1,7 +1,7 @@
-/* $Id: miniwget.h,v 1.7 2012/06/23 22:35:59 nanard Exp $ */
+/* $Id: miniwget.h,v 1.12 2016/01/24 17:24:36 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2015 Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
@@ -14,11 +14,11 @@
extern "C" {
#endif
-MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size);
+MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size, int * status_code);
-MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int);
+MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *);
-MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int);
+MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *);
int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
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/testminiwget.c b/external/miniupnpc/testminiwget.c
index 8ae90320e..5eb49ec1c 100644
--- a/external/miniupnpc/testminiwget.c
+++ b/external/miniupnpc/testminiwget.c
@@ -1,7 +1,7 @@
-/* $Id: testminiwget.c,v 1.4 2012/06/23 22:35:59 nanard Exp $ */
+/* $Id: testminiwget.c,v 1.5 2016/01/24 17:24:36 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2012 Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution.
* */
@@ -20,15 +20,17 @@ int main(int argc, char * * argv)
int size, writtensize;
FILE *f;
char addr[64];
+ int status_code = -1;
if(argc < 3) {
fprintf(stderr, "Usage:\t%s url file\n", argv[0]);
fprintf(stderr, "Example:\t%s http://www.google.com/ out.html\n", argv[0]);
return 1;
}
- data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0);
- if(!data) {
- fprintf(stderr, "Error fetching %s\n", argv[1]);
+ data = miniwget_getaddr(argv[1], &size, addr, sizeof(addr), 0, &status_code);
+ if(!data || (status_code != 200)) {
+ if(data) free(data);
+ fprintf(stderr, "Error %d fetching %s\n", status_code, argv[1]);
return 1;
}
printf("local address : %s\n", addr);
diff --git a/external/miniupnpc/upnpc.c b/external/miniupnpc/upnpc.c
index 1a910ccdc..94f131c87 100644
--- a/external/miniupnpc/upnpc.c
+++ b/external/miniupnpc/upnpc.c
@@ -1,7 +1,7 @@
-/* $Id: upnpc.c,v 1.111 2015/07/23 20:40:10 nanard Exp $ */
+/* $Id: upnpc.c,v 1.114 2016/01/22 15:04:23 nanard Exp $ */
/* Project : miniupnp
* Author : Thomas Bernard
- * Copyright (c) 2005-2015 Thomas Bernard
+ * Copyright (c) 2005-2016 Thomas Bernard
* This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */
@@ -66,7 +66,7 @@ static void DisplayInfos(struct UPNPUrls * urls,
char connectionType[64];
char status[64];
char lastconnerr[64];
- unsigned int uptime;
+ unsigned int uptime = 0;
unsigned int brUp, brDown;
time_t timenow, timestarted;
int r;
@@ -82,9 +82,11 @@ static void DisplayInfos(struct UPNPUrls * urls,
else
printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
status, uptime, lastconnerr);
- timenow = time(NULL);
- timestarted = timenow - uptime;
- printf(" Time started : %s", ctime(&timestarted));
+ if(uptime > 0) {
+ timenow = time(NULL);
+ timestarted = timenow - uptime;
+ printf(" Time started : %s", ctime(&timestarted));
+ }
if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
&brDown, &brUp) != UPNPCOMMAND_SUCCESS) {
printf("GetLinkLayerMaxBitRates failed.\n");
@@ -538,7 +540,7 @@ int main(int argc, char ** argv)
char ** commandargv = 0;
int commandargc = 0;
struct UPNPDev * devlist = 0;
- char lanaddr[64]; /* my ip address on the LAN */
+ char lanaddr[64] = "unset"; /* my ip address on the LAN */
int i;
const char * rootdescurl = 0;
const char * multicastif = 0;
@@ -560,7 +562,7 @@ int main(int argc, char ** argv)
}
#endif
printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
- printf(" (c) 2005-2015 Thomas Bernard.\n");
+ printf(" (c) 2005-2016 Thomas Bernard.\n");
printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n"
"for more information.\n");
/* command line processing */
diff --git a/external/miniupnpc/upnpcommands.c b/external/miniupnpc/upnpcommands.c
index cabdb50d8..76cf9e391 100644
--- a/external/miniupnpc/upnpcommands.c
+++ b/external/miniupnpc/upnpcommands.c
@@ -616,14 +616,14 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
protocol[3] = '\0';
}
p = GetValueFromNameValueList(&pdata, "NewInternalClient");
- if(p && intClient)
+ if(p)
{
strncpy(intClient, p, 16);
intClient[15] = '\0';
r = 0;
}
p = GetValueFromNameValueList(&pdata, "NewInternalPort");
- if(p && intPort)
+ if(p)
{
strncpy(intPort, p, 6);
intPort[5] = '\0';
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 0ac4a0aa1..e2349744d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -87,7 +87,6 @@ add_subdirectory(wallet)
add_subdirectory(p2p)
add_subdirectory(cryptonote_protocol)
-add_subdirectory(connectivity_tool)
add_subdirectory(miner)
add_subdirectory(simplewallet)
add_subdirectory(daemonizer)
diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt
index 4953d92a6..39459d5c4 100644
--- a/src/blockchain_db/CMakeLists.txt
+++ b/src/blockchain_db/CMakeLists.txt
@@ -65,6 +65,7 @@ target_link_libraries(blockchain_db
crypto
cryptonote_core
${Boost_DATE_TIME_LIBRARY}
+ ${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
${LMDB_LIBRARY}
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index bfdb22a10..a7fa556bd 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -135,7 +135,7 @@ const unsigned int DB_BUFFER_LENGTH = 32 * MB;
const unsigned int DB_DEF_CACHESIZE = 256 * MB;
#if defined(BDB_BULK_CAN_THREAD)
-const unsigned int DB_BUFFER_COUNT = std::thread::hardware_concurrency();
+const unsigned int DB_BUFFER_COUNT = boost::thread::hardware_concurrency();
#else
const unsigned int DB_BUFFER_COUNT = 1;
#endif
@@ -2180,6 +2180,12 @@ void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, const std::v
LOG_PRINT_L3("db3: " << db3);
}
+std::map<uint64_t, uint64_t>::BlockchainBDB::get_output_histogram(const std::vector<uint64_t> &amounts) const
+{
+ LOG_PRINT_L3("BlockchainBDB::" << __func__);
+ throw1(DB_ERROR("Not implemented."));
+}
+
void BlockchainBDB::set_hard_fork_starting_height(uint8_t version, uint64_t height)
{
LOG_PRINT_L3("BlockchainBDB::" << __func__);
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index bf9665cae..5c6bda4eb 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -130,7 +130,7 @@ public:
T acquire_buffer()
{
- std::unique_lock<std::mutex> lock(m_lock);
+ boost::unique_lock<boost::mutex> lock(m_lock);
m_cv.wait(lock, [&]{ return m_count > 0; });
--m_count;
@@ -154,7 +154,7 @@ public:
void release_buffer(T buffer)
{
- std::unique_lock<std::mutex> lock(m_lock);
+ boost::unique_lock<boost::mutex> lock(m_lock);
assert(buffer != nullptr);
auto it = m_buffer_map.find(buffer);
@@ -196,10 +196,10 @@ private:
std::vector<T> m_buffers;
std::unordered_map<T, size_t> m_buffer_map;
- std::condition_variable m_cv;
+ boost::condition_variable m_cv;
std::vector<bool> m_open_slot;
size_t m_count;
- std::mutex m_lock;
+ boost::mutex m_lock;
size_t m_buffer_count;
};
@@ -341,6 +341,15 @@ public:
virtual bool can_thread_bulk_indices() const { return false; }
#endif
+ /**
+ * @brief return a histogram of outputs on the blockchain
+ *
+ * @param amounts optional set of amounts to lookup
+ *
+ * @return a set of amount/instances
+ */
+ std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts) const;
+
private:
virtual void add_block( const block& blk
, const size_t& block_size
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 93527b484..7edef51ea 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -38,18 +38,20 @@
#include "cryptonote_core/difficulty.h"
#include "cryptonote_core/hardfork.h"
-/* DB Driver Interface
+/** \file
+ * Cryptonote Blockchain Database Interface
*
* The DB interface is a store for the canonical block chain.
* It serves as a persistent storage for the blockchain.
*
- * For the sake of efficiency, the reference implementation will also
+ * For the sake of efficiency, a concrete implementation may also
* store some blockchain data outside of the blocks, such as spent
* transfer key images, unspent transaction outputs, etc.
*
+ * Examples are as follows:
+ *
* Transactions are duplicated so that we don't have to fetch a whole block
- * in order to fetch a transaction from that block. If this is deemed
- * unnecessary later, this can change.
+ * in order to fetch a transaction from that block.
*
* Spent key images are duplicated outside of the blocks so it is quick
* to verify an output hasn't already been spent
@@ -57,12 +59,6 @@
* Unspent transaction outputs are duplicated to quickly gather random
* outputs to use for mixins
*
- * IMPORTANT:
- * A concrete implementation of this interface should populate these
- * duplicated members! It is possible to have a partial implementation
- * of this interface call to private members of the interface to be added
- * later that will then populate as needed.
- *
* Indices and Identifiers:
* The word "index" is used ambiguously throughout this code. It is
* particularly confusing when talking about the output or transaction
@@ -82,61 +78,6 @@
* of a specific amount. An "output local index" N refers to the Nth output
* of a specific tx.
*
- *
- * General:
- * open()
- * is_open()
- * close()
- * sync()
- * reset()
- *
- * Lock and unlock provided for reorg externally, and for block
- * additions internally, this way threaded reads are completely fine
- * unless the blockchain is changing.
- * bool lock()
- * unlock()
- *
- * vector<str> get_filenames()
- *
- * Blocks:
- * bool block_exists(hash)
- * height add_block(block, block_size, cumulative_difficulty, coins_generated, transactions)
- * block get_block(hash)
- * height get_block_height(hash)
- * header get_block_header(hash)
- * block get_block_from_height(height)
- * size_t get_block_size(height)
- * difficulty get_block_cumulative_difficulty(height)
- * uint64_t get_block_already_generated_coins(height)
- * uint64_t get_block_timestamp(height)
- * uint64_t get_top_block_timestamp()
- * hash get_block_hash_from_height(height)
- * blocks get_blocks_range(height1, height2)
- * hashes get_hashes_range(height1, height2)
- * hash top_block_hash()
- * block get_top_block()
- * height height()
- * void pop_block(block&, tx_list&)
- *
- * Transactions:
- * bool tx_exists(hash, tx_id)
- * uint64_t get_tx_unlock_time(hash)
- * tx get_tx(hash)
- * uint64_t get_tx_count()
- * tx_list get_tx_list(hash_list)
- * height get_tx_block_height(hash)
- *
- * Outputs:
- * uint64_t get_num_outputs(amount)
- * pub_key get_output_key(amount, index)
- * hash,index get_output_tx_and_index_from_global(index)
- * hash,index get_output_tx_and_index(amount, index)
- * vec<uint64> get_tx_amount_output_indices(tx_id)
- *
- *
- * Spent Output Key Images:
- * bool has_key_image(key_image)
- *
* Exceptions:
* DB_ERROR -- generic
* DB_OPEN_FAILURE
@@ -156,15 +97,19 @@
namespace cryptonote
{
-// typedef for convenience
+/** a pair of <transaction hash, output index>, typedef for convenience */
typedef std::pair<crypto::hash, uint64_t> tx_out_index;
#pragma pack(push, 1)
+
+/**
+ * @brief a struct containing output metadata
+ */
struct output_data_t
{
- crypto::public_key pubkey;
- uint64_t unlock_time;
- uint64_t height;
+ crypto::public_key pubkey; //!< the output's public key (for spend verification)
+ uint64_t unlock_time; //!< the output's unlock time (or height)
+ uint64_t height; //!< the height of the block which created the output
};
#pragma pack(pop)
@@ -180,6 +125,10 @@ struct tx_data_t
/***********************************
* Exception Definitions
***********************************/
+
+/**
+ * @brief A base class for BlockchainDB exceptions
+ */
class DB_EXCEPTION : public std::exception
{
private:
@@ -197,6 +146,9 @@ class DB_EXCEPTION : public std::exception
}
};
+/**
+ * @brief A generic BlockchainDB exception
+ */
class DB_ERROR : public DB_EXCEPTION
{
public:
@@ -204,7 +156,9 @@ class DB_ERROR : public DB_EXCEPTION
DB_ERROR(const char* s) : DB_EXCEPTION(s) { }
};
-// For distinguishing errors trying to set up a DB txn from other errors
+/**
+ * @brief thrown when there is an error starting a DB transaction
+ */
class DB_ERROR_TXN_START : public DB_EXCEPTION
{
public:
@@ -212,6 +166,9 @@ class DB_ERROR_TXN_START : public DB_EXCEPTION
DB_ERROR_TXN_START(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when opening the BlockchainDB fails
+ */
class DB_OPEN_FAILURE : public DB_EXCEPTION
{
public:
@@ -219,6 +176,9 @@ class DB_OPEN_FAILURE : public DB_EXCEPTION
DB_OPEN_FAILURE(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when creating the BlockchainDB fails
+ */
class DB_CREATE_FAILURE : public DB_EXCEPTION
{
public:
@@ -226,6 +186,9 @@ class DB_CREATE_FAILURE : public DB_EXCEPTION
DB_CREATE_FAILURE(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when synchronizing the BlockchainDB to disk fails
+ */
class DB_SYNC_FAILURE : public DB_EXCEPTION
{
public:
@@ -233,6 +196,9 @@ class DB_SYNC_FAILURE : public DB_EXCEPTION
DB_SYNC_FAILURE(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when a requested block does not exist
+ */
class BLOCK_DNE : public DB_EXCEPTION
{
public:
@@ -240,6 +206,9 @@ class BLOCK_DNE : public DB_EXCEPTION
BLOCK_DNE(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when a block's parent does not exist (and it needed to)
+ */
class BLOCK_PARENT_DNE : public DB_EXCEPTION
{
public:
@@ -247,6 +216,9 @@ class BLOCK_PARENT_DNE : public DB_EXCEPTION
BLOCK_PARENT_DNE(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when a block exists, but shouldn't, namely when adding a block
+ */
class BLOCK_EXISTS : public DB_EXCEPTION
{
public:
@@ -254,6 +226,9 @@ class BLOCK_EXISTS : public DB_EXCEPTION
BLOCK_EXISTS(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when something is wrong with the block to be added
+ */
class BLOCK_INVALID : public DB_EXCEPTION
{
public:
@@ -261,6 +236,9 @@ class BLOCK_INVALID : public DB_EXCEPTION
BLOCK_INVALID(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when a requested transaction does not exist
+ */
class TX_DNE : public DB_EXCEPTION
{
public:
@@ -268,6 +246,9 @@ class TX_DNE : public DB_EXCEPTION
TX_DNE(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when a transaction exists, but shouldn't, namely when adding a block
+ */
class TX_EXISTS : public DB_EXCEPTION
{
public:
@@ -275,6 +256,9 @@ class TX_EXISTS : public DB_EXCEPTION
TX_EXISTS(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when a requested output does not exist
+ */
class OUTPUT_DNE : public DB_EXCEPTION
{
public:
@@ -282,6 +266,9 @@ class OUTPUT_DNE : public DB_EXCEPTION
OUTPUT_DNE(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when an output exists, but shouldn't, namely when adding a block
+ */
class OUTPUT_EXISTS : public DB_EXCEPTION
{
public:
@@ -289,6 +276,9 @@ class OUTPUT_EXISTS : public DB_EXCEPTION
OUTPUT_EXISTS(const char* s) : DB_EXCEPTION(s) { }
};
+/**
+ * @brief thrown when a spent key image exists, but shouldn't, namely when adding a block
+ */
class KEY_IMAGE_EXISTS : public DB_EXCEPTION
{
public:
@@ -301,6 +291,18 @@ class KEY_IMAGE_EXISTS : public DB_EXCEPTION
***********************************/
+/**
+ * @brief The BlockchainDB backing store interface declaration/contract
+ *
+ * This class provides a uniform interface for using BlockchainDB to store
+ * a blockchain. Any implementation of this class will also implement all
+ * functions exposed here, so one can use this class without knowing what
+ * implementation is being used. Refer to each pure virtual function's
+ * documentation here when implementing a BlockchainDB subclass.
+ *
+ * A subclass which encounters an issue should report that issue by throwing
+ * a DB_EXCEPTION which adequately conveys the issue.
+ */
class BlockchainDB
{
private:
@@ -308,7 +310,22 @@ private:
* private virtual members
*********************************************************************/
- // tells the subclass to add the block and metadata to storage
+ /**
+ * @brief add the block and metadata to the db
+ *
+ * The subclass implementing this will add the specified block and
+ * block metadata to its backing store. This does not include its
+ * transactions, those are added in a separate step.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param blk the block to be added
+ * @param block_size the size of the block (transactions and all)
+ * @param cumulative_difficulty the accumulated difficulty after this block
+ * @param coins_generated the number of coins generated total after this block
+ * @param blk_hash the hash of the block
+ */
virtual void add_block( const block& blk
, const size_t& block_size
, const difficulty_type& cumulative_difficulty
@@ -316,90 +333,280 @@ private:
, const crypto::hash& blk_hash
) = 0;
- // tells the subclass to remove data about the top block
+ /**
+ * @brief remove data about the top block
+ *
+ * The subclass implementing this will remove the block data from the top
+ * block in the chain. The data to be removed is that which was added in
+ * BlockchainDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash)
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ */
virtual void remove_block() = 0;
- // tells the subclass to store the transaction and its metadata, returns tx ID
+ /**
+ * @brief store the transaction and its metadata
+ *
+ * The subclass implementing this will add the specified transaction data
+ * to its backing store. This includes only the transaction blob itself
+ * and the other data passed here, not the separate outputs of the
+ * transaction.
+ *
+ * It returns a tx ID, which is a mapping from the tx_hash. The tx ID
+ * is used in #add_tx_amount_output_indices().
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param blk_hash the hash of the block containing the transaction
+ * @param tx the transaction to be added
+ * @param tx_hash the hash of the transaction
+ * @return the transaction ID
+ */
virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash) = 0;
- // tells the subclass to remove data about a transaction
+ /**
+ * @brief remove data about a transaction
+ *
+ * The subclass implementing this will remove the transaction data
+ * for the passed transaction. The data to be removed was added in
+ * add_transaction_data(). Additionally, current subclasses have behavior
+ * which requires the transaction itself as a parameter here. Future
+ * implementations should note that this parameter is subject to be removed
+ * at a later time.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param tx_hash the hash of the transaction to be removed
+ * @param tx the transaction
+ */
virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx) = 0;
- // tells the subclass to store an output, returns amount output index
- virtual uint64_t add_output(const crypto::hash& tx_hash,
- const tx_out& tx_output,
- const uint64_t& local_index,
- const uint64_t unlock_time
- ) = 0;
-
- // tells the subclass to store amount output indices for a tx's outputs
- virtual void add_tx_amount_output_indices(const uint64_t tx_id,
- const std::vector<uint64_t>& amount_output_indices
- ) = 0;
-
- // tells the subclass to store a spent key
+ /**
+ * @brief store an output
+ *
+ * The subclass implementing this will add the output data passed to its
+ * backing store in a suitable manner. In addition, the subclass is responsible
+ * for keeping track of the global output count in some manner, so that
+ * outputs may be indexed by the order in which they were created. In the
+ * future, this tracking (of the number, at least) should be moved to
+ * this class, as it is necessary and the same among all BlockchainDB.
+ *
+ * It returns an amount output index, which is the index of the output
+ * for its specified amount.
+ *
+ * This data should be stored in such a manner that the only thing needed to
+ * reverse the process is the tx_out.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param tx_hash hash of the transaction the output was created by
+ * @param tx_output the output
+ * @param local_index index of the output in its transaction
+ * @param unlock_time unlock time/height of the output
+ * @return amount output index
+ */
+ virtual uint64_t add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time) = 0;
+
+ /**
+ * @brief store amount output indices for a tx's outputs
+ *
+ * The subclass implementing this will add the amount output indices to its
+ * backing store in a suitable manner. The tx_id will be the same one that
+ * was returned from #add_output().
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param tx_id ID of the transaction containing these outputs
+ * @param amount_output_indices the amount output indices of the transaction
+ */
+ virtual void add_tx_amount_output_indices(const uint64_t tx_id, const std::vector<uint64_t>& amount_output_indices) = 0;
+
+ /**
+ * @brief store a spent key
+ *
+ * The subclass implementing this will store the spent key image.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param k_image the spent key image to store
+ */
virtual void add_spent_key(const crypto::key_image& k_image) = 0;
- // tells the subclass to remove a spent key
+ /**
+ * @brief remove a spent key
+ *
+ * The subclass implementing this will remove the key image.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param k_image the spent key image to remove
+ */
virtual void remove_spent_key(const crypto::key_image& k_image) = 0;
/*********************************************************************
* private concrete members
*********************************************************************/
- // private version of pop_block, for undoing if an add_block goes tits up
+ /**
+ * @brief private version of pop_block, for undoing if an add_block fails
+ *
+ * This function simply calls pop_block(block& blk, std::vector<transaction>& txs)
+ * with dummy parameters, as the returns-by-reference can be discarded.
+ */
void pop_block();
- // helper function for add_transactions, to add each individual tx
+ /**
+ * @brief helper function for add_transactions, to add each individual transaction
+ *
+ * This function is called by add_transactions() for each transaction to be
+ * added.
+ *
+ * @param blk_hash hash of the block which has the transaction
+ * @param tx the transaction to add
+ * @param tx_hash_ptr the hash of the transaction, if already calculated
+ */
void add_transaction(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash* tx_hash_ptr = NULL);
// helper function to remove transaction from blockchain
+ /**
+ * @brief helper function to remove transaction from the blockchain
+ *
+ * This function encapsulates aspects of removing a transaction.
+ *
+ * @param tx_hash the hash of the transaction to be removed
+ */
void remove_transaction(const crypto::hash& tx_hash);
- uint64_t num_calls = 0;
- uint64_t time_blk_hash = 0;
- uint64_t time_add_block1 = 0;
- uint64_t time_add_transaction = 0;
+ uint64_t num_calls = 0; //!< a performance metric
+ uint64_t time_blk_hash = 0; //!< a performance metric
+ uint64_t time_add_block1 = 0; //!< a performance metric
+ uint64_t time_add_transaction = 0; //!< a performance metric
protected:
- mutable uint64_t time_tx_exists = 0;
- uint64_t time_commit1 = 0;
- bool m_auto_remove_logs = true;
+ mutable uint64_t time_tx_exists = 0; //!< a performance metric
+ uint64_t time_commit1 = 0; //!< a performance metric
+ bool m_auto_remove_logs = true; //!< whether or not to automatically remove old logs
HardFork* m_hardfork;
public:
- // virtual dtor
+ /**
+ * @brief An empty destructor.
+ */
virtual ~BlockchainDB() { };
- // reset profiling stats
+ /**
+ * @brief reset profiling stats
+ */
void reset_stats();
- // show profiling stats
+ /**
+ * @brief show profiling stats
+ *
+ * This function prints current performance/profiling data to whichever
+ * log file(s) are set up (possibly including stdout or stderr)
+ */
void show_stats();
- // open the db at location <filename>, or create it if there isn't one.
+ /**
+ * @brief open a db, or create it if necessary.
+ *
+ * This function opens an existing database or creates it if it
+ * does not exist.
+ *
+ * The subclass implementing this will handle all file opening/creation,
+ * and is responsible for maintaining its state.
+ *
+ * The parameter <filename> may not refer to a file name, necessarily, but
+ * could be an IP:PORT for a database which needs it, and so on. Calling it
+ * <filename> is convenient and should be descriptive enough, however.
+ *
+ * For now, db_flags are
+ * specific to the subclass being instantiated. This is subject to change,
+ * and the db_flags parameter may be deprecated.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param filename a string referring to the BlockchainDB to open
+ * @param db_flags flags relevant to how to open/use the BlockchainDB
+ */
virtual void open(const std::string& filename, const int db_flags = 0) = 0;
- // returns true of the db is open/ready, else false
+ /**
+ * @brief Gets the current open/ready state of the BlockchainDB
+ *
+ * @return true if open/ready, otherwise false
+ */
bool is_open() const;
- // close and sync the db
+ /**
+ * @brief close the BlockchainDB
+ *
+ * At minimum, this call ensures that further use of the BlockchainDB
+ * instance will not have effect. In any case where it is necessary
+ * to do so, a subclass implementing this will sync with disk.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ */
virtual void close() = 0;
- // sync the db
+ /**
+ * @brief sync the BlockchainDB with disk
+ *
+ * This function should write any changes to whatever permanent backing
+ * store the subclass uses. Example: a BlockchainDB instance which
+ * keeps the whole blockchain in RAM won't need to regularly access a
+ * disk, but should write out its state when this is called.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ */
virtual void sync() = 0;
- // reset the db -- USE WITH CARE
+ /**
+ * @brief Remove everything from the BlockchainDB
+ *
+ * This function should completely remove all data from a BlockchainDB.
+ *
+ * Use with caution!
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ */
virtual void reset() = 0;
- // get all files used by this db (if any)
+ /**
+ * @brief get all files used by the BlockchainDB (if any)
+ *
+ * This function is largely for ease of automation, namely for unit tests.
+ *
+ * The subclass implementation should return all filenames it uses.
+ *
+ * @return a list of filenames
+ */
virtual std::vector<std::string> get_filenames() const = 0;
// return the name of the folder the db's file(s) should reside in
+ /**
+ * @brief gets the name of the folder the BlockchainDB's file(s) should be in
+ *
+ * The subclass implementation should return the name of the folder in which
+ * it stores files, or an empty string if there is none.
+ *
+ * @return the name of the folder with the BlockchainDB's files, if any.
+ */
virtual std::string get_db_name() const = 0;
@@ -407,13 +614,86 @@ public:
// RAII-friendly and multi-read one-write friendly locking mechanism
//
// acquire db lock
+ /**
+ * @brief acquires the BlockchainDB lock
+ *
+ * This function is a stub until such a time as locking is implemented at
+ * this level.
+ *
+ * The subclass implementation should return true unless implementing a
+ * locking scheme of some sort, in which case it should return true upon
+ * acquisition of the lock and block until then.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @return true, unless at a future time false makes sense (timeout, etc)
+ */
virtual bool lock() = 0;
// release db lock
+ /**
+ * @brief This function releases the BlockchainDB lock
+ *
+ * The subclass, should it have implemented lock(), will release any lock
+ * held by the calling thread. In the case of recursive locking, it should
+ * release one instance of a lock.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ */
virtual void unlock() = 0;
+ /**
+ * @brief tells the BlockchainDB to start a new "batch" of blocks
+ *
+ * If the subclass implements a batching method of caching blocks in RAM to
+ * be added to a backing store in groups, it should start a batch which will
+ * end either when <batch_num_blocks> has been added or batch_stop() has
+ * been called. In either case, it should end the batch and write to its
+ * backing store.
+ *
+ * If a batch is already in-progress, this function should throw a DB_ERROR.
+ * This exception may change in the future if it is deemed necessary to
+ * have a more granular exception type for this scenario.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param batch_num_blocks number of blocks to batch together
+ */
virtual void batch_start(uint64_t batch_num_blocks=0) = 0;
+
+ /**
+ * @brief ends a batch transaction
+ *
+ * If the subclass implements batching, this function should store the
+ * batch it is currently on and mark it finished.
+ *
+ * If no batch is in-progress, this function should throw a DB_ERROR.
+ * This exception may change in the future if it is deemed necessary to
+ * have a more granular exception type for this scenario.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ */
virtual void batch_stop() = 0;
+
+ /**
+ * @brief sets whether or not to batch transactions
+ *
+ * If the subclass implements batching, this function tells it to begin
+ * batching automatically.
+ *
+ * If the subclass implements batching and has a batch in-progress, a
+ * parameter of false should disable batching and call batch_stop() to
+ * store the current batch.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param bool batch whether or not to use batch transactions.
+ */
virtual void set_batch_transactions(bool) = 0;
virtual void block_txn_start(bool readonly=false) = 0;
@@ -423,8 +703,26 @@ public:
virtual void set_hard_fork(HardFork* hf);
// adds a block with the given metadata to the top of the blockchain, returns the new height
- // NOTE: subclass implementations of this (or the functions it calls) need
- // to handle undoing any partially-added blocks in the event of a failure.
+ /**
+ * @brief handles the addition of a new block to BlockchainDB
+ *
+ * This function organizes block addition and calls various functions as
+ * necessary.
+ *
+ * NOTE: subclass implementations of this (or the functions it calls) need
+ * to handle undoing any partially-added blocks in the event of a failure.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ *
+ * @param blk the block to be added
+ * @param block_size the size of the block (transactions and all)
+ * @param cumulative_difficulty the accumulated difficulty after this block
+ * @param coins_generated the number of coins generated total after this block
+ * @param txs the transactions in the block
+ *
+ * @return the height of the chain post-addition
+ */
virtual uint64_t add_block( const block& blk
, const size_t& block_size
, const difficulty_type& cumulative_difficulty
@@ -432,140 +730,634 @@ public:
, const std::vector<transaction>& txs
);
- // return true if a block with hash <h> exists in the blockchain
+ /**
+ * @brief checks if a block exists
+ *
+ * @param h the hash of the requested block
+ *
+ * @return true of the block exists, otherwise false
+ */
virtual bool block_exists(const crypto::hash& h) const = 0;
- // return block with hash <h>
+ /**
+ * @brief fetches the block with the given hash
+ *
+ * The subclass should return the requested block.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param h the hash to look for
+ *
+ * @return the block requested
+ */
virtual block get_block(const crypto::hash& h) const = 0;
- // return the height of the block with hash <h> on the blockchain,
- // throw if it doesn't exist
+ /**
+ * @brief gets the height of the block with a given hash
+ *
+ * The subclass should return the requested height.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param h the hash to look for
+ *
+ * @return the height
+ */
virtual uint64_t get_block_height(const crypto::hash& h) const = 0;
- // return header for block with hash <h>
+ /**
+ * @brief fetch a block header
+ *
+ * The subclass should return the block header from the block with
+ * the given hash.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param h the hash to look for
+ *
+ * @return the block header
+ */
virtual block_header get_block_header(const crypto::hash& h) const = 0;
- // return block at height <height>
+ /**
+ * @brief fetch a block by height
+ *
+ * The subclass should return the block at the given height.
+ *
+ * If the block does not exist, that is to say if the blockchain is not
+ * that high, then the subclass should throw BLOCK_DNE
+ *
+ * @param height the height to look for
+ *
+ * @return the block
+ */
virtual block get_block_from_height(const uint64_t& height) const = 0;
- // return timestamp of block at height <height>
+ /**
+ * @brief fetch a block's timestamp
+ *
+ * The subclass should return the timestamp of the block with the
+ * given height.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param height the height requested
+ *
+ * @return the timestamp
+ */
virtual uint64_t get_block_timestamp(const uint64_t& height) const = 0;
- // return timestamp of most recent block
+ /**
+ * @brief fetch the top block's timestamp
+ *
+ * The subclass should return the timestamp of the most recent block.
+ *
+ * @return the top block's timestamp
+ */
virtual uint64_t get_top_block_timestamp() const = 0;
- // return block size of block at height <height>
+ /**
+ * @brief fetch a block's size
+ *
+ * The subclass should return the size of the block with the
+ * given height.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param height the height requested
+ *
+ * @return the size
+ */
virtual size_t get_block_size(const uint64_t& height) const = 0;
- // return cumulative difficulty up to and including block at height <height>
+ /**
+ * @brief fetch a block's cumulative difficulty
+ *
+ * The subclass should return the cumulative difficulty of the block with the
+ * given height.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param height the height requested
+ *
+ * @return the cumulative difficulty
+ */
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const = 0;
- // return difficulty of block at height <height>
+ /**
+ * @brief fetch a block's difficulty
+ *
+ * The subclass should return the difficulty of the block with the
+ * given height.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param height the height requested
+ *
+ * @return the difficulty
+ */
virtual difficulty_type get_block_difficulty(const uint64_t& height) const = 0;
- // return number of coins generated up to and including block at height <height>
+ /**
+ * @brief fetch a block's already generated coins
+ *
+ * The subclass should return the total coins generated as of the block
+ * with the given height.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param height the height requested
+ *
+ * @return the already generated coins
+ */
virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const = 0;
- // return hash of block at height <height>
+ /**
+ * @brief fetch a block's hash
+ *
+ * The subclass should return hash of the block with the
+ * given height.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param height the height requested
+ *
+ * @return the hash
+ */
virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const = 0;
- // return vector of blocks in range <h1,h2> of height (inclusively)
+ /**
+ * @brief fetch a list of blocks
+ *
+ * The subclass should return a vector of blocks with heights starting at
+ * h1 and ending at h2, inclusively.
+ *
+ * If the height range requested goes past the end of the blockchain,
+ * the subclass should throw BLOCK_DNE. (current implementations simply
+ * don't catch this exception as thrown by methods called within)
+ *
+ * @param h1 the start height
+ * @param h2 the end height
+ *
+ * @return a vector of blocks
+ */
virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const = 0;
- // return vector of block hashes in range <h1, h2> of height (inclusively)
+ /**
+ * @brief fetch a list of block hashes
+ *
+ * The subclass should return a vector of block hashes from blocks with
+ * heights starting at h1 and ending at h2, inclusively.
+ *
+ * If the height range requested goes past the end of the blockchain,
+ * the subclass should throw BLOCK_DNE. (current implementations simply
+ * don't catch this exception as thrown by methods called within)
+ *
+ * @param h1 the start height
+ * @param h2 the end height
+ *
+ * @return a vector of block hashes
+ */
virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const = 0;
- // return the hash of the top block on the chain
+ /**
+ * @brief fetch the top block's hash
+ *
+ * The subclass should return the hash of the most recent block
+ *
+ * @return the top block's hash
+ */
virtual crypto::hash top_block_hash() const = 0;
- // return the block at the top of the blockchain
+ /**
+ * @brief fetch the top block
+ *
+ * The subclass should return most recent block
+ *
+ * @return the top block
+ */
virtual block get_top_block() const = 0;
- // return the height of the chain
+ /**
+ * @brief fetch the current blockchain height
+ *
+ * The subclass should return the current blockchain height
+ *
+ * @return the current blockchain height
+ */
virtual uint64_t height() const = 0;
- // pops the top block off the blockchain.
- // Returns by reference the popped block and its associated transactions
- //
- // IMPORTANT:
- // When a block is popped, the transactions associated with it need to be
- // removed, as well as outputs and spent key images associated with
- // those transactions.
+
+ /**
+ * <!--
+ * TODO: Rewrite (if necessary) such that all calls to remove_* are
+ * done in concrete members of this base class.
+ * -->
+ *
+ * @brief pops the top block off the blockchain
+ *
+ * The subclass should remove the most recent block from the blockchain,
+ * along with all transactions, outputs, and other metadata created as
+ * a result of its addition to the blockchain. Most of this is handled
+ * by the concrete members of the base class provided the subclass correctly
+ * implements remove_* functions.
+ *
+ * The subclass should return by reference the popped block and
+ * its associated transactions
+ *
+ * @param blk return-by-reference the block which was popped
+ * @param txs return-by-reference the transactions from the popped block
+ */
virtual void pop_block(block& blk, std::vector<transaction>& txs);
- // return true if a transaction with hash <h> exists
+ /**
+ * @brief check if a transaction with a given hash exists
+ *
+ * The subclass should check if a transaction is stored which has the
+ * given hash and return true if so, false otherwise.
+ *
+ * @param h the hash to check against
+ * @param tx_id (optional) returns the tx_id for the tx hash
+ *
+ * @return true if the transaction exists, otherwise false
+ */
virtual bool tx_exists(const crypto::hash& h) const = 0;
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_id) const = 0;
// return unlock time of tx with hash <h>
+ /**
+ * @brief fetch a transaction's unlock time/height
+ *
+ * The subclass should return the stored unlock time for the transaction
+ * with the given hash.
+ *
+ * If no such transaction exists, the subclass should throw TX_DNE.
+ *
+ * @param h the hash of the requested transaction
+ *
+ * @return the unlock time/height
+ */
virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const = 0;
// return tx with hash <h>
// throw if no such tx exists
+ /**
+ * @brief fetches the transaction with the given hash
+ *
+ * The subclass should return the transaction stored which has the given
+ * hash.
+ *
+ * If the transaction does not exist, the subclass should throw TX_DNE.
+ *
+ * @param h the hash to look for
+ *
+ * @return the transaction with the given hash
+ */
virtual transaction get_tx(const crypto::hash& h) const = 0;
- // returns the total number of transactions in all blocks
+ /**
+ * @brief fetches the total number of transactions ever
+ *
+ * The subclass should return a count of all the transactions from
+ * all blocks.
+ *
+ * @return the number of transactions in the blockchain
+ */
virtual uint64_t get_tx_count() const = 0;
- // return list of tx with hashes <hlist>.
- // TODO: decide if a missing hash means return empty list
- // or just skip that hash
+ /**
+ * @brief fetches a list of transactions based on their hashes
+ *
+ * The subclass should attempt to fetch each transaction referred to by
+ * the hashes passed.
+ *
+ * Currently, if any of the transactions is not in BlockchainDB, the call
+ * to get_tx in the implementation will throw TX_DNE.
+ *
+ * <!-- TODO: decide if this behavior is correct for missing transactions -->
+ *
+ * @param hlist a list of hashes
+ *
+ * @return the list of transactions
+ */
virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const = 0;
// returns height of block that contains transaction with hash <h>
+ /**
+ * @brief fetches the height of a transaction's block
+ *
+ * The subclass should attempt to return the height of the block containing
+ * the transaction with the given hash.
+ *
+ * If the transaction cannot be found, the subclass should throw TX_DNE.
+ *
+ * @param h the hash of the transaction
+ *
+ * @return the height of the transaction's block
+ */
virtual uint64_t get_tx_block_height(const crypto::hash& h) const = 0;
// returns the total number of outputs of amount <amount>
+ /**
+ * @brief fetches the number of outputs of a given amount
+ *
+ * The subclass should return a count of outputs of the given amount,
+ * or zero if there are none.
+ *
+ * <!-- TODO: should outputs spent with a low mixin (especially 0) be
+ * excluded from the count? -->
+ *
+ * @param amount the output amount being looked up
+ *
+ * @return the number of outputs of the given amount
+ */
virtual uint64_t get_num_outputs(const uint64_t& amount) const = 0;
- // return index of the first element (should be hidden, but isn't)
+ /**
+ * @brief return index of the first element (should be hidden, but isn't)
+ *
+ * @return the index
+ */
virtual uint64_t get_indexing_base() const { return 0; }
- // return public key for output with global output amount <amount> and index <index>
+ /**
+ * @brief get some of an output's data
+ *
+ * The subclass should return the public key, unlock time, and block height
+ * for the output with the given amount and index, collected in a struct.
+ *
+ * If the output cannot be found, the subclass should throw OUTPUT_DNE.
+ *
+ * If any of these parts cannot be found, but some are, the subclass
+ * should throw DB_ERROR with a message stating as much.
+ *
+ * @param amount the output amount
+ * @param index the output's index (indexed by amount)
+ *
+ * @return the requested output data
+ */
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index) = 0;
+
+ /**
+ * @brief get some of an output's data
+ *
+ * The subclass should return the public key, unlock time, and block height
+ * for the output with the given global index, collected in a struct.
+ *
+ * If the output cannot be found, the subclass should throw OUTPUT_DNE.
+ *
+ * If any of these parts cannot be found, but some are, the subclass
+ * should throw DB_ERROR with a message stating as much.
+ *
+ * @param global_index the output's index (global)
+ *
+ * @return the requested output data
+ */
virtual output_data_t get_output_key(const uint64_t& global_index) const = 0;
- // returns the tx hash associated with an output, referenced by global output index
+ /**
+ * @brief gets an output's tx hash and index
+ *
+ * The subclass should return the hash of the transaction which created the
+ * output with the global index given, as well as its index in that transaction.
+ *
+ * @param index an output's global index
+ *
+ * @return the tx hash and output index
+ */
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const = 0;
- // returns the transaction-local reference for the output with <amount> at <index>
- // return type is pair of tx hash and index
+ /**
+ * @brief gets an output's tx hash and index
+ *
+ * The subclass should return the hash of the transaction which created the
+ * output with the amount and index given, as well as its index in that
+ * transaction.
+ *
+ * @param amount an output amount
+ * @param index an output's amount-specific index
+ *
+ * @return the tx hash and output index
+ */
virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) = 0;
+
+ /**
+ * @brief gets some outputs' tx hashes and indices
+ *
+ * This function is a mirror of
+ * get_output_tx_and_index(const uint64_t& amount, const uint64_t& index),
+ * but for a list of outputs rather than just one.
+ *
+ * @param amount an output amount
+ * @param offsets a list of amount-specific output indices
+ * @param indices return-by-reference a list of tx hashes and output indices (as pairs)
+ */
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices) = 0;
- virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) = 0;
+ /**
+ * @brief gets outputs' data
+ *
+ * This function is a mirror of
+ * get_output_data(const uint64_t& amount, const uint64_t& index)
+ * but for a list of outputs rather than just one.
+ *
+ * @param amount an output amount
+ * @param offsets a list of amount-specific output indices
+ * @param outputs return-by-reference a list of outputs' metadata
+ */
+ virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) = 0;
+
+ /*
+ * FIXME: Need to check with git blame and ask what this does to
+ * document it
+ */
virtual bool can_thread_bulk_indices() const = 0;
- // return a vector of indices corresponding to the amount output index for
- // each output in the transaction with ID <tx_id>
+ /**
+ * @brief gets output indices (amount-specific) for a transaction's outputs
+ *
+ * The subclass should fetch the amount-specific output indices for each
+ * output in the transaction with the given ID.
+ *
+ * If the transaction does not exist, the subclass should throw TX_DNE.
+ *
+ * If an output cannot be found, the subclass should throw OUTPUT_DNE.
+ *
+ * @param tx_id a transaction ID
+ *
+ * @return a list of amount-specific output indices
+ */
virtual std::vector<uint64_t> get_tx_amount_output_indices(const uint64_t tx_id) const = 0;
- // returns true if key image <img> is present in spent key images storage
+ /**
+ * @brief check if a key image is stored as spent
+ *
+ * @param img the key image to check for
+ *
+ * @return true if the image is present, otherwise false
+ */
virtual bool has_key_image(const crypto::key_image& img) const = 0;
+ /**
+ * @brief runs a function over all key images stored
+ *
+ * The subclass should run the passed function for each key image it has
+ * stored, passing the key image as its parameter.
+ *
+ * If any call to the function returns false, the subclass should return
+ * false. Otherwise, the subclass returns true.
+ *
+ * @param std::function fn the function to run
+ *
+ * @return false if the function returns false for any key image, otherwise true
+ */
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const = 0;
+
+ /**
+ * @brief runs a function over all blocks stored
+ *
+ * The subclass should run the passed function for each block it has
+ * stored, passing (block_height, block_hash, block) as its parameters.
+ *
+ * If any call to the function returns false, the subclass should return
+ * false. Otherwise, the subclass returns true.
+ *
+ * The subclass should throw DB_ERROR if any of the expected values are
+ * not found. Current implementations simply return false.
+ *
+ * @param std::function fn the function to run
+ *
+ * @return false if the function returns false for any block, otherwise true
+ */
virtual bool for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const = 0;
+
+ /**
+ * @brief runs a function over all transactions stored
+ *
+ * The subclass should run the passed function for each transaction it has
+ * stored, passing (transaction_hash, transaction) as its parameters.
+ *
+ * If any call to the function returns false, the subclass should return
+ * false. Otherwise, the subclass returns true.
+ *
+ * The subclass should throw DB_ERROR if any of the expected values are
+ * not found. Current implementations simply return false.
+ *
+ * @param std::function fn the function to run
+ *
+ * @return false if the function returns false for any transaction, otherwise true
+ */
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const = 0;
+
+ /**
+ * @brief runs a function over all outputs stored
+ *
+ * The subclass should run the passed function for each output it has
+ * stored, passing (amount, transaction_hash, tx_local_output_index)
+ * as its parameters.
+ *
+ * If any call to the function returns false, the subclass should return
+ * false. Otherwise, the subclass returns true.
+ *
+ * The subclass should throw DB_ERROR if any of the expected values are
+ * not found. Current implementations simply return false.
+ *
+ * @param std::function f the function to run
+ *
+ * @return false if the function returns false for any output, otherwise true
+ */
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const = 0;
+
+ //
// Hard fork related storage
+ //
+
+ // FIXME: verify that this is all correct
+ // - TW
+ /**
+ * @brief sets the height at which a hard fork has been voted to happen
+ *
+ *
+ * @param version the version voted to fork to
+ * @param height the height of the first block on the new fork
+ */
virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height) = 0;
+
+ /**
+ * @brief gets the height at which a hard fork has been voted to happen
+ *
+ * @param version the version to check
+ *
+ * @return the height at which the hard fork was accepted, if it has been,
+ * otherwise max(uint64_t)
+ */
virtual uint64_t get_hard_fork_starting_height(uint8_t version) const = 0;
+
+ /**
+ * @brief sets which hardfork version a height is on
+ *
+ * @param height the height
+ * @param version the version
+ */
virtual void set_hard_fork_version(uint64_t height, uint8_t version) = 0;
+
+ /**
+ * @brief checks which hardfork version a height is on
+ *
+ * @param height the height
+ *
+ * @return the version
+ */
virtual uint8_t get_hard_fork_version(uint64_t height) const = 0;
+
+ /**
+ * @brief verify hard fork info in database
+ */
virtual void check_hard_fork_info() = 0;
+
+ /**
+ * @brief delete hard fork info from database
+ */
virtual void drop_hard_fork_info() = 0;
+ /**
+ * @brief return a histogram of outputs on the blockchain
+ *
+ * @param amounts optional set of amounts to lookup
+ *
+ * @return a set of amount/instances
+ */
+ virtual std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts) const = 0;
+
+ /**
+ * @brief is BlockchainDB in read-only mode?
+ *
+ * @return true if in read-only mode, otherwise false
+ */
virtual bool is_read_only() const = 0;
- // fix up anything that may be wrong due to past bugs
+ // TODO: this should perhaps be (or call) a series of functions which
+ // progressively update through version updates
+ /**
+ * @brief fix up anything that may be wrong due to past bugs
+ */
virtual void fixup();
+ /**
+ * @brief set whether or not to automatically remove logs
+ *
+ * This function is only relevant for one implementation (BlockchainBDB), but
+ * is here to keep BlockchainDB users implementation-agnostic.
+ *
+ * @param auto_remove whether or not to auto-remove logs
+ */
void set_auto_remove_logs(bool auto_remove) { m_auto_remove_logs = auto_remove; }
- bool m_open;
- mutable epee::critical_section m_synchronization_lock;
+ bool m_open; //!< Whether or not the BlockchainDB is open/ready for use
+ mutable epee::critical_section m_synchronization_lock; //!< A lock, currently for when BlockchainLMDB needs to resize the backing db file
+
}; // class BlockchainDB
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index f9deca0b4..4320b12bf 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -234,7 +234,7 @@ typedef struct mdb_block_info
uint64_t bi_height;
uint64_t bi_timestamp;
uint64_t bi_coins;
- uint64_t bi_size;
+ uint64_t bi_size; // a size_t really but we need 32-bit compat
difficulty_type bi_diff;
crypto::hash bi_hash;
} mdb_block_info;
@@ -424,7 +424,9 @@ void BlockchainLMDB::do_resize(uint64_t increase_size)
mdb_txn_safe::wait_no_active_txns();
- mdb_env_set_mapsize(m_env, new_mapsize);
+ int result = mdb_env_set_mapsize(m_env, new_mapsize);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to set new mapsize: ", result).c_str()));
LOG_PRINT_GREEN("LMDB Mapsize increased." << " Old: " << mei.me_mapsize / (1024 * 1024) << "MiB" << ", New: " << new_mapsize / (1024 * 1024) << "MiB", LOG_LEVEL_0);
@@ -2569,6 +2571,63 @@ void BlockchainLMDB::get_output_tx_and_index(const uint64_t& amount, const std::
LOG_PRINT_L3("db3: " << db3);
}
+std::map<uint64_t, uint64_t> BlockchainLMDB::get_output_histogram(const std::vector<uint64_t> &amounts) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(output_amounts);
+
+ std::map<uint64_t, uint64_t> histogram;
+ MDB_val k;
+ MDB_val v;
+
+ if (amounts.empty())
+ {
+ MDB_cursor_op op = MDB_FIRST;
+ while (1)
+ {
+ int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, op);
+ op = MDB_NEXT_NODUP;
+ if (ret == MDB_NOTFOUND)
+ break;
+ if (ret)
+ throw0(DB_ERROR(lmdb_error("Failed to enumerate outputs: ", ret).c_str()));
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_output_amounts, &num_elems);
+ uint64_t amount = *(const uint64_t*)k.mv_data;
+ histogram[amount] = num_elems;
+ }
+ }
+ else
+ {
+ for (const auto &amount: amounts)
+ {
+ MDB_val_copy<uint64_t> k(amount);
+ int ret = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_SET);
+ if (ret == MDB_NOTFOUND)
+ {
+ histogram[amount] = 0;
+ }
+ else if (ret == MDB_SUCCESS)
+ {
+ mdb_size_t num_elems = 0;
+ mdb_cursor_count(m_cur_output_amounts, &num_elems);
+ histogram[amount] = num_elems;
+ }
+ else
+ {
+ throw0(DB_ERROR(lmdb_error("Failed to enumerate outputs: ", ret).c_str()));
+ }
+ }
+ }
+
+ TXN_POSTFIX_RDONLY();
+
+ return histogram;
+}
+
void BlockchainLMDB::check_hard_fork_info()
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 1f8c3bbef..8021a652c 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -258,6 +258,16 @@ public:
virtual void pop_block(block& blk, std::vector<transaction>& txs);
virtual bool can_thread_bulk_indices() const { return true; }
+
+ /**
+ * @brief return a histogram of outputs on the blockchain
+ *
+ * @param amounts optional set of amounts to lookup
+ *
+ * @return a set of amount/instances
+ */
+ std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts) const;
+
private:
void do_resize(uint64_t size_increase=0);
diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h
index 52c4fa0a8..51c17a9b6 100644
--- a/src/blockchain_utilities/blockchain_utilities.h
+++ b/src/blockchain_utilities/blockchain_utilities.h
@@ -33,12 +33,15 @@
// CONFIG: choose one of the three #define's
//
-// DB_MEMORY is a sensible default for users migrating to LMDB, as it allows
+// DB_MEMORY was a sensible default for users migrating to LMDB, as it allowed
// the exporter to use the in-memory blockchain while the other binaries
// work with LMDB, without recompiling anything.
+//
+// Now that the majority of users are on 0.9.2, and the converter has largely
+// become a generalised database tool, the default has changed to DB_LMDB.
//
-#define SOURCE_DB DB_MEMORY
-//#define SOURCE_DB DB_LMDB
+// #define SOURCE_DB DB_MEMORY
+#define SOURCE_DB DB_LMDB
// to use global compile-time setting (DB_MEMORY or DB_LMDB):
// #define SOURCE_DB BLOCKCHAIN_DB
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index b51e94ead..8a6bed31a 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/common/json_util.h b/src/common/json_util.h
new file mode 100644
index 000000000..6f8b4c18f
--- /dev/null
+++ b/src/common/json_util.h
@@ -0,0 +1,53 @@
+// 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
+
+#define GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, name, type, jtype, mandatory) \
+ type field_##name; \
+ bool field_##name##_found = false; \
+ (void)field_##name##_found; \
+ do if (json.HasMember(#name)) \
+ { \
+ if (json[#name].Is##jtype()) \
+ { \
+ field_##name = json[#name].Get##jtype(); \
+ field_##name##_found = true; \
+ } \
+ else \
+ { \
+ LOG_ERROR("Field " << #name << " found in JSON, but not " << #jtype); \
+ return false; \
+ } \
+ } \
+ else if (mandatory) \
+ { \
+ LOG_ERROR("Field " << #name << " not found in JSON"); \
+ return false; \
+ } while(0)
+
diff --git a/src/common/util.h b/src/common/util.h
index de0244c7f..7554b1df7 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -150,8 +150,8 @@ namespace tools
/*! \brief calles m_handler */
static void handle_signal(int type)
{
- static std::mutex m_mutex;
- std::unique_lock<std::mutex> lock(m_mutex);
+ static boost::mutex m_mutex;
+ boost::unique_lock<boost::mutex> lock(m_mutex);
m_handler(type);
}
diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp
deleted file mode 100644
index 458d30cc3..000000000
--- a/src/connectivity_tool/conn_tool.cpp
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright (c) 2014-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.
-//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-#include "include_base_utils.h"
-#include "version.h"
-
-using namespace epee;
-#include <boost/program_options.hpp>
-#include "p2p/p2p_protocol_defs.h"
-#include "common/command_line.h"
-#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_protocol/cryptonote_protocol_handler.h"
-#include "net/levin_client.h"
-#include "storages/levin_abstract_invoke2.h"
-#include "cryptonote_core/cryptonote_core.h"
-#include "storages/portable_storage_template_helper.h"
-#include "crypto/crypto.h"
-#include "storages/http_abstract_invoke.h"
-#include "net/http_client.h"
-
-namespace po = boost::program_options;
-using namespace cryptonote;
-using namespace nodetool;
-
-namespace
-{
- const command_line::arg_descriptor<std::string, true> arg_ip = {"ip", "set ip"};
- const command_line::arg_descriptor<size_t> arg_port = {"port", "set port"};
- const command_line::arg_descriptor<size_t> arg_rpc_port = {"rpc_port", "set rpc port"};
- const command_line::arg_descriptor<uint32_t, true> arg_timeout = {"timeout", "set timeout"};
- const command_line::arg_descriptor<std::string> arg_priv_key = {"private_key", "private key to subscribe debug command", "", true};
- const command_line::arg_descriptor<uint64_t> arg_peer_id = {"peer_id", "peer_id if known(if not - will be requested)", 0};
- const command_line::arg_descriptor<bool> arg_generate_keys = {"generate_keys_pair", "generate private and public keys pair"};
- const command_line::arg_descriptor<bool> arg_request_stat_info = {"request_stat_info", "request statistics information"};
- const command_line::arg_descriptor<bool> arg_request_net_state = {"request_net_state", "request network state information (peer list, connections count)"};
- const command_line::arg_descriptor<bool> arg_get_daemon_info = {"rpc_get_daemon_info", "request daemon state info vie rpc (--rpc_port option should be set ).", "", true};
-}
-
-typedef COMMAND_REQUEST_STAT_INFO_T<t_cryptonote_protocol_handler<core>::stat_info> COMMAND_REQUEST_STAT_INFO;
-
-struct response_schema
-{
- std::string status;
- std::string COMMAND_REQUEST_STAT_INFO_status;
- std::string COMMAND_REQUEST_NETWORK_STATE_status;
- enableable<COMMAND_REQUEST_STAT_INFO::response> si_rsp;
- enableable<COMMAND_REQUEST_NETWORK_STATE::response> ns_rsp;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
- KV_SERIALIZE(COMMAND_REQUEST_STAT_INFO_status)
- KV_SERIALIZE(COMMAND_REQUEST_NETWORK_STATE_status)
- KV_SERIALIZE(si_rsp)
- KV_SERIALIZE(ns_rsp)
- END_KV_SERIALIZE_MAP()
-};
-
- std::string get_response_schema_as_json(response_schema& rs)
- {
- std::stringstream ss;
- ss << "{" << ENDL
- << " \"status\": \"" << rs.status << "\"," << ENDL
- << " \"COMMAND_REQUEST_NETWORK_STATE_status\": \"" << rs.COMMAND_REQUEST_NETWORK_STATE_status << "\"," << ENDL
- << " \"COMMAND_REQUEST_STAT_INFO_status\": \"" << rs.COMMAND_REQUEST_STAT_INFO_status << "\"";
- if(rs.si_rsp.enabled)
- {
- ss << "," << ENDL << " \"si_rsp\": " << epee::serialization::store_t_to_json(rs.si_rsp.v, 1);
- }
- if(rs.ns_rsp.enabled)
- {
- ss << "," << ENDL << " \"ns_rsp\": {" << ENDL
- << " \"local_time\": " << rs.ns_rsp.v.local_time << "," << ENDL
- << " \"my_id\": \"" << rs.ns_rsp.v.my_id << "\"," << ENDL
- << " \"connections_list\": [" << ENDL;
-
- size_t i = 0;
- BOOST_FOREACH(const connection_entry& ce, rs.ns_rsp.v.connections_list)
- {
- ss << " {\"peer_id\": \"" << ce.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(ce.adr.ip) << "\", \"port\": " << ce.adr.port << ", \"is_income\": "<< ce.is_income << "}";
- if(rs.ns_rsp.v.connections_list.size()-1 != i)
- ss << ",";
- ss << ENDL;
- i++;
- }
- ss << " ]," << ENDL;
- ss << " \"local_peerlist_white\": [" << ENDL;
- i = 0;
- BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_white)
- {
- ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}";
- if(rs.ns_rsp.v.local_peerlist_white.size()-1 != i)
- ss << ",";
- ss << ENDL;
- i++;
- }
- ss << " ]," << ENDL;
-
- ss << " \"local_peerlist_gray\": [" << ENDL;
- i = 0;
- BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_gray)
- {
- ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}";
- if(rs.ns_rsp.v.local_peerlist_gray.size()-1 != i)
- ss << ",";
- ss << ENDL;
- i++;
- }
- ss << " ]" << ENDL << " }" << ENDL;
- }
- ss << "}";
- return std::move(ss.str());
- }
-//---------------------------------------------------------------------------------------------------------------
-bool print_COMMAND_REQUEST_STAT_INFO(const COMMAND_REQUEST_STAT_INFO::response& si)
-{
- std::cout << " ------ COMMAND_REQUEST_STAT_INFO ------ " << ENDL;
- std::cout << "Version: " << si.version << ENDL;
- std::cout << "OS Version: " << si.os_version << ENDL;
- std::cout << "Connections: " << si.connections_count << ENDL;
- std::cout << "INC Connections: " << si.incoming_connections_count << ENDL;
-
-
- std::cout << "Tx pool size: " << si.payload_info.tx_pool_size << ENDL;
- std::cout << "BC height: " << si.payload_info.blockchain_height << ENDL;
- std::cout << "Mining speed: " << si.payload_info.mining_speed << ENDL;
- std::cout << "Alternative blocks: " << si.payload_info.alternative_blocks << ENDL;
- std::cout << "Top block id: " << si.payload_info.top_block_id_str << ENDL;
- return true;
-}
-//---------------------------------------------------------------------------------------------------------------
-bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::response& ns)
-{
- std::cout << " ------ COMMAND_REQUEST_NETWORK_STATE ------ " << ENDL;
- std::cout << "Peer id: " << ns.my_id << ENDL;
- std::cout << "Active connections:" << ENDL;
- BOOST_FOREACH(const connection_entry& ce, ns.connections_list)
- {
- std::cout << ce.id << "\t" << string_tools::get_ip_string_from_int32(ce.adr.ip) << ":" << ce.adr.port << (ce.is_income ? "(INC)":"(OUT)") << ENDL;
- }
-
- std::cout << "Peer list white:" << ns.my_id << ENDL;
- BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_white)
- {
- std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL;
- }
-
- std::cout << "Peer list gray:" << ns.my_id << ENDL;
- BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_gray)
- {
- std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL;
- }
-
-
- return true;
-}
-//---------------------------------------------------------------------------------------------------------------
-bool handle_get_daemon_info(po::variables_map& vm)
-{
- if(!command_line::has_arg(vm, arg_rpc_port))
- {
- std::cout << "ERROR: rpc port not set" << ENDL;
- return false;
- }
-
- epee::net_utils::http::http_simple_client http_client;
-
- cryptonote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req);
- cryptonote::COMMAND_RPC_GET_INFO::response res = AUTO_VAL_INIT(res);
- std::string daemon_addr = command_line::get_arg(vm, arg_ip) + ":" + std::to_string(command_line::get_arg(vm, arg_rpc_port));
- bool r = net_utils::invoke_http_json_remote_command2(daemon_addr + "/getinfo", req, res, http_client, command_line::get_arg(vm, arg_timeout));
- if(!r)
- {
- std::cout << "ERROR: failed to invoke request" << ENDL;
- return false;
- }
- std::cout << "OK" << ENDL
- << "height: " << res.height << ENDL
- << "difficulty: " << res.difficulty << ENDL
- << "tx_count: " << res.tx_count << ENDL
- << "tx_pool_size: " << res.tx_pool_size << ENDL
- << "alt_blocks_count: " << res.alt_blocks_count << ENDL
- << "outgoing_connections_count: " << res.outgoing_connections_count << ENDL
- << "incoming_connections_count: " << res.incoming_connections_count << ENDL
- << "white_peerlist_size: " << res.white_peerlist_size << ENDL
- << "grey_peerlist_size: " << res.grey_peerlist_size << ENDL;
-
- return true;
-}
-//---------------------------------------------------------------------------------------------------------------
-bool handle_request_stat(po::variables_map& vm, peerid_type peer_id)
-{
-
- if(!command_line::has_arg(vm, arg_priv_key))
- {
- std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "secret key not set \"" << ENDL << "}";
- return false;
- }
- crypto::secret_key prvk = AUTO_VAL_INIT(prvk);
- if(!string_tools::hex_to_pod(command_line::get_arg(vm, arg_priv_key) , prvk))
- {
- std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}";
- return false;
- }
-
-
- response_schema rs = AUTO_VAL_INIT(rs);
-
- levin::levin_client_impl2 transport;
- if(!transport.connect(command_line::get_arg(vm, arg_ip), static_cast<int>(command_line::get_arg(vm, arg_port)), static_cast<int>(command_line::get_arg(vm, arg_timeout))))
- {
- std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}";
- return false;
- }else
- rs.status = "OK";
-
- if(!peer_id)
- {
- COMMAND_REQUEST_PEER_ID::request req = AUTO_VAL_INIT(req);
- COMMAND_REQUEST_PEER_ID::response rsp = AUTO_VAL_INIT(rsp);
- if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_PEER_ID::ID, req, rsp, transport))
- {
- std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}";
- return false;
- }else
- {
- peer_id = rsp.my_id;
- }
- }
-
-
- nodetool::proof_of_trust pot = AUTO_VAL_INIT(pot);
- pot.peer_id = peer_id;
- pot.time = time(NULL);
- crypto::public_key pubk = AUTO_VAL_INIT(pubk);
- string_tools::hex_to_pod(::config::P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY, pubk);
- crypto::hash h = tools::get_proof_of_trust_hash(pot);
- crypto::generate_signature(h, pubk, prvk, pot.sign);
-
- if(command_line::get_arg(vm, arg_request_stat_info))
- {
- COMMAND_REQUEST_STAT_INFO::request req = AUTO_VAL_INIT(req);
- req.tr = pot;
- if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_STAT_INFO::ID, req, rs.si_rsp.v, transport))
- {
- std::stringstream ss;
- ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_STAT_INFO to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port);
- rs.COMMAND_REQUEST_STAT_INFO_status = ss.str();
- }else
- {
- rs.si_rsp.enabled = true;
- rs.COMMAND_REQUEST_STAT_INFO_status = "OK";
- }
- }
-
-
- if(command_line::get_arg(vm, arg_request_net_state))
- {
- ++pot.time;
- h = tools::get_proof_of_trust_hash(pot);
- crypto::generate_signature(h, pubk, prvk, pot.sign);
- COMMAND_REQUEST_NETWORK_STATE::request req = AUTO_VAL_INIT(req);
- req.tr = pot;
- if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_NETWORK_STATE::ID, req, rs.ns_rsp.v, transport))
- {
- std::stringstream ss;
- ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_NETWORK_STATE to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port);
- rs.COMMAND_REQUEST_NETWORK_STATE_status = ss.str();
- }else
- {
- rs.ns_rsp.enabled = true;
- rs.COMMAND_REQUEST_NETWORK_STATE_status = "OK";
- }
- }
- std::cout << get_response_schema_as_json(rs);
- return true;
-}
-//---------------------------------------------------------------------------------------------------------------
-bool generate_and_print_keys()
-{
- crypto::public_key pk = AUTO_VAL_INIT(pk);
- crypto::secret_key sk = AUTO_VAL_INIT(sk);
- generate_keys(pk, sk);
- std::cout << "PUBLIC KEY: " << epee::string_tools::pod_to_hex(pk) << ENDL
- << "PRIVATE KEY: " << epee::string_tools::pod_to_hex(sk);
- return true;
-}
-int main(int argc, char* argv[])
-{
- string_tools::set_module_name_and_folder(argv[0]);
- log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0);
-
- // Declare the supported options.
- po::options_description desc_general("General options");
- command_line::add_arg(desc_general, command_line::arg_help);
-
- po::options_description desc_params("Connectivity options");
- command_line::add_arg(desc_params, arg_ip);
- command_line::add_arg(desc_params, arg_port);
- command_line::add_arg(desc_params, arg_rpc_port);
- command_line::add_arg(desc_params, arg_timeout);
- command_line::add_arg(desc_params, arg_request_stat_info);
- command_line::add_arg(desc_params, arg_request_net_state);
- command_line::add_arg(desc_params, arg_generate_keys);
- command_line::add_arg(desc_params, arg_peer_id);
- command_line::add_arg(desc_params, arg_priv_key);
- command_line::add_arg(desc_params, arg_get_daemon_info);
-
-
- po::options_description desc_all;
- desc_all.add(desc_general).add(desc_params);
-
- po::variables_map vm;
- bool r = command_line::handle_error_helper(desc_all, [&]()
- {
- po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm);
- if (command_line::get_arg(vm, command_line::arg_help))
- {
- std::cout << desc_all << ENDL;
- return false;
- }
-
- po::store(command_line::parse_command_line(argc, argv, desc_params, false), vm);
- po::notify(vm);
-
- return true;
- });
- if (!r)
- return 1;
-
- if(command_line::has_arg(vm, arg_request_stat_info) || command_line::has_arg(vm, arg_request_net_state))
- {
- return handle_request_stat(vm, command_line::get_arg(vm, arg_peer_id)) ? 0:1;
- }
- if(command_line::has_arg(vm, arg_get_daemon_info))
- {
- return handle_get_daemon_info(vm) ? 0:1;
- }
- else if(command_line::has_arg(vm, arg_generate_keys))
- {
- return generate_and_print_keys() ? 0:1;
- }
- else
- {
- std::cerr << "Not enough arguments." << ENDL;
- std::cerr << desc_all << ENDL;
- }
-
- return 1;
-}
-
diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp
index fa7b1b580..e47aab0f7 100644
--- a/src/crypto/crypto.cpp
+++ b/src/crypto/crypto.cpp
@@ -34,7 +34,8 @@
#include <cstdlib>
#include <cstring>
#include <memory>
-#include <mutex>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/lock_guard.hpp>
#include "common/varint.h"
#include "warnings.h"
@@ -52,8 +53,6 @@ namespace crypto {
using std::abort;
using std::int32_t;
using std::int64_t;
- using std::lock_guard;
- using std::mutex;
using std::size_t;
using std::uint32_t;
using std::uint64_t;
@@ -63,7 +62,7 @@ namespace crypto {
#include "random.h"
}
- mutex random_lock;
+ boost::mutex random_lock;
static inline unsigned char *operator &(ec_point &point) {
return &reinterpret_cast<unsigned char &>(point);
@@ -100,7 +99,7 @@ namespace crypto {
*
*/
secret_key crypto_ops::generate_keys(public_key &pub, secret_key &sec, const secret_key& recovery_key, bool recover) {
- lock_guard<mutex> lock(random_lock);
+ boost::lock_guard<boost::mutex> lock(random_lock);
ge_p3 point;
secret_key rng;
@@ -199,7 +198,7 @@ namespace crypto {
};
void crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) {
- lock_guard<mutex> lock(random_lock);
+ boost::lock_guard<boost::mutex> lock(random_lock);
ge_p3 tmp3;
ec_scalar k;
s_comm buf;
@@ -280,7 +279,7 @@ POP_WARNINGS
const public_key *const *pubs, size_t pubs_count,
const secret_key &sec, size_t sec_index,
signature *sig) {
- lock_guard<mutex> lock(random_lock);
+ boost::lock_guard<boost::mutex> lock(random_lock);
size_t i;
ge_p3 image_unp;
ge_dsmp image_pre;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 360b571f3..883aa521a 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -31,7 +31,8 @@
#pragma once
#include <cstddef>
-#include <mutex>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/lock_guard.hpp>
#include <vector>
#include "common/pod-class.h"
@@ -44,7 +45,7 @@ namespace crypto {
#include "random.h"
}
- extern std::mutex random_lock;
+ extern boost::mutex random_lock;
#pragma pack(push, 1)
POD_CLASS ec_point {
@@ -121,7 +122,7 @@ namespace crypto {
template<typename T>
typename std::enable_if<std::is_pod<T>::value, T>::type rand() {
typename std::remove_cv<T>::type res;
- std::lock_guard<std::mutex> lock(random_lock);
+ boost::lock_guard<boost::mutex> lock(random_lock);
generate_random_bytes(sizeof(T), &res);
return res;
}
diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md
new file mode 100644
index 000000000..eec3e21e7
--- /dev/null
+++ b/src/crypto/crypto_ops_builder/README.md
@@ -0,0 +1,21 @@
+# Monero
+
+Copyright (c) 2014-2016, The Monero Project
+
+## Crypto Ops Builder
+
+In order to ensure the safest implementation of the cryptography in use by Monero we have opted to use the SUPERCOP ref10 implementations wherever possible. The main reason is that SUPERCOP ref10 is old, well tested, and primarily the work of Daniel J. Bernstein and Tanja Lange (among others, see ```designers``` in the ref10 folder). This is particularly relevant, as the team that designed Curve25519 and EdDSA, both of which are at Monero's core, is the same team that created the SUPERCOP implementation.
+
+SUPERCOP ref10 is a fairly secure implementation that focuses on things like constant-time algorithms, to reduce side-channel attacks, sometimes at the cost of performance. However, we consider this a fair trade-off, especially considering that Monero is not that performance sensitive at this stage. In future we may consider faster implementations that still have a measure of safety against side-channel attacks.
+
+## Additional Cryptography
+
+Unfortunately SUPERCOP ref10 does not contain every function Monero's ```crypto-ops``` class needs. Thus there are several new files in the ```ref10CommentedCombined``` folder which allow for the class to be built during compilation. The original ref10 is included in the source tree in order to allow for a comparison to be made between the two, and also to allow for a quick comparison to be made between our in-source copy of SUPERCOP ref10 and an independently downloaded copy.
+
+## Usage
+
+The operation to produce the ```crypto-ops.c``` is automatic and part of the build process. If, however, you want to manually run the build process to verify the output, you can use ```MakeCryptoOps.py```.
+
+## Attribution
+
+The majority of the work we are using is from SUPERCOP, and copyrights and attribution fall to those developers and cryptographers. Beyond that we also include some of the original CryptoNote reference code. The entire build process, and all of the work analysing the functions and figuring out what comes from where, has been done by the Monero Research Lab. Shen Noether, in particular, deserves the bulk of the attribution for that.
diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/description b/src/crypto/crypto_ops_builder/ref10CommentedCombined/description
index 07bf45d05..fadc9f9af 100644
--- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/description
+++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/description
@@ -1,7 +1,7 @@
shen_ed25519_ref10
-MakeCryptoOps.py makes crypto-ops.c in the monero source from the ref10 implementation
+MakeCryptoOps.py makes crypto-ops.c in the Monero source from the ref10 implementation
EdDSA signatures using Curve25519
from http://hyperelliptic.org/ebats/supercop-20141124.tar.bz2
-commented / combined by shen noether, monero research labs
+Commented / combined by Shen Noether, Monero Research Lab
diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/designers b/src/crypto/crypto_ops_builder/ref10CommentedCombined/designers
index 63781c08f..8ee9c735f 100644
--- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/designers
+++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/designers
@@ -5,5 +5,5 @@ Tanja Lange
Peter Schwabe
Bo-Yin Yang
-MakeCryptoOps.py
-Shen Noether monero research labs
+MakeCryptoOps.py:
+Shen Noether, Monero Research Labs
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 88eea1d7e..205356797 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -31,7 +31,6 @@ set(cryptonote_core_sources
blockchain_storage.cpp
blockchain.cpp
checkpoints.cpp
- checkpoints_create.cpp
cryptonote_basic_impl.cpp
cryptonote_core.cpp
cryptonote_format_utils.cpp
@@ -49,7 +48,6 @@ set(cryptonote_core_private_headers
blockchain_storage_boost_serialization.h
blockchain.h
checkpoints.h
- checkpoints_create.h
connection_context.h
cryptonote_basic.h
cryptonote_basic_impl.h
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index f5c912d76..a0c557c79 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -49,7 +49,7 @@
#include "common/boost_serialization_helper.h"
#include "warnings.h"
#include "crypto/hash.h"
-#include "cryptonote_core/checkpoints_create.h"
+#include "cryptonote_core/checkpoints.h"
#include "cryptonote_core/cryptonote_core.h"
#if defined(PER_BLOCK_CHECKPOINT)
#include "blocks/blocks.h"
@@ -81,6 +81,9 @@ static const struct {
// version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork.
{ 2, 1009827, 0, 1442763710 },
+
+ // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21.
+ { 3, 1141317, 0, 1458558528 },
};
static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
@@ -837,7 +840,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
}
}
- //removing alt_chain entries from alternative chain
+ //removing alt_chain entries from alternative chains container
for (auto ch_ent: alt_chain)
{
m_alternative_chains.erase(ch_ent);
@@ -945,7 +948,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height)
}
//------------------------------------------------------------------
// This function validates the miner transaction reward
-bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward)
+bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version)
{
LOG_PRINT_L3("Blockchain::" << __func__);
//validate reward
@@ -954,6 +957,15 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
money_in_use += o.amount;
partial_block_reward = false;
+ if (version >= 3) {
+ for (auto &o: b.miner_tx.vout) {
+ if (!is_valid_decomposed_amount(o.amount)) {
+ LOG_PRINT_L1("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount");
+ return false;
+ }
+ }
+ }
+
std::vector<size_t> last_blocks_sizes;
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, get_current_hard_fork_version()))
@@ -981,15 +993,14 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
// to show the amount of coins that were actually generated, the remainder will be pushed back for later
// emission. This modifies the emission curve very slightly.
CHECK_AND_ASSERT_MES(money_in_use - fee <= base_reward, false, "base reward calculation bug");
- base_reward = money_in_use - fee;
if(base_reward + fee != money_in_use)
partial_block_reward = true;
+ base_reward = money_in_use - fee;
}
return true;
}
//------------------------------------------------------------------
-// get the block sizes of the last <count> blocks, starting at <from_height>
-// and return by reference <sz>.
+// get the block sizes of the last <count> blocks, and return by reference <sz>.
void Blockchain::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1405,6 +1416,10 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block
//TODO: This function *looks* like it won't need to be rewritten
// to use BlockchainDB, as it calls other functions that were,
// but it warrants some looking into later.
+//
+//FIXME: This function appears to want to return false if any transactions
+// that belong with blocks are missing, but not if blocks themselves
+// are missing.
bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1418,6 +1433,9 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
{
std::list<crypto::hash> missed_tx_ids;
std::list<transaction> txs;
+
+ // FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids
+ // is for missed blocks, not missed transactions as well.
get_transactions(bl.tx_hashes, txs, missed_tx_ids);
if (missed_tx_ids.size() != 0)
@@ -1665,6 +1683,8 @@ uint64_t Blockchain::block_difficulty(uint64_t i) const
return 0;
}
//------------------------------------------------------------------
+//TODO: return type should be void, throw on exception
+// alternatively, return true only if no blocks missed
template<class t_ids_container, class t_blocks_container, class t_missed_container>
bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const
{
@@ -1689,6 +1709,8 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container
return true;
}
//------------------------------------------------------------------
+//TODO: return type should be void, throw on exception
+// alternatively, return true only if no transactions missed
template<class t_ids_container, class t_tx_container, class t_missed_container>
bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
{
@@ -1705,7 +1727,6 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
{
missed_txs.push_back(tx_hash);
}
- //FIXME: is this the correct way to handle this?
catch (const std::exception& e)
{
return false;
@@ -1963,16 +1984,22 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<u
return true;
}
//------------------------------------------------------------------
+//FIXME: it seems this function is meant to be merely a wrapper around
+// another function of the same name, this one adding one bit of
+// functionality. Should probably move anything more than that
+// (getting the hash of the block at height max_used_block_id)
+// to the other function to keep everything in one place.
// This function overloads its sister function with
// an extra value (hash of highest block that holds an output used as input)
// as a return-by-reference.
-bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, bool kept_by_block)
+bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
#if defined(PER_BLOCK_CHECKPOINT)
// check if we're doing per-block checkpointing
+ // FIXME: investigate why this block returns
if (m_db->height() < m_blocks_hash_check.size() && kept_by_block)
{
TIME_MEASURE_START(a);
@@ -1987,7 +2014,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block
#endif
TIME_MEASURE_START(a);
- bool res = check_tx_inputs(tx, &max_used_block_height);
+ bool res = check_tx_inputs(tx, tvc, &max_used_block_height);
TIME_MEASURE_FINISH(a);
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
if(m_show_time_stats)
@@ -2006,7 +2033,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block
return true;
}
//------------------------------------------------------------------
-bool Blockchain::check_tx_outputs(const transaction& tx)
+bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context &tvc)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2015,6 +2042,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx)
if (m_hardfork->get_current_version() >= 2) {
for (auto &o: tx.vout) {
if (!is_valid_decomposed_amount(o.amount)) {
+ tvc.m_invalid_output = true;
return false;
}
}
@@ -2036,7 +2064,11 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
}
//------------------------------------------------------------------
// This function validates transaction inputs and their keys.
-bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height)
+// FIXME: consider moving functionality specific to one input into
+// check_tx_input() rather than here, and use this function simply
+// to iterate the inputs as necessary (splitting the task
+// using threads, etc.)
+bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t sig_index = 0;
@@ -2083,11 +2115,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_bloc
if (n_unmixable == 0)
{
LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and no unmixable inputs");
+ tvc.m_low_mixin = true;
return false;
}
if (n_mixable > 1)
{
LOG_PRINT_L1("Tx " << get_transaction_hash(tx) << " has too low mixin (" << mixin << "), and more than one mixable input with unmixable inputs");
+ tvc.m_low_mixin = true;
return false;
}
}
@@ -2106,7 +2140,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_bloc
std::vector < uint64_t > results;
results.resize(tx.vin.size(), 0);
- int threads = std::thread::hardware_concurrency();
+ int threads = boost::thread::hardware_concurrency();
boost::asio::io_service ioservice;
boost::thread_group threadpool;
@@ -2146,6 +2180,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_bloc
if(have_tx_keyimg_as_spent(in_to_key.k_image))
{
LOG_PRINT_L1("Key image already spent in blockchain: " << epee::string_tools::pod_to_hex(in_to_key.k_image));
+ tvc.m_double_spend = true;
return false;
}
@@ -2318,7 +2353,7 @@ bool Blockchain::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_
output_keys.clear();
- //check ring signature
+ // collect output keys
outputs_visitor vi(output_keys, *this);
if (!scan_outputkeys_for_indexes(txin, vi, tx_prefix_hash, pmax_related_block_height))
{
@@ -2615,6 +2650,11 @@ leave:
txs.push_back(tx);
TIME_MEASURE_START(dd);
+ // FIXME: the storage should not be responsible for validation.
+ // If it does any, it is merely a sanity check.
+ // Validation is the purview of the Blockchain class
+ // - TW
+ //
// ND: this is not needed, db->add_block() checks for duplicate k_images and fails accordingly.
// if (!check_for_double_spend(tx, keys))
// {
@@ -2632,7 +2672,8 @@ leave:
#endif
{
// validate that transaction inputs and the keys spending them are correct.
- if(!check_tx_inputs(tx))
+ tx_verification_context tvc;
+ if(!check_tx_inputs(tx, tvc))
{
LOG_PRINT_L1("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs.");
@@ -2672,7 +2713,7 @@ leave:
TIME_MEASURE_START(vmt);
uint64_t base_reward = 0;
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
- if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward))
+ if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version()))
{
LOG_PRINT_L1("Block with id: " << id << " has incorrect miner transaction");
bvc.m_verifivation_failed = true;
@@ -2795,10 +2836,13 @@ bool Blockchain::add_new_block(const block& bl_, block_verification_context& bvc
return handle_block_to_main_chain(bl, id, bvc);
}
//------------------------------------------------------------------
+//TODO: Refactor, consider returning a failure height and letting
+// caller decide course of action.
void Blockchain::check_against_checkpoints(const checkpoints& points, bool enforce)
{
const auto& pts = points.get_points();
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
m_db->batch_start();
for (const auto& pt : pts)
{
@@ -2831,16 +2875,16 @@ void Blockchain::check_against_checkpoints(const checkpoints& points, bool enfor
// with an existing checkpoint.
bool Blockchain::update_checkpoints(const std::string& file_path, bool check_dns)
{
- if (!cryptonote::load_checkpoints_from_json(m_checkpoints, file_path))
+ if (!m_checkpoints.load_checkpoints_from_json(file_path))
{
- return false;
+ return false;
}
// if we're checking both dns and json, load checkpoints from dns.
// if we're not hard-enforcing dns checkpoints, handle accordingly
if (m_enforce_dns_checkpoints && check_dns)
{
- if (!cryptonote::load_checkpoints_from_dns(m_checkpoints))
+ if (!m_checkpoints.load_checkpoints_from_dns())
{
return false;
}
@@ -2848,7 +2892,7 @@ bool Blockchain::update_checkpoints(const std::string& file_path, bool check_dns
else if (check_dns)
{
checkpoints dns_points;
- cryptonote::load_checkpoints_from_dns(dns_points);
+ dns_points.load_checkpoints_from_dns();
if (m_checkpoints.check_for_conflicts(dns_points))
{
check_against_checkpoints(dns_points, false);
@@ -2875,6 +2919,8 @@ void Blockchain::block_longhash_worker(const uint64_t height, const std::vector<
TIME_MEASURE_START(t);
slow_hash_allocate_state();
+ //FIXME: height should be changing here, as get_block_longhash expects
+ // the height of the block passed to it
for (const auto & block : blocks)
{
crypto::hash id = get_block_hash(block);
@@ -2930,6 +2976,7 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
}
//------------------------------------------------------------------
+//FIXME: unused parameter txs
void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, std::unordered_map<crypto::hash, cryptonote::transaction> &txs) const
{
try
@@ -2966,7 +3013,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
return true;
bool blocks_exist = false;
- uint64_t threads = std::thread::hardware_concurrency();
+ uint64_t threads = boost::thread::hardware_concurrency();
if (blocks_entry.size() > 1 && threads > 1 && m_max_prepare_blocks_threads > 1)
{
@@ -3166,7 +3213,7 @@ 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 = std::thread::hardware_concurrency();
+ threads = boost::thread::hardware_concurrency();
if (!m_db->can_thread_bulk_indices())
threads = 1;
@@ -3281,6 +3328,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);
}
+std::map<uint64_t, uint64_t> Blockchain:: get_output_histogram(const std::vector<uint64_t> &amounts) const
+{
+ return m_db->get_output_histogram(amounts);
+}
+
void Blockchain::load_compiled_in_block_hashes()
{
if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr)
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index ab2c8f9e8..a62487d1e 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -60,11 +60,14 @@ namespace cryptonote
class tx_memory_pool;
struct test_options;
+ /** Declares ways in which the BlockchainDB backend should be told to sync
+ *
+ */
enum blockchain_db_sync_mode
{
- db_sync,
- db_async,
- db_nosync
+ db_sync, //!< handle syncing calls instead of the backing db, synchronously
+ db_async, //!< handle syncing calls instead of the backing db, asynchronously
+ db_nosync //!< Leave syncing up to the backing db (safest, but slowest because of disk I/O)
};
/************************************************************************/
@@ -73,6 +76,9 @@ namespace cryptonote
class Blockchain
{
public:
+ /**
+ * @brief Now-defunct (TODO: remove) struct from in-memory blockchain
+ */
struct transaction_chain_entry
{
transaction tx;
@@ -81,127 +87,708 @@ namespace cryptonote
std::vector<uint64_t> m_global_output_indexes;
};
+ /**
+ * @brief container for passing a block and metadata about it on the blockchain
+ */
struct block_extended_info
{
- block bl;
- uint64_t height;
- size_t block_cumulative_size;
- difficulty_type cumulative_difficulty;
- uint64_t already_generated_coins;
+ block bl; //!< the block
+ uint64_t height; //!< the height of the block in the blockchain
+ size_t block_cumulative_size; //!< the size (in bytes) of the block
+ difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block
+ uint64_t already_generated_coins; //!< the total coins minted after that block
};
+ /**
+ * @brief Blockchain constructor
+ *
+ * @param tx_pool a reference to the transaction pool to be kept by the Blockchain
+ */
Blockchain(tx_memory_pool& tx_pool);
+ /**
+ * @brief Initialize the Blockchain state
+ *
+ * @param db a pointer to the backing store to use for the blockchain
+ * @param testnet true if on testnet, else false
+ * @param test_options test parameters
+ *
+ * @return true on success, false if any initialization steps fail
+ */
bool init(BlockchainDB* db, const bool testnet = false, const cryptonote::test_options *test_options = NULL);
+
+ /**
+ * @brief Initialize the Blockchain state
+ *
+ * @param db a pointer to the backing store to use for the blockchain
+ * @param hf a structure containing hardfork information
+ * @param testnet true if on testnet, else false
+ *
+ * @return true on success, false if any initialization steps fail
+ */
bool init(BlockchainDB* db, HardFork*& hf, const bool testnet = false);
+
+ /**
+ * @brief Uninitializes the blockchain state
+ *
+ * Saves to disk any state that needs to be maintained
+ *
+ * @return true on success, false if any uninitialization steps fail
+ */
bool deinit();
+ /**
+ * @brief assign a set of blockchain checkpoint hashes
+ *
+ * @param chk_pts the set of checkpoints to assign
+ */
void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; }
- //bool push_new_block();
+ /**
+ * @brief get blocks and transactions from blocks based on start height and count
+ *
+ * @param start_offset the height on the blockchain to start at
+ * @param count the number of blocks to get, if there are as many after start_offset
+ * @param blocks return-by-reference container to put result blocks in
+ * @param txs return-by-reference container to put result transactions in
+ *
+ * @return false if start_offset > blockchain height, else true
+ */
bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
+
+ /**
+ * @brief get blocks from blocks based on start height and count
+ *
+ * @param start_offset the height on the blockchain to start at
+ * @param count the number of blocks to get, if there are as many after start_offset
+ * @param blocks return-by-reference container to put result blocks in
+ *
+ * @return false if start_offset > blockchain height, else true
+ */
bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
+
+ /**
+ * @brief compiles a list of all blocks stored as alternative chains
+ *
+ * @param blocks return-by-reference container to put result blocks in
+ *
+ * @return true
+ */
bool get_alternative_blocks(std::list<block>& blocks) const;
+
+ /**
+ * @brief returns the number of alternative blocks stored
+ *
+ * @return the number of alternative blocks stored
+ */
size_t get_alternative_blocks_count() const;
+
+ /**
+ * @brief gets a block's hash given a height
+ *
+ * @param height the height of the block
+ *
+ * @return the hash of the block at the requested height, or a zeroed hash if there is no such block
+ */
crypto::hash get_block_id_by_height(uint64_t height) const;
+
+ /**
+ * @brief gets the block with a given hash
+ *
+ * @param h the hash to look for
+ * @param blk return-by-reference variable to put result block in
+ *
+ * @return true if the block was found, else false
+ */
bool get_block_by_hash(const crypto::hash &h, block &blk) const;
+
+ /**
+ * @brief get all block hashes (main chain, alt chains, and invalid blocks)
+ *
+ * @param main return-by-reference container to put result main chain blocks' hashes in
+ * @param alt return-by-reference container to put result alt chain blocks' hashes in
+ * @param invalid return-by-reference container to put result invalid blocks' hashes in
+ */
void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) const;
+
+ /**
+ * @brief performs some preprocessing on a group of incoming blocks to speed up verification
+ *
+ * @param blocks a list of incoming blocks
+ *
+ * @return false on erroneous blocks, else true
+ */
bool prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks);
+
+ /**
+ * @brief incoming blocks post-processing, cleanup, and disk sync
+ *
+ * @param force_sync if true, and Blockchain is handling syncing to disk, always sync
+ *
+ * @return true
+ */
bool cleanup_handle_incoming_blocks(bool force_sync = false);
+ /**
+ * @brief search the blockchain for a transaction by hash
+ *
+ * @param id the hash to search for
+ *
+ * @return true if the tx exists, else false
+ */
bool have_tx(const crypto::hash &id) const;
+
+ /**
+ * @brief check if any key image in a transaction has already been spent
+ *
+ * @param tx the transaction to check
+ *
+ * @return true if any key image is already spent in the blockchain, else false
+ */
bool have_tx_keyimges_as_spent(const transaction &tx) const;
+
+ /**
+ * @brief check if a key image is already spent on the blockchain
+ *
+ * Whenever a transaction output is used as an input for another transaction
+ * (a true input, not just one of a mixing set), a key image is generated
+ * and stored in the transaction in order to prevent double spending. If
+ * this key image is seen again, the transaction using it is rejected.
+ *
+ * @param key_im the key image to search for
+ *
+ * @return true if the key image is already spent in the blockchain, else false
+ */
bool have_tx_keyimg_as_spent(const crypto::key_image &key_im) const;
+ /**
+ * @brief get the current height of the blockchain
+ *
+ * @return the height
+ */
uint64_t get_current_blockchain_height() const;
+
+ /**
+ * @brief get the hash of the most recent block on the blockchain
+ *
+ * @return the hash
+ */
crypto::hash get_tail_id() const;
+
+ /**
+ * @brief get the height and hash of the most recent block on the blockchain
+ *
+ * @param height return-by-reference variable to store the height in
+ *
+ * @return the hash
+ */
crypto::hash get_tail_id(uint64_t& height) const;
+
+ /**
+ * @brief returns the difficulty target the next block to be added must meet
+ *
+ * @return the target
+ */
difficulty_type get_difficulty_for_next_block();
+
+ /**
+ * @brief adds a block to the blockchain
+ *
+ * Adds a new block to the blockchain. If the block's parent is not the
+ * current top of the blockchain, the block may be added to an alternate
+ * chain. If the block does not belong, is already in the blockchain
+ * or an alternate chain, or is invalid, return false.
+ *
+ * @param bl_ the block to be added
+ * @param bvc metadata about the block addition's success/failure
+ *
+ * @return true on successful addition to the blockchain, else false
+ */
bool add_new_block(const block& bl_, block_verification_context& bvc);
+
+ /**
+ * @brief clears the blockchain and starts a new one
+ *
+ * @param b the first block in the new chain (the genesis block)
+ *
+ * @return true on success, else false
+ */
bool reset_and_set_genesis_block(const block& b);
+
+ /**
+ * @brief creates a new block to mine against
+ *
+ * @param b return-by-reference block to be filled in
+ * @param miner_address address new coins for the block will go to
+ * @param di return-by-reference tells the miner what the difficulty target is
+ * @param height return-by-reference tells the miner what height it's mining against
+ * @param ex_nonce extra data to be added to the miner transaction's extra
+ *
+ * @return true if block template filled in successfully, else false
+ */
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce);
+
+ /**
+ * @brief checks if a block is known about with a given hash
+ *
+ * This function checks the main chain, alternate chains, and invalid blocks
+ * for a block with the given hash
+ *
+ * @param id the hash to search for
+ *
+ * @return true if the block is known, else false
+ */
bool have_block(const crypto::hash& id) const;
+
+ /**
+ * @brief gets the total number of transactions on the main chain
+ *
+ * @return the number of transactions on the main chain
+ */
size_t get_total_transactions() const;
+
+ /**
+ * @brief gets the hashes for a subset of the blockchain
+ *
+ * puts into list <ids> a list of hashes representing certain blocks
+ * from the blockchain in reverse chronological order
+ *
+ * the blocks chosen, at the time of this writing, are:
+ * the most recent 11
+ * powers of 2 less recent from there, so 13, 17, 25, etc...
+ *
+ * @param ids return-by-reference list to put the resulting hashes in
+ *
+ * @return true
+ */
bool get_short_chain_history(std::list<crypto::hash>& ids) 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
+ */
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
+
+ /**
+ * @brief find the most recent common point between ours and a foreign chain
+ *
+ * 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.
+ *
+ * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
+ * @param starter_offset return-by-reference the most recent common block's height
+ *
+ * @return true if a block found in common, else false
+ */
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, uint64_t& starter_offset) const;
+
+ /**
+ * @brief get recent blocks for a foreign chain
+ *
+ * This function gets recent blocks relative to a foreign chain, starting either at
+ * a requested height or whatever height is the most recent ours and the foreign
+ * chain have in common.
+ *
+ * @param req_start_block if non-zero, specifies a start point (otherwise find most recent commonality)
+ * @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
+ * @param blocks return-by-reference the blocks and their transactions
+ * @param total_height return-by-reference our current blockchain height
+ * @param start_height return-by-reference the height of the first block returned
+ * @param max_count the max number of blocks to get
+ *
+ * @return true if a block found in common or req_start_block specified, else false
+ */
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
+
+ /**
+ * @brief retrieves a set of blocks and their transactions, and possibly other transactions
+ *
+ * the request object encapsulates a list of block hashes and a (possibly empty) list of
+ * transaction hashes. for each block hash, the block is fetched along with all of that
+ * block's transactions. Any transactions requested separately are fetched afterwards.
+ *
+ * @param arg the request
+ * @param rsp return-by-reference the response to fill in
+ *
+ * @return true unless any blocks or transactions are missing
+ */
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp);
- bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res);
+
+ /**
+ * @brief gets random outputs to mix with
+ *
+ * This function takes an RPC request for outputs to mix with
+ * and creates an RPC response with the resultant output indices.
+ *
+ * Outputs to mix with are randomly selected from the utxo set
+ * for each output amount in the request.
+ *
+ * @param req the output amounts and number of mixins to select
+ * @param res return-by-reference the resultant output indices
+ *
+ * @return true
+ */
bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
+
+ /**
+ * @brief gets the global indices for outputs from a given transaction
+ *
+ * This function gets the global indices for all outputs belonging
+ * to a specific transaction.
+ *
+ * @param tx_id the hash of the transaction to fetch indices for
+ * @param indexs return-by-reference the global indices for the transaction's outputs
+ *
+ * @return false if the transaction does not exist, or if no indices are found, otherwise true
+ */
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
+
+ /**
+ * @brief stores the blockchain
+ *
+ * If Blockchain is handling storing of the blockchain (rather than BlockchainDB),
+ * this initiates a blockchain save.
+ *
+ * @return true unless saving the blockchain fails
+ */
bool store_blockchain();
- bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, bool kept_by_block = false);
- bool check_tx_outputs(const transaction& tx);
+ /**
+ * @brief validates a transaction's inputs
+ *
+ * validates a transaction's inputs as correctly used and not previously
+ * spent. also returns the hash and height of the most recent block
+ * which contains an output that was used as an input to the transaction.
+ *
+ * @param tx the transaction to validate
+ * @param pmax_used_block_height return-by-reference block height of most recent input
+ * @param max_used_block_id return-by-reference block hash of most recent input
+ * @param tvc returned information about tx verification
+ * @param kept_by_block whether or not the transaction is from a previously-verified block
+ *
+ * @return false if any input is invalid, otherwise true
+ */
+ bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
+
+ /**
+ * @brief check that a transaction's outputs conform to current standards
+ *
+ * This function checks, for example at the time of this writing, that
+ * each output is of the form a * 10^b (phrased differently, that if
+ * written out would have only one non-zero digit in base 10).
+ *
+ * @param tx the transaction to check the outputs of
+ * @param tvc returned info about tx verification
+ *
+ * @return false if any outputs do not conform, otherwise true
+ */
+ bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc);
+
+ /**
+ * @brief gets the blocksize limit based on recent blocks
+ *
+ * @return the limit
+ */
uint64_t get_current_cumulative_blocksize_limit() const;
+
+ /**
+ * @brief checks if the blockchain is currently being stored
+ *
+ * Note: this should be meaningless in cases where Blockchain is not
+ * directly managing saving the blockchain to disk.
+ *
+ * @return true if Blockchain is having the chain stored currently, else false
+ */
bool is_storing_blockchain()const{return m_is_blockchain_storing;}
+
+ /**
+ * @brief gets the difficulty of the block with a given height
+ *
+ * @param i the height
+ *
+ * @return the difficulty
+ */
uint64_t block_difficulty(uint64_t i) const;
+ /**
+ * @brief gets blocks based on a list of block hashes
+ *
+ * @tparam t_ids_container a standard-iterable container
+ * @tparam t_blocks_container a standard-iterable container
+ * @tparam t_missed_container a standard-iterable container
+ * @param block_ids a container of block hashes for which to get the corresponding blocks
+ * @param blocks return-by-reference a container to store result blocks in
+ * @param missed_bs return-by-reference a container to store missed blocks in
+ *
+ * @return false if an unexpected exception occurs, else true
+ */
template<class t_ids_container, class t_blocks_container, class t_missed_container>
bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const;
+ /**
+ * @brief gets transactions based on a list of transaction hashes
+ *
+ * @tparam t_ids_container a standard-iterable container
+ * @tparam t_tx_container a standard-iterable container
+ * @tparam t_missed_container a standard-iterable container
+ * @param txs_ids a container of hashes for which to get the corresponding transactions
+ * @param txs return-by-reference a container to store result transactions in
+ * @param missed_txs return-by-reference a container to store missed transactions in
+ *
+ * @return false if an unexpected exception occurs, else true
+ */
template<class t_ids_container, class t_tx_container, class t_missed_container>
bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const;
+
//debug functions
+
+ /**
+ * @brief prints data about a snippet of the blockchain
+ *
+ * if start_index is greater than the blockchain height, do nothing
+ *
+ * @param start_index height on chain to start at
+ * @param end_index height on chain to end at
+ */
void print_blockchain(uint64_t start_index, uint64_t end_index) const;
+
+ /**
+ * @brief prints every block's hash
+ *
+ * WARNING: This function will absolutely crush a terminal in prints, so
+ * it is recommended to redirect this output to a log file (or null sink
+ * if a log file is already set up, as should be the default)
+ */
void print_blockchain_index() const;
+
+ /**
+ * @brief currently does nothing, candidate for removal
+ *
+ * @param file
+ */
void print_blockchain_outs(const std::string& file) const;
+ /**
+ * @brief check the blockchain against a set of checkpoints
+ *
+ * If a block fails a checkpoint and enforce is enabled, the blockchain
+ * will be rolled back to two blocks prior to that block. If enforce
+ * is disabled, as is currently the default case with DNS-based checkpoints,
+ * an error will be printed to the user but no other action will be taken.
+ *
+ * @param points the checkpoints to check against
+ * @param enforce whether or not to take action on failure
+ */
void check_against_checkpoints(const checkpoints& points, bool enforce);
+
+ /**
+ * @brief configure whether or not to enforce DNS-based checkpoints
+ *
+ * @param enforce the new enforcement setting
+ */
void set_enforce_dns_checkpoints(bool enforce);
+
+ /**
+ * @brief loads new checkpoints from a file and optionally from DNS
+ *
+ * @param file_path the path of the file to look for and load checkpoints from
+ * @param check_dns whether or not to check for new DNS-based checkpoints
+ *
+ * @return false if any enforced checkpoint type fails to load, otherwise true
+ */
bool update_checkpoints(const std::string& file_path, bool check_dns);
+
// user options, must be called before calling init()
+
+ //FIXME: parameter names don't match function definition in .cpp file
+ /**
+ * @brief sets various performance options
+ *
+ * @param block_threads max number of threads when preparing blocks for addition
+ * @param blocks_per_sync number of blocks to cache before syncing to database
+ * @param sync_mode the ::blockchain_db_sync_mode to use
+ * @param fast_sync sync using built-in block hashes as trusted
+ */
void set_user_options(uint64_t block_threads, uint64_t blocks_per_sync,
blockchain_db_sync_mode sync_mode, bool fast_sync);
+ /**
+ * @brief set whether or not to show/print time statistics
+ *
+ * @param stats the new time stats setting
+ */
void set_show_time_stats(bool stats) { m_show_time_stats = stats; }
+ /**
+ * @brief gets the hardfork voting state object
+ *
+ * @return the HardFork object
+ */
HardFork::State get_hard_fork_state() const;
+
+ /**
+ * @brief gets the current hardfork version in use/voted for
+ *
+ * @return the version
+ */
uint8_t get_current_hard_fork_version() const { return m_hardfork->get_current_version(); }
+
+ /**
+ * @brief returns the newest hardfork version known to the blockchain
+ *
+ * @return the version
+ */
uint8_t get_ideal_hard_fork_version() const { return m_hardfork->get_ideal_version(); }
+
+ /**
+ * @brief returns the newest hardfork version voted to be enabled
+ * as of a certain height
+ *
+ * @param height the height for which to check version info
+ *
+ * @return the version
+ */
uint8_t get_ideal_hard_fork_version(uint64_t height) const { return m_hardfork->get_ideal_version(height); }
+ /**
+ * @brief get information about hardfork voting for a version
+ *
+ * @param version the version in question
+ * @param window the size of the voting window
+ * @param votes the number of votes to enable <version>
+ * @param threshold the number of votes required to enable <version>
+ * @param earliest_height the earliest height at which <version> is allowed
+ * @param voting which version this node is voting for/using
+ *
+ * @return whether the version queried is enabled
+ */
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 remove transactions from the transaction pool (if present)
+ *
+ * @param txids a list of hashes of transactions to be removed
+ *
+ * @return false if any removals fail, otherwise true
+ */
bool flush_txes_from_pool(const std::list<crypto::hash> &txids);
+ /**
+ * @brief return a histogram of outputs on the blockchain
+ *
+ * @param amounts optional set of amounts to lookup
+ *
+ * @return a set of amount/instances
+ */
+ std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts) const;
+
+ /**
+ * @brief perform a check on all key images in the blockchain
+ *
+ * @param std::function the check to perform, pass/fail
+ *
+ * @return false if any key image fails the check, otherwise true
+ */
bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
+
+ /**
+ * @brief perform a check on all blocks in the blockchain
+ *
+ * @param std::function the check to perform, pass/fail
+ *
+ * @return false if any block fails the check, otherwise true
+ */
bool for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const block&)>) const;
+
+ /**
+ * @brief perform a check on all transactions in the blockchain
+ *
+ * @param std::function the check to perform, pass/fail
+ *
+ * @return false if any transaction fails the check, otherwise true
+ */
bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const;
+
+ /**
+ * @brief perform a check on all outputs in the blockchain
+ *
+ * @param std::function the check to perform, pass/fail
+ *
+ * @return false if any output fails the check, otherwise true
+ */
bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)>) const;
+ /**
+ * @brief get a reference to the BlockchainDB in use by Blockchain
+ *
+ * @return a reference to the BlockchainDB instance
+ */
BlockchainDB& get_db()
{
return *m_db;
}
+ /**
+ * @brief get a number of outputs of a specific amount
+ *
+ * @param amount the amount
+ * @param offsets the indices (indexed to the amount) of the outputs
+ * @param outputs return-by-reference the outputs collected
+ * @param txs unused, candidate for removal
+ */
void output_scan_worker(const uint64_t amount,const std::vector<uint64_t> &offsets,
std::vector<output_data_t> &outputs, std::unordered_map<crypto::hash,
cryptonote::transaction> &txs) const;
+ /**
+ * @brief computes the "short" and "long" hashes for a set of blocks
+ *
+ * @param height the height of the first block
+ * @param blocks the blocks to be hashed
+ * @param map return-by-reference the hashes for each block
+ */
void block_longhash_worker(const uint64_t height, const std::vector<block> &blocks,
std::unordered_map<crypto::hash, crypto::hash> &map) const;
private:
+
+ // TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
+
typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container;
+
typedef std::unordered_set<crypto::key_image> key_images_container;
+
typedef std::vector<block_extended_info> blocks_container;
+
typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash;
+
typedef std::unordered_map<crypto::hash, block> blocks_by_hash;
+
typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction
+
BlockchainDB* m_db;
tx_memory_pool& m_tx_pool;
+
mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
// main chain
transactions_container m_transactions;
size_t m_current_block_cumul_sz_limit;
+ // metadata containers
std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table;
std::unordered_map<crypto::hash, std::pair<bool, uint64_t>> m_check_tx_inputs_table;
std::unordered_map<crypto::hash, crypto::hash> m_blocks_longhash_table;
@@ -243,39 +830,325 @@ namespace cryptonote
bool m_testnet;
+ /**
+ * @brief collects the keys for all outputs being "spent" as an input
+ *
+ * This function makes sure that each "input" in an input (mixins) exists
+ * and collects the public key for each from the transaction it was included in
+ * via the visitor passed to it.
+ *
+ * If pmax_related_block_height is not NULL, its value is set to the height
+ * of the most recent block which contains an output used in the input set
+ *
+ * @tparam visitor_t a class encapsulating tx is unlocked and collect tx key
+ * @param tx_in_to_key a transaction input instance
+ * @param vis an instance of the visitor to use
+ * @param tx_prefix_hash the hash of the associated transaction_prefix
+ * @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
+ *
+ * @return false if any keys are not found or any inputs are not unlocked, otherwise true
+ */
template<class visitor_t>
inline bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t &vis, const crypto::hash &tx_prefix_hash, uint64_t* pmax_related_block_height = NULL) const;
+
+ /**
+ * @brief collect output public keys of a transaction input set
+ *
+ * This function locates all outputs associated with a given input set (mixins)
+ * and validates that they exist and are usable
+ * (unlocked, unspent is checked elsewhere).
+ *
+ * If pmax_related_block_height is not NULL, its value is set to the height
+ * of the most recent block which contains an output used in the input set
+ *
+ * @param txin the transaction input
+ * @param tx_prefix_hash the transaction prefix hash, for caching organization
+ * @param sig the input signature
+ * @param output_keys return-by-reference the public keys of the outputs in the input set
+ * @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
+ *
+ * @return false if any output is not yet unlocked, or is missing, otherwise true
+ */
bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, std::vector<crypto::public_key> &output_keys, uint64_t* pmax_related_block_height);
- bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL);
+ /**
+ * @brief validate a transaction's inputs and their keys
+ *
+ * This function validates transaction inputs and their keys. Previously
+ * it also performed double spend checking, but that has been moved to its
+ * own function.
+ *
+ * If pmax_related_block_height is not NULL, its value is set to the height
+ * of the most recent block which contains an output used in any input set
+ *
+ * Currently this function calls ring signature validation for each
+ * transaction.
+ *
+ * @param tx the transaction to validate
+ * @param tvc returned information about tx verification
+ * @param pmax_related_block_height return-by-pointer the height of the most recent block in the input set
+ *
+ * @return false if any validation step fails, otherwise true
+ */
+ bool check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL);
+
+ /**
+ * @brief performs a blockchain reorganization according to the longest chain rule
+ *
+ * This function aggregates all the actions necessary to switch to a
+ * newly-longer chain. If any step in the reorganization process fails,
+ * the blockchain is reverted to its previous state.
+ *
+ * @param alt_chain the chain to switch to
+ * @param discard_disconnected_chain whether or not to keep the old chain as an alternate
+ *
+ * @return false if the reorganization fails, otherwise true
+ */
bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::iterator>& alt_chain, bool discard_disconnected_chain);
+
+ /**
+ * @brief removes the most recent block from the blockchain
+ *
+ * @return the block removed
+ */
block pop_block_from_blockchain();
+ /**
+ * @brief validate and add a new block to the end of the blockchain
+ *
+ * This function is merely a convenience wrapper around the other
+ * of the same name. This one passes the block's hash to the other
+ * as well as the block and verification context.
+ *
+ * @param bl the block to be added
+ * @param bvc metadata concerning the block's validity
+ *
+ * @return true if the block was added successfully, otherwise false
+ */
bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
+
+ /**
+ * @brief validate and add a new block to the end of the blockchain
+ *
+ * When a block is given to Blockchain to be added to the blockchain, it
+ * is passed here if it is determined to belong at the end of the current
+ * chain.
+ *
+ * @param bl the block to be added
+ * @param id the hash of the block
+ * @param bvc metadata concerning the block's validity
+ *
+ * @return true if the block was added successfully, otherwise false
+ */
bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
+
+ /**
+ * @brief validate and add a new block to an alternate blockchain
+ *
+ * If a block to be added does not belong to the main chain, but there
+ * is an alternate chain to which it should be added, that is handled
+ * here.
+ *
+ * @param b the block to be added
+ * @param id the hash of the block
+ * @param bvc metadata concerning the block's validity
+ *
+ * @return true if the block was added successfully, otherwise false
+ */
bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
+
+ /**
+ * @brief gets the difficulty requirement for a new block on an alternate chain
+ *
+ * @param alt_chain the chain to be added to
+ * @param bei the block being added (and metadata, see ::block_extended_info)
+ *
+ * @return the difficulty requirement
+ */
difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::iterator>& alt_chain, block_extended_info& bei) const;
+
+ /**
+ * @brief sanity checks a miner transaction before validating an entire block
+ *
+ * This function merely checks basic things like the structure of the miner
+ * transaction, the unlock time, and that the amount doesn't overflow.
+ *
+ * @param b the block containing the miner transaction
+ * @param height the height at which the block will be added
+ *
+ * @return false if anything is found wrong with the miner transaction, otherwise true
+ */
bool prevalidate_miner_transaction(const block& b, uint64_t height);
- bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward);
- bool validate_transaction(const block& b, uint64_t height, const transaction& tx);
+
+ /**
+ * @brief validates a miner (coinbase) transaction
+ *
+ * This function makes sure that the miner calculated his reward correctly
+ * and that his miner transaction totals reward + fee.
+ *
+ * @param b the block containing the miner transaction to be validated
+ * @param cumulative_block_size the block's size
+ * @param fee the total fees collected in the block
+ * @param base_reward return-by-reference the new block's generated coins
+ * @param already_generated_coins the amount of currency generated prior to this block
+ * @param partial_block_reward return-by-reference true if miner accepted only partial reward
+ * @param version hard fork version for that transaction
+ *
+ * @return false if anything is found wrong with the miner transaction, otherwise true
+ */
+ bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version);
+
+ /**
+ * @brief reverts the blockchain to its previous state following a failed switch
+ *
+ * If Blockchain fails to switch to an alternate chain when it means
+ * to do so, this function reverts the blockchain to how it was before
+ * the attempted switch.
+ *
+ * @param original_chain the chain to switch back to
+ * @param rollback_height the height to revert to before appending the original chain
+ *
+ * @return false if something goes wrong with reverting (very bad), otherwise true
+ */
bool rollback_blockchain_switching(std::list<block>& original_chain, uint64_t rollback_height);
- bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height);
- bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
- bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id);
+
+ /**
+ * @brief gets recent block sizes for median calculation
+ *
+ * get the block sizes of the last <count> blocks, and return by reference <sz>.
+ *
+ * @param sz return-by-reference the list of sizes
+ * @param count the number of blocks to get sizes for
+ */
void get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const;
+
+ /**
+ * @brief adds the given output to the requested set of random outputs
+ *
+ * @param result_outs return-by-reference the set the output is to be added to
+ * @param amount the output amount
+ * @param i the output index (indexed to amount)
+ */
void add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) const;
+
+ /**
+ * @brief checks if a transaction is unlocked (its outputs spendable)
+ *
+ * This function checks to see if a transaction is unlocked.
+ * unlock_time is either a block index or a unix time.
+ *
+ * @param unlock_time the unlock parameter (height or time)
+ *
+ * @return true if spendable, otherwise false
+ */
bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
+
+ /**
+ * @brief stores an invalid block in a separate container
+ *
+ * Storing invalid blocks allows quick dismissal of the same block
+ * if it is seen again.
+ *
+ * @param bl the invalid block
+ * @param h the block's hash
+ *
+ * @return false if the block cannot be stored for some reason, otherwise true
+ */
bool add_block_as_invalid(const block& bl, const crypto::hash& h);
+
+ /**
+ * @brief stores an invalid block in a separate container
+ *
+ * Storing invalid blocks allows quick dismissal of the same block
+ * if it is seen again.
+ *
+ * @param bei the invalid block (see ::block_extended_info)
+ * @param h the block's hash
+ *
+ * @return false if the block cannot be stored for some reason, otherwise true
+ */
bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h);
+
+ /**
+ * @brief checks a block's timestamp
+ *
+ * This function grabs the timestamps from the most recent <n> blocks,
+ * where n = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW. If there are not those many
+ * blocks in the blockchain, the timestap is assumed to be valid. If there
+ * are, this function returns:
+ * true if the block's timestamp is not less than the timestamp of the
+ * median of the selected blocks
+ * false otherwise
+ *
+ * @param b the block to be checked
+ *
+ * @return true if the block's timestamp is valid, otherwise false
+ */
bool check_block_timestamp(const block& b) const;
+
+ /**
+ * @brief checks a block's timestamp
+ *
+ * If the block is not more recent than the median of the recent
+ * timestamps passed here, it is considered invalid.
+ *
+ * @param timestamps a list of the most recent timestamps to check against
+ * @param b the block to be checked
+ *
+ * @return true if the block's timestamp is valid, otherwise false
+ */
bool check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b) const;
+
+ /**
+ * @brief get the "adjusted time"
+ *
+ * Currently this simply returns the current time according to the
+ * user's machine.
+ *
+ * @return the current time
+ */
uint64_t get_adjusted_time() const;
+
+ /**
+ * @brief finish an alternate chain's timestamp window from the main chain
+ *
+ * for an alternate chain, get the timestamps from the main chain to complete
+ * the needed number of timestamps for the BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW.
+ *
+ * @param start_height the alternate chain's attachment height to the main chain
+ * @param timestamps return-by-value the timestamps set to be populated
+ *
+ * @return true unless start_height is greater than the current blockchain height
+ */
bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
+
+ /**
+ * @brief calculate the block size limit for the next block to be added
+ *
+ * @return true
+ */
bool update_next_cumulative_size_limit();
void return_tx_to_pool(const std::vector<transaction> &txs);
+ /**
+ * @brief make sure a transaction isn't attempting a double-spend
+ *
+ * @param tx the transaction to check
+ * @param keys_this_block a cumulative list of spent keys for the current block
+ *
+ * @return false if a double spend was detected, otherwise true
+ */
bool check_for_double_spend(const transaction& tx, key_images_container& keys_this_block) const;
- void get_timestamp_and_difficulty(uint64_t &timestamp, difficulty_type &difficulty, const int offset) const;
+
+ /**
+ * @brief validates a transaction input's ring signature
+ *
+ * @param tx_prefix_hash the transaction prefix' hash
+ * @param key_image the key image generated from the true input
+ * @param pubkeys the public keys for each input in the ring signature
+ * @param sig the signature generated for each input in the ring signature
+ * @param result false if the ring signature is invalid, otherwise true
+ */
void check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image,
const std::vector<crypto::public_key> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp
index e1b89f887..a829b7cbe 100644
--- a/src/cryptonote_core/blockchain_storage.cpp
+++ b/src/cryptonote_core/blockchain_storage.cpp
@@ -48,7 +48,7 @@
#include "common/boost_serialization_helper.h"
#include "warnings.h"
#include "crypto/hash.h"
-#include "cryptonote_core/checkpoints_create.h"
+#include "cryptonote_core/checkpoints.h"
//#include "serialization/json_archive.h"
#include "../../contrib/otshell_utils/utils.hpp"
#include "../../src/p2p/data_logger.hpp"
@@ -1854,7 +1854,7 @@ void blockchain_storage::check_against_checkpoints(const checkpoints& points, bo
// with an existing checkpoint.
bool blockchain_storage::update_checkpoints(const std::string& file_path, bool check_dns)
{
- if (!cryptonote::load_checkpoints_from_json(m_checkpoints, file_path))
+ if (!m_checkpoints.load_checkpoints_from_json(file_path))
{
return false;
}
@@ -1863,7 +1863,7 @@ bool blockchain_storage::update_checkpoints(const std::string& file_path, bool c
// if we're not hard-enforcing dns checkpoints, handle accordingly
if (m_enforce_dns_checkpoints && check_dns)
{
- if (!cryptonote::load_checkpoints_from_dns(m_checkpoints))
+ if (!m_checkpoints.load_checkpoints_from_dns())
{
return false;
}
@@ -1871,7 +1871,7 @@ bool blockchain_storage::update_checkpoints(const std::string& file_path, bool c
else if (check_dns)
{
checkpoints dns_points;
- cryptonote::load_checkpoints_from_dns(dns_points, m_testnet);
+ dns_points.load_checkpoints_from_dns(m_testnet);
if (m_checkpoints.check_for_conflicts(dns_points))
{
check_against_checkpoints(dns_points, false);
diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp
index 24d066dae..c038a4802 100644
--- a/src/cryptonote_core/checkpoints.cpp
+++ b/src/cryptonote_core/checkpoints.cpp
@@ -1,21 +1,21 @@
// Copyright (c) 2014-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
@@ -25,14 +25,44 @@
// 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.
-//
+//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "include_base_utils.h"
+
using namespace epee;
#include "checkpoints.h"
+#include "common/dns_utils.h"
+#include "include_base_utils.h"
+#include <sstream>
+#include <random>
+
+namespace
+{
+ bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b)
+ {
+ if (a.size() != b.size()) return false;
+
+ for (const auto& record_in_a : a)
+ {
+ bool ok = false;
+ for (const auto& record_in_b : b)
+ {
+ if (record_in_a == record_in_b)
+ {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) return false;
+ }
+
+ return true;
+ }
+} // anonymous namespace
+
namespace cryptonote
{
//---------------------------------------------------------------------------
@@ -84,10 +114,7 @@ namespace cryptonote
return check_block(height, h, ignored);
}
//---------------------------------------------------------------------------
- // this basically says if the blockchain is smaller than the first
- // checkpoint then alternate blocks are allowed. Alternatively, if the
- // last checkpoint *before* the end of the current chain is also before
- // the block to be added, then this is fine.
+ //FIXME: is this the desired behavior?
bool checkpoints::is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const
{
if (0 == block_height)
@@ -128,4 +155,206 @@ namespace cryptonote
}
return true;
}
+
+ bool checkpoints::init_default_checkpoints()
+ {
+ ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148");
+ ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381");
+ ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d");
+ ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876");
+ ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2");
+ ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25");
+ ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6");
+ ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c");
+ ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3");
+ ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698");
+ ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2");
+ ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb");
+ ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063");
+ ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553");
+ ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61");
+ ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf");
+ ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131");
+ ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8");
+ ADD_CHECKPOINT(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d");
+ ADD_CHECKPOINT(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831");
+ ADD_CHECKPOINT(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02");
+ ADD_CHECKPOINT(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5");
+ ADD_CHECKPOINT(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd");
+ ADD_CHECKPOINT(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60");
+ ADD_CHECKPOINT(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337");
+ ADD_CHECKPOINT(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f");
+
+ return true;
+ }
+
+ bool checkpoints::load_checkpoints_from_json(const std::string json_hashfile_fullpath)
+ {
+ boost::system::error_code errcode;
+ if (! (boost::filesystem::exists(json_hashfile_fullpath, errcode)))
+ {
+ LOG_PRINT_L1("Blockchain checkpoints file not found");
+ return true;
+ }
+
+ LOG_PRINT_L1("Adding checkpoints from blockchain hashfile");
+
+ uint64_t prev_max_height = get_max_height();
+ LOG_PRINT_L1("Hard-coded max checkpoint height is " << prev_max_height);
+ t_hash_json hashes;
+ epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath);
+ for (std::vector<t_hashline>::const_iterator it = hashes.hashlines.begin(); it != hashes.hashlines.end(); )
+ {
+ uint64_t height;
+ height = it->height;
+ if (height <= prev_max_height) {
+ LOG_PRINT_L1("ignoring checkpoint height " << height);
+ } else {
+ std::string blockhash = it->hash;
+ LOG_PRINT_L1("Adding checkpoint height " << height << ", hash=" << blockhash);
+ ADD_CHECKPOINT(height, blockhash);
+ }
+ ++it;
+ }
+
+ return true;
+ }
+
+ bool checkpoints::load_checkpoints_from_dns(bool testnet)
+ {
+ // All four MoneroPulse domains have DNSSEC on and valid
+ static const std::vector<std::string> dns_urls = { "checkpoints.moneropulse.se"
+ , "checkpoints.moneropulse.org"
+ , "checkpoints.moneropulse.net"
+ , "checkpoints.moneropulse.co"
+ };
+
+ static const std::vector<std::string> testnet_dns_urls = { "testpoints.moneropulse.se"
+ , "testpoints.moneropulse.org"
+ , "testpoints.moneropulse.net"
+ , "testpoints.moneropulse.co"
+ };
+
+ std::vector<std::vector<std::string> > records;
+ records.resize(dns_urls.size());
+
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1);
+ size_t first_index = dis(gen);
+
+ bool avail, valid;
+ size_t cur_index = first_index;
+ do
+ {
+ std::string url;
+ if (testnet)
+ {
+ url = testnet_dns_urls[cur_index];
+ }
+ else
+ {
+ url = dns_urls[cur_index];
+ }
+
+ records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid);
+ if (!avail)
+ {
+ records[cur_index].clear();
+ LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping.");
+ }
+ if (!valid)
+ {
+ records[cur_index].clear();
+ LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping.");
+ }
+
+ cur_index++;
+ if (cur_index == dns_urls.size())
+ {
+ cur_index = 0;
+ }
+ records[cur_index].clear();
+ } while (cur_index != first_index);
+
+ size_t num_valid_records = 0;
+
+ for( const auto& record_set : records)
+ {
+ if (record_set.size() != 0)
+ {
+ num_valid_records++;
+ }
+ }
+
+ if (num_valid_records < 2)
+ {
+ LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received");
+ return true;
+ }
+
+ int good_records_index = -1;
+ for (size_t i = 0; i < records.size() - 1; ++i)
+ {
+ if (records[i].size() == 0) continue;
+
+ for (size_t j = i + 1; j < records.size(); ++j)
+ {
+ if (dns_records_match(records[i], records[j]))
+ {
+ good_records_index = i;
+ break;
+ }
+ }
+ if (good_records_index >= 0) break;
+ }
+
+ if (good_records_index < 0)
+ {
+ LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched");
+ return true;
+ }
+
+ for (auto& record : records[good_records_index])
+ {
+ auto pos = record.find(":");
+ if (pos != std::string::npos)
+ {
+ uint64_t height;
+ crypto::hash hash;
+
+ // parse the first part as uint64_t,
+ // if this fails move on to the next record
+ std::stringstream ss(record.substr(0, pos));
+ if (!(ss >> height))
+ {
+ continue;
+ }
+
+ // parse the second part as crypto::hash,
+ // if this fails move on to the next record
+ std::string hashStr = record.substr(pos + 1);
+ if (!epee::string_tools::parse_tpod_from_hex_string(hashStr, hash))
+ {
+ continue;
+ }
+
+ ADD_CHECKPOINT(height, hashStr);
+ }
+ }
+ return true;
+ }
+
+ bool checkpoints::load_new_checkpoints(const std::string json_hashfile_fullpath, bool testnet, bool dns)
+ {
+ bool result;
+
+ result = load_checkpoints_from_json(json_hashfile_fullpath);
+ if (dns)
+ {
+ result &= load_checkpoints_from_dns(testnet);
+ }
+
+ return result;
+ }
}
diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h
index 00a53ec24..71727753e 100644
--- a/src/cryptonote_core/checkpoints.h
+++ b/src/cryptonote_core/checkpoints.h
@@ -1,21 +1,21 @@
// Copyright (c) 2014-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
@@ -25,30 +25,193 @@
// 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.
-//
+//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <map>
#include <vector>
#include "cryptonote_basic_impl.h"
+#include "misc_log_ex.h"
+#include "storages/portable_storage_template_helper.h" // epee json include
+
+#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(add_checkpoint(h, hash), false);
+#define JSON_HASH_FILE_NAME "checkpoints.json"
namespace cryptonote
{
+ /**
+ * @brief A container for blockchain checkpoints
+ *
+ * A checkpoint is a pre-defined hash for the block at a given height.
+ * Some of these are compiled-in, while others can be loaded at runtime
+ * either from a json file or via DNS from a checkpoint-hosting server.
+ */
class checkpoints
{
public:
+
+ /**
+ * @brief default constructor
+ */
checkpoints();
+
+ /**
+ * @brief adds a checkpoint to the container
+ *
+ * @param height the height of the block the checkpoint is for
+ * @param hash_str the hash of the block, as a string
+ *
+ * @return false if parsing the hash fails, or if the height is a duplicate
+ * AND the existing checkpoint hash does not match the new one,
+ * otherwise returns true
+ */
bool add_checkpoint(uint64_t height, const std::string& hash_str);
+
+ /**
+ * @brief checks if there is a checkpoint in the future
+ *
+ * This function checks if the height passed is lower than the highest
+ * checkpoint.
+ *
+ * @param height the height to check against
+ *
+ * @return false if no checkpoints, otherwise returns whether or not
+ * the height passed is lower than the highest checkpoint.
+ */
bool is_in_checkpoint_zone(uint64_t height) const;
- bool check_block(uint64_t height, const crypto::hash& h) const;
+
+ /**
+ * @brief checks if the given height and hash agree with the checkpoints
+ *
+ * This function checks if the given height and hash exist in the
+ * checkpoints container. If so, it returns whether or not the passed
+ * parameters match the stored values.
+ *
+ * @param height the height to be checked
+ * @param h the hash to be checked
+ * @param is_a_checkpoint return-by-reference if there is a checkpoint at the given height
+ *
+ * @return true if there is no checkpoint at the given height,
+ * true if the passed parameters match the stored checkpoint,
+ * false otherwise
+ */
bool check_block(uint64_t height, const crypto::hash& h, bool& is_a_checkpoint) const;
+
+ /**
+ * @overload
+ */
+ bool check_block(uint64_t height, const crypto::hash& h) const;
+
+ /**
+ * @brief checks if alternate chain blocks should be kept for a given height
+ *
+ * this basically says if the blockchain is smaller than the first
+ * checkpoint then alternate blocks are allowed. Alternatively, if the
+ * last checkpoint *before* the end of the current chain is also before
+ * the block to be added, then this is fine.
+ *
+ * @param blockchain_height the current blockchain height
+ * @param block_height the height of the block to be added as alternate
+ *
+ * @return true if alternate blocks are allowed given the parameters,
+ * otherwise false
+ */
bool is_alternative_block_allowed(uint64_t blockchain_height, uint64_t block_height) const;
+
+ /**
+ * @brief gets the highest checkpoint height
+ *
+ * @return the height of the highest checkpoint
+ */
uint64_t get_max_height() const;
+
+ /**
+ * @brief gets the checkpoints container
+ *
+ * @return a const reference to the checkpoints container
+ */
const std::map<uint64_t, crypto::hash>& get_points() const;
+
+ /**
+ * @brief checks if our checkpoints container conflicts with another
+ *
+ * A conflict refers to a case where both checkpoint sets have a checkpoint
+ * for a specific height but their hashes for that height do not match.
+ *
+ * @param other the other checkpoints instance to check against
+ *
+ * @return false if any conflict is found, otherwise true
+ */
bool check_for_conflicts(const checkpoints& other) const;
+
+ /**
+ * @brief loads the default main chain checkpoints
+ *
+ * @return true unless adding a checkpoint fails
+ */
+ bool init_default_checkpoints();
+
+ /**
+ * @brief load new checkpoints
+ *
+ * Loads new checkpoints from the specified json file, as well as
+ * (optionally) from DNS.
+ *
+ * @param json_hashfile_fullpath path to the json checkpoints file
+ * @param testnet whether to load testnet checkpoints or mainnet
+ * @param dns whether or not to load DNS checkpoints
+ *
+ * @return true if loading successful and no conflicts
+ */
+ bool load_new_checkpoints(const std::string json_hashfile_fullpath, bool testnet=false, bool dns=true);
+
+ /**
+ * @brief load new checkpoints from json
+ *
+ * @param json_hashfile_fullpath path to the json checkpoints file
+ *
+ * @return true if loading successful and no conflicts
+ */
+ bool load_checkpoints_from_json(const std::string json_hashfile_fullpath);
+
+ /**
+ * @brief load new checkpoints from DNS
+ *
+ * @param testnet whether to load testnet checkpoints or mainnet
+ *
+ * @return true if loading successful and no conflicts
+ */
+ bool load_checkpoints_from_dns(bool testnet = false);
+
private:
- std::map<uint64_t, crypto::hash> m_points;
+
+
+ /**
+ * @brief struct for loading a checkpoint from json
+ */
+ struct t_hashline
+ {
+ uint64_t height; //!< the height of the checkpoint
+ std::string hash; //!< the hash for the checkpoint
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(height)
+ KV_SERIALIZE(hash)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ /**
+ * @brief struct for loading many checkpoints from json
+ */
+ struct t_hash_json {
+ std::vector<t_hashline> hashlines; //!< the checkpoint lines from the file
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(hashlines)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ std::map<uint64_t, crypto::hash> m_points; //!< the checkpoints container
+
};
}
diff --git a/src/cryptonote_core/checkpoints_create.cpp b/src/cryptonote_core/checkpoints_create.cpp
deleted file mode 100644
index 41f2321d5..000000000
--- a/src/cryptonote_core/checkpoints_create.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright (c) 2014-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.
-//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-#include "checkpoints_create.h"
-#include "common/dns_utils.h"
-#include "include_base_utils.h"
-#include <sstream>
-#include <random>
-#include "storages/portable_storage_template_helper.h" // epee json include
-
-namespace
-{
- bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b)
- {
- if (a.size() != b.size()) return false;
-
- for (const auto& record_in_a : a)
- {
- bool ok = false;
- for (const auto& record_in_b : b)
- {
- if (record_in_a == record_in_b)
- {
- ok = true;
- break;
- }
- }
- if (!ok) return false;
- }
-
- return true;
- }
-} // anonymous namespace
-
-namespace cryptonote
-{
-
-struct t_hashline
-{
- uint64_t height;
- std::string hash;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(height)
- KV_SERIALIZE(hash)
- END_KV_SERIALIZE_MAP()
-};
-
-struct t_hash_json {
- std::vector<t_hashline> hashlines;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(hashlines)
- END_KV_SERIALIZE_MAP()
-};
-
-bool create_checkpoints(cryptonote::checkpoints& checkpoints)
-{
- ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148");
- ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381");
- ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d");
- ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876");
- ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2");
- ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25");
- ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6");
- ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c");
- ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3");
- ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698");
- ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2");
- ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb");
- ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063");
- ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553");
- ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61");
- ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf");
- ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131");
- ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8");
- ADD_CHECKPOINT(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d");
- ADD_CHECKPOINT(300000, "0c1cd46df6ccff90ec4ab493281f2583c344cd62216c427628990fe9db1bb8b6");
- ADD_CHECKPOINT(400000, "1b2b0e7a30e59691491529a3d506d1ba3d6052d0f6b52198b7330b28a6f1b6ac");
- ADD_CHECKPOINT(450000, "4d098b511ca97723e81737c448343cfd4e6dadb3d8a0e757c6e4d595e6e48357");
- ADD_CHECKPOINT(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831");
- ADD_CHECKPOINT(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02");
- ADD_CHECKPOINT(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5");
- ADD_CHECKPOINT(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd");
- ADD_CHECKPOINT(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60");
- ADD_CHECKPOINT(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337");
- ADD_CHECKPOINT(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f");
-
- return true;
-}
-
-bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath)
-{
- boost::system::error_code errcode;
- if (! (boost::filesystem::exists(json_hashfile_fullpath, errcode)))
- {
- LOG_PRINT_L1("Blockchain checkpoints file not found");
- return true;
- }
-
- LOG_PRINT_L1("Adding checkpoints from blockchain hashfile");
-
- uint64_t prev_max_height = checkpoints.get_max_height();
- LOG_PRINT_L1("Hard-coded max checkpoint height is " << prev_max_height);
- t_hash_json hashes;
- epee::serialization::load_t_from_json_file(hashes, json_hashfile_fullpath);
- for (std::vector<t_hashline>::const_iterator it = hashes.hashlines.begin(); it != hashes.hashlines.end(); )
- {
- uint64_t height;
- height = it->height;
- if (height <= prev_max_height) {
- LOG_PRINT_L1("ignoring checkpoint height " << height);
- } else {
- std::string blockhash = it->hash;
- LOG_PRINT_L1("Adding checkpoint height " << height << ", hash=" << blockhash);
- ADD_CHECKPOINT(height, blockhash);
- }
- ++it;
- }
-
- return true;
-}
-
-bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, bool testnet)
-{
- // All four MoneroPulse domains have DNSSEC on and valid
- static const std::vector<std::string> dns_urls = { "checkpoints.moneropulse.se"
- , "checkpoints.moneropulse.org"
- , "checkpoints.moneropulse.net"
- , "checkpoints.moneropulse.co"
- };
-
- static const std::vector<std::string> testnet_dns_urls = { "testpoints.moneropulse.se"
- , "testpoints.moneropulse.org"
- , "testpoints.moneropulse.net"
- , "testpoints.moneropulse.co"
- };
-
- std::vector<std::vector<std::string> > records;
- records.resize(dns_urls.size());
-
- std::random_device rd;
- std::mt19937 gen(rd());
- std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1);
- size_t first_index = dis(gen);
-
- bool avail, valid;
- size_t cur_index = first_index;
- do
- {
- std::string url;
- if (testnet)
- {
- url = testnet_dns_urls[cur_index];
- }
- else
- {
- url = dns_urls[cur_index];
- }
-
- records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid);
- if (!avail)
- {
- records[cur_index].clear();
- LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping.");
- }
- if (!valid)
- {
- records[cur_index].clear();
- LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping.");
- }
-
- cur_index++;
- if (cur_index == dns_urls.size())
- {
- cur_index = 0;
- }
- records[cur_index].clear();
- } while (cur_index != first_index);
-
- size_t num_valid_records = 0;
-
- for( const auto& record_set : records)
- {
- if (record_set.size() != 0)
- {
- num_valid_records++;
- }
- }
-
- if (num_valid_records < 2)
- {
- LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received");
- return true;
- }
-
- int good_records_index = -1;
- for (size_t i = 0; i < records.size() - 1; ++i)
- {
- if (records[i].size() == 0) continue;
-
- for (size_t j = i + 1; j < records.size(); ++j)
- {
- if (dns_records_match(records[i], records[j]))
- {
- good_records_index = i;
- break;
- }
- }
- if (good_records_index >= 0) break;
- }
-
- if (good_records_index < 0)
- {
- LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched");
- return true;
- }
-
- for (auto& record : records[good_records_index])
- {
- auto pos = record.find(":");
- if (pos != std::string::npos)
- {
- uint64_t height;
- crypto::hash hash;
-
- // parse the first part as uint64_t,
- // if this fails move on to the next record
- std::stringstream ss(record.substr(0, pos));
- if (!(ss >> height))
- {
- continue;
- }
-
- // parse the second part as crypto::hash,
- // if this fails move on to the next record
- std::string hashStr = record.substr(pos + 1);
- if (!epee::string_tools::parse_tpod_from_hex_string(hashStr, hash))
- {
- continue;
- }
-
- ADD_CHECKPOINT(height, hashStr);
- }
- }
- return true;
-}
-
-bool load_new_checkpoints(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath)
-{
- // TODO: replace hard-coded url with const string or #define
- return (load_checkpoints_from_json(checkpoints, json_hashfile_fullpath) && load_checkpoints_from_dns(checkpoints));
-}
-
-} // namespace cryptonote
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 6f0fe88a4..20b9f0b0b 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -42,7 +42,7 @@ using namespace epee;
#include "cryptonote_format_utils.h"
#include "misc_language.h"
#include <csignal>
-#include "cryptonote_core/checkpoints_create.h"
+#include "cryptonote_core/checkpoints.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
#if defined(BERKELEY_DB)
@@ -159,7 +159,7 @@ namespace cryptonote
if (!m_testnet && !m_fakechain)
{
cryptonote::checkpoints checkpoints;
- if (!cryptonote::create_checkpoints(checkpoints))
+ if (!checkpoints.init_default_checkpoints())
{
throw std::runtime_error("Failed to initialize checkpoints");
}
@@ -415,7 +415,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
r = m_miner.init(vm, m_testnet);
- CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
+ CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance");
return load_state_data();
}
@@ -489,6 +489,7 @@ namespace cryptonote
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected");
tvc.m_verifivation_failed = true;
+ tvc.m_too_big = true;
return false;
}
@@ -634,11 +635,6 @@ namespace cryptonote
return m_blockchain_storage.get_total_transactions();
}
//-----------------------------------------------------------------------------------------------
- //bool core::get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys)
- //{
- // return m_blockchain_storage.get_outs(amount, pkeys);
- //}
- //-----------------------------------------------------------------------------------------------
bool core::add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed)
{
if(m_mempool.have_tx(tx_hash))
@@ -770,10 +766,6 @@ namespace cryptonote
{
m_miner.on_synchronized();
}
- //bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector<size_t>& sizes, size_t count)
- //{
- // return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count);
- //}
//-----------------------------------------------------------------------------------------------
bool core::add_new_block(const block& b, block_verification_context& bvc)
{
@@ -894,10 +886,6 @@ namespace cryptonote
return m_blockchain_storage.get_block_by_hash(h, blk);
}
//-----------------------------------------------------------------------------------------------
- //void core::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid) {
- // m_blockchain_storage.get_all_known_block_ids(main, alt, invalid);
- //}
- //-----------------------------------------------------------------------------------------------
std::string core::print_pool(bool short_format) const
{
return m_mempool.print_pool(short_format);
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 32f0b2ad4..30384209f 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -63,157 +63,739 @@ namespace cryptonote
/************************************************************************/
/* */
/************************************************************************/
+
+ /**
+ * @brief handles core cryptonote functionality
+ *
+ * This class coordinates cryptonote functionality including, but not
+ * limited to, communication among the Blockchain, the transaction pool,
+ * any miners, and the network.
+ */
class core: public i_miner_handler
{
public:
+
+ /**
+ * @brief constructor
+ *
+ * sets member variables into a usable state
+ *
+ * @param pprotocol pre-constructed protocol object to store and use
+ */
core(i_cryptonote_protocol* pprotocol);
+
+ /**
+ * @copydoc Blockchain::handle_get_objects
+ *
+ * @note see Blockchain::handle_get_objects()
+ * @param context connection context associated with the request
+ */
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context);
+
+ /**
+ * @brief calls various idle routines
+ *
+ * @note see miner::on_idle and tx_memory_pool::on_idle
+ *
+ * @return true
+ */
bool on_idle();
+
+ /**
+ * @brief handles an incoming transaction
+ *
+ * Parses an incoming transaction and, if nothing is obviously wrong,
+ * passes it along to the transaction pool
+ *
+ * @param tx_blob the tx to handle
+ * @param tvc metadata about the transaction's validity
+ * @param keeped_by_block if the transaction has been in a block
+ * @param relayed whether or not the transaction was relayed to us
+ *
+ * @return true if the transaction made it to the transaction pool, otherwise false
+ */
bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed);
+
+ /**
+ * @brief handles an incoming block
+ *
+ * periodic update to checkpoints is triggered here
+ * Attempts to add the block to the Blockchain and, on success,
+ * optionally updates the miner's block template.
+ *
+ * @param block_blob the block to be added
+ * @param bvc return-by-reference metadata context about the block's validity
+ * @param update_miner_blocktemplate whether or not to update the miner's block template
+ *
+ * @return false if loading new checkpoints fails, or the block is not
+ * added, otherwise true
+ */
bool handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate = true);
+
+ /**
+ * @copydoc Blockchain::prepare_handle_incoming_blocks
+ *
+ * @note see Blockchain::prepare_handle_incoming_blocks
+ */
bool prepare_handle_incoming_blocks(const std::list<block_complete_entry> &blocks);
- bool cleanup_handle_incoming_blocks(bool force_sync = false);
+ /**
+ * @copydoc Blockchain::cleanup_handle_incoming_blocks
+ *
+ * @note see Blockchain::cleanup_handle_incoming_blocks
+ */
+ bool cleanup_handle_incoming_blocks(bool force_sync = false);
+
+ /**
+ * @brief check the size of a block against the current maximum
+ *
+ * @param block_blob the block to check
+ *
+ * @return whether or not the block is too big
+ */
bool check_incoming_block_size(const blobdata& block_blob) const;
+
+ /**
+ * @brief get the cryptonote protocol instance
+ *
+ * @return the instance
+ */
i_cryptonote_protocol* get_protocol(){return m_pprotocol;}
//-------------------- i_miner_handler -----------------------
+
+ /**
+ * @brief stores and relays a block found by a miner
+ *
+ * Updates the miner's target block, attempts to store the found
+ * block in Blockchain, and -- on success -- relays that block to
+ * the network.
+ *
+ * @param b the block found
+ *
+ * @return true if the block was added to the main chain, otherwise false
+ */
virtual bool handle_block_found( block& b);
+
+ /**
+ * @copydoc Blockchain::create_block_template
+ *
+ * @note see Blockchain::create_block_template
+ */
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce);
+ /**
+ * @brief gets the miner instance
+ *
+ * @return a reference to the miner instance
+ */
miner& get_miner(){return m_miner;}
+
+ /**
+ * @brief gets the miner instance (const)
+ *
+ * @return a const reference to the miner instance
+ */
const miner& get_miner()const{return m_miner;}
+
+ /**
+ * @brief adds command line options to the given options set
+ *
+ * As of now, there are no command line options specific to core,
+ * so this function simply returns.
+ *
+ * @param desc return-by-reference the command line options set to add to
+ */
static void init_options(boost::program_options::options_description& desc);
+
+ /**
+ * @brief initializes the core as needed
+ *
+ * This function initializes the transaction pool, the Blockchain, and
+ * a miner instance with parameters given on the command line (or defaults)
+ *
+ * @param vm command line parameters
+ * @param test_options configuration options for testing
+ *
+ * @return false if one of the init steps fails, otherwise true
+ */
bool init(const boost::program_options::variables_map& vm, const test_options *test_options = NULL);
+
+ /**
+ * @copydoc Blockchain::reset_and_set_genesis_block
+ *
+ * @note see Blockchain::reset_and_set_genesis_block
+ */
bool set_genesis_block(const block& b);
+
+ /**
+ * @brief performs safe shutdown steps for core and core components
+ *
+ * Uninitializes the miner instance, transaction pool, and Blockchain
+ *
+ * if m_fast_exit is set, the call to Blockchain::deinit() is not made.
+ *
+ * @return true
+ */
bool deinit();
+
+ /**
+ * @brief sets fast exit flag
+ *
+ * @note see deinit()
+ */
static void set_fast_exit();
+
+ /**
+ * @brief gets the current state of the fast exit flag
+ *
+ * @return the fast exit flag
+ *
+ * @note see deinit()
+ */
static bool get_fast_exit();
+
+ /**
+ * @brief sets to drop blocks downloaded (for testing)
+ */
void test_drop_download();
+
+ /**
+ * @brief sets to drop blocks downloaded below a certain height
+ *
+ * @param height height below which to drop blocks
+ */
void test_drop_download_height(uint64_t height);
+
+ /**
+ * @brief gets whether or not to drop blocks (for testing)
+ *
+ * @return whether or not to drop blocks
+ */
bool get_test_drop_download() const;
+
+ /**
+ * @brief gets whether or not to drop blocks
+ *
+ * If the current blockchain height <= our block drop threshold
+ * and test drop blocks is set, return true
+ *
+ * @return see above
+ */
bool get_test_drop_download_height() const;
+
+ /**
+ * @copydoc Blockchain::get_current_blockchain_height
+ *
+ * @note see Blockchain::get_current_blockchain_height()
+ */
uint64_t get_current_blockchain_height() const;
- bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id) const;
+
+ /**
+ * @brief get the hash and height of the most recent block
+ *
+ * @param height return-by-reference height of the block
+ * @param top_id return-by-reference hash of the block
+ *
+ * @return true
+ */
+ bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) const;
+
+ /**
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const
+ *
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const
+ */
bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
+
+ /**
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const
+ *
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const
+ */
bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
+
+ /**
+ * @copydoc Blockchain::get_blocks(const t_ids_container&, t_blocks_container&, t_missed_container&) const
+ *
+ * @note see Blockchain::get_blocks(const t_ids_container&, t_blocks_container&, t_missed_container&) const
+ */
template<class t_ids_container, class t_blocks_container, class t_missed_container>
bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) const
{
return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs);
}
+
+ /**
+ * @copydoc Blockchain::get_block_id_by_height
+ *
+ * @note see Blockchain::get_block_id_by_height
+ */
crypto::hash get_block_id_by_height(uint64_t height) const;
+
+ /**
+ * @copydoc Blockchain::get_transactions
+ *
+ * @note see Blockchain::get_transactions
+ */
bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const;
+
+ /**
+ * @copydoc Blockchain::get_block_by_hash
+ *
+ * @note see Blockchain::get_block_by_hash
+ */
bool get_block_by_hash(const crypto::hash &h, block &blk) const;
- //void get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid);
+ /**
+ * @copydoc Blockchain::get_alternative_blocks
+ *
+ * @note see Blockchain::get_alternative_blocks(std::list<block>&) const
+ */
bool get_alternative_blocks(std::list<block>& blocks) const;
+
+ /**
+ * @copydoc Blockchain::get_alternative_blocks_count
+ *
+ * @note see Blockchain::get_alternative_blocks_count() const
+ */
size_t get_alternative_blocks_count() const;
+ /**
+ * @brief set the pointer to the cryptonote protocol object to use
+ *
+ * @param pprotocol the pointer to set ours as
+ */
void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol);
+
+ /**
+ * @copydoc Blockchain::set_checkpoints
+ *
+ * @note see Blockchain::set_checkpoints()
+ */
void set_checkpoints(checkpoints&& chk_pts);
+
+ /**
+ * @brief set the file path to read from when loading checkpoints
+ *
+ * @param path the path to set ours as
+ */
void set_checkpoints_file_path(const std::string& path);
+
+ /**
+ * @brief set whether or not we enforce DNS checkpoints
+ *
+ * @param enforce_dns enforce DNS checkpoints or not
+ */
void set_enforce_dns_checkpoints(bool enforce_dns);
+ /**
+ * @copydoc tx_memory_pool::get_transactions
+ *
+ * @note see tx_memory_pool::get_transactions
+ */
bool get_pool_transactions(std::list<transaction>& txs) const;
+
+ /**
+ * @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info
+ *
+ * @note see tx_memory_pool::get_pool_transactions_and_spent_keys_info
+ */
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_transactions_count
+ *
+ * @note see tx_memory_pool::get_transactions_count
+ */
size_t get_pool_transactions_count() const;
+
+ /**
+ * @copydoc Blockchain::get_total_transactions
+ *
+ * @note see Blockchain::get_total_transactions
+ */
size_t get_blockchain_total_transactions() const;
- //bool get_outs(uint64_t amount, std::list<crypto::public_key>& pkeys);
+
+ /**
+ * @copydoc Blockchain::have_block
+ *
+ * @note see Blockchain::have_block
+ */
bool have_block(const crypto::hash& id) const;
+
+ /**
+ * @copydoc Blockchain::get_short_chain_history
+ *
+ * @note see Blockchain::get_short_chain_history
+ */
bool get_short_chain_history(std::list<crypto::hash>& ids) const;
+
+ /**
+ * @copydoc Blockchain::find_blockchain_supplement(const std::list<crypto::hash>&, NOTIFY_RESPONSE_CHAIN_ENTRY::request&) const
+ *
+ * @note see Blockchain::find_blockchain_supplement(const std::list<crypto::hash>&, NOTIFY_RESPONSE_CHAIN_ENTRY::request&) const
+ */
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
+
+ /**
+ * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const
+ *
+ * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const
+ */
bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
+
+ /**
+ * @brief gets some stats about the daemon
+ *
+ * @param st_inf return-by-reference container for the stats requested
+ *
+ * @return true
+ */
bool get_stat_info(core_stat_info& st_inf) const;
- //bool get_backward_blocks_sizes(uint64_t from_height, std::vector<size_t>& sizes, size_t count);
+
+ /**
+ * @copydoc Blockchain::get_tx_outputs_gindexs
+ *
+ * @note see Blockchain::get_tx_outputs_gindexs
+ */
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs) const;
+
+ /**
+ * @copydoc Blockchain::get_tail_id
+ *
+ * @note see Blockchain::get_tail_id
+ */
crypto::hash get_tail_id() const;
+
+ /**
+ * @copydoc Blockchain::get_random_outs_for_amounts
+ *
+ * @note see Blockchain::get_random_outs_for_amounts
+ */
bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) const;
+
+
+ /**
+ * @copydoc miner::pause
+ *
+ * @note see miner::pause
+ */
void pause_mine();
+
+ /**
+ * @copydoc miner::resume
+ *
+ * @note see miner::resume
+ */
void resume_mine();
+
#if BLOCKCHAIN_DB == DB_LMDB
+ /**
+ * @brief gets the Blockchain instance
+ *
+ * @return a reference to the Blockchain instance
+ */
Blockchain& get_blockchain_storage(){return m_blockchain_storage;}
+
+ /**
+ * @brief gets the Blockchain instance (const)
+ *
+ * @return a const reference to the Blockchain instance
+ */
const Blockchain& get_blockchain_storage()const{return m_blockchain_storage;}
#else
blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;}
const blockchain_storage& get_blockchain_storage()const{return m_blockchain_storage;}
#endif
- //debug functions
+
+ /**
+ * @copydoc Blockchain::print_blockchain
+ *
+ * @note see Blockchain::print_blockchain
+ */
void print_blockchain(uint64_t start_index, uint64_t end_index) const;
+
+ /**
+ * @copydoc Blockchain::print_blockchain_index
+ *
+ * @note see Blockchain::print_blockchain_index
+ */
void print_blockchain_index() const;
+
+ /**
+ * @copydoc tx_memory_pool::print_pool
+ *
+ * @note see tx_memory_pool::print_pool
+ */
std::string print_pool(bool short_format) const;
+
+ /**
+ * @copydoc Blockchain::print_blockchain_outs
+ *
+ * @note see Blockchain::print_blockchain_outs
+ */
void print_blockchain_outs(const std::string& file);
+
+ /**
+ * @copydoc miner::on_synchronized
+ *
+ * @note see miner::on_synchronized
+ */
void on_synchronized();
+ /**
+ * @brief sets the target blockchain height
+ *
+ * @param target_blockchain_height the height to set
+ */
void set_target_blockchain_height(uint64_t target_blockchain_height);
+
+ /**
+ * @brief gets the target blockchain height
+ *
+ * @param target_blockchain_height the target height
+ */
uint64_t get_target_blockchain_height() const;
+ /**
+ * @brief tells the Blockchain to update its checkpoints
+ *
+ * This function will check if enough time has passed since the last
+ * time checkpoints were updated and tell the Blockchain to update
+ * its checkpoints if it is time. If updating checkpoints fails,
+ * the daemon is told to shut down.
+ *
+ * @note see Blockchain::update_checkpoints()
+ */
bool update_checkpoints();
+ /**
+ * @brief tells the daemon to wind down operations and stop running
+ *
+ * Currently this function raises SIGTERM, allowing the installed signal
+ * handlers to do the actual stopping.
+ */
+ void graceful_exit();
+
+ /**
+ * @brief stops the daemon running
+ *
+ * @note see graceful_exit()
+ */
void stop();
+ /**
+ * @copydoc Blockchain::have_tx_keyimg_as_spent
+ *
+ * @note see Blockchain::have_tx_keyimg_as_spent
+ */
bool is_key_image_spent(const crypto::key_image& key_im) const;
+
+ /**
+ * @brief check if multiple key images are spent
+ *
+ * plural version of is_key_image_spent()
+ *
+ * @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(const std::vector<crypto::key_image>& key_im, std::vector<bool> &spent) const;
private:
+
+ /**
+ * @copydoc add_new_tx(const transaction&, tx_verification_context&, bool)
+ *
+ * @param tx_hash the transaction's hash
+ * @param tx_prefix_hash the transaction prefix' hash
+ * @param blob_size the size of the transaction
+ * @param relayed whether or not the transaction was relayed to us
+ *
+ */
bool add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed);
+
+ /**
+ * @brief add a new transaction to the transaction pool
+ *
+ * Adds a new transaction to the transaction pool.
+ *
+ * @param tx the transaction to add
+ * @param tvc return-by-reference metadata about the transaction's verification process
+ * @param keeped_by_block whether or not the transaction has been in a block
+ * @param relayed whether or not the transaction was relayed to us
+ *
+ * @return true if the transaction is already in the transaction pool,
+ * is already in a block on the Blockchain, or is successfully added
+ * to the transaction pool
+ */
bool add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed);
+
+ /**
+ * @copydoc Blockchain::add_new_block
+ *
+ * @note see Blockchain::add_new_block
+ */
bool add_new_block(const block& b, block_verification_context& bvc);
+
+ /**
+ * @brief load any core state stored on disk
+ *
+ * currently does nothing, but may have state to load in the future.
+ *
+ * @return true
+ */
bool load_state_data();
+
+ /**
+ * @copydoc parse_tx_from_blob(transaction&, crypto::hash&, crypto::hash&, const blobdata&) const
+ *
+ * @note see parse_tx_from_blob(transaction&, crypto::hash&, crypto::hash&, const blobdata&) const
+ */
bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) const;
+ /**
+ * @brief check a transaction's syntax
+ *
+ * For now this does nothing, but it may check something about the tx
+ * in the future.
+ *
+ * @param tx the transaction to check
+ *
+ * @return true
+ */
bool check_tx_syntax(const transaction& tx) const;
- //check correct values, amounts and all lightweight checks not related with database
+
+ /**
+ * @brief validates some simple properties of a transaction
+ *
+ * Currently checks: tx has inputs,
+ * tx inputs all of supported type(s),
+ * tx outputs valid (type, key, amount),
+ * input and output total amounts don't overflow,
+ * output amount <= input amount,
+ * tx not too large,
+ * each input has a different key image.
+ *
+ * @param tx the transaction to check
+ * @param keeped_by_block if the transaction has been in a block
+ *
+ * @return true if all the checks pass, otherwise false
+ */
bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const;
- //check if tx already in memory pool or in main blockchain
- bool check_tx_ring_signature(const txin_to_key& tx, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig) const;
- bool is_tx_spendtime_unlocked(uint64_t unlock_time) const;
+ /**
+ * @copydoc miner::on_block_chain_update
+ *
+ * @note see miner::on_block_chain_update
+ *
+ * @return true
+ */
bool update_miner_block_template();
+
+ /**
+ * @brief act on a set of command line options given
+ *
+ * @param vm the command line options
+ *
+ * @return true
+ */
bool handle_command_line(const boost::program_options::variables_map& vm);
- bool on_update_blocktemplate_interval();
+
+ /**
+ * @brief verify that each input key image in a transaction is unique
+ *
+ * @param tx the transaction to check
+ *
+ * @return false if any key image is repeated, otherwise true
+ */
bool check_tx_inputs_keyimages_diff(const transaction& tx) const;
- void graceful_exit();
+
+ /**
+ * @brief checks HardFork status and prints messages about it
+ *
+ * Checks the status of HardFork and logs/prints if an update to
+ * the daemon is necessary.
+ *
+ * @note see Blockchain::get_hard_fork_state and HardFork::State
+ *
+ * @return true
+ */
bool check_fork_time();
+
+ /**
+ * @brief attempts to relay any transactions in the mempool which need it
+ *
+ * @return true
+ */
bool relay_txpool_transactions();
+
+ /**
+ * @brief locks a file in the BlockchainDB directory
+ *
+ * @param path the directory in which to place the file
+ *
+ * @return true if lock acquired successfully, otherwise false
+ */
bool lock_db_directory(const boost::filesystem::path &path);
+
+ /**
+ * @brief unlocks the db directory
+ *
+ * @note see lock_db_directory()
+ *
+ * @return true
+ */
bool unlock_db_directory();
- static std::atomic<bool> m_fast_exit;
- bool m_test_drop_download = true;
- uint64_t m_test_drop_download_height = 0;
+ static std::atomic<bool> m_fast_exit; //!< whether or not to deinit Blockchain on exit
+
+ bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
- tx_memory_pool m_mempool;
+ uint64_t m_test_drop_download_height = 0; //!< height under which to drop incoming blocks, if doing so
+
+ tx_memory_pool m_mempool; //!< transaction pool instance
#if BLOCKCHAIN_DB == DB_LMDB
- Blockchain m_blockchain_storage;
+ Blockchain m_blockchain_storage; //!< Blockchain instance
#else
blockchain_storage m_blockchain_storage;
#endif
- i_cryptonote_protocol* m_pprotocol;
- epee::critical_section m_incoming_tx_lock;
+
+ i_cryptonote_protocol* m_pprotocol; //!< cryptonote protocol instance
+
+ epee::critical_section m_incoming_tx_lock; //!< incoming transaction lock
+
//m_miner and m_miner_addres are probably temporary here
- miner m_miner;
- account_public_address m_miner_address;
- std::string m_config_folder;
- cryptonote_protocol_stub m_protocol_stub;
- epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval;
- epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner;
+ miner m_miner; //!< miner instance
+ account_public_address m_miner_address; //!< address to mine to (for miner instance)
+
+ std::string m_config_folder; //!< folder to look in for configs and other files
+
+ cryptonote_protocol_stub m_protocol_stub; //!< cryptonote protocol stub instance
+
+ epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled
+ epee::math_helper::once_a_time_seconds<60*60*2, false> m_fork_moaner; //!< interval for checking HardFork status
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
+
friend class tx_validate_inputs;
- std::atomic<bool> m_starter_message_showed;
+ std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?
+
+ uint64_t m_target_blockchain_height; //!< blockchain height target
+
+ bool m_testnet; //!< are we on testnet?
- uint64_t m_target_blockchain_height;
+ bool m_fakechain; //!< are we using a fake chain (for testing purposes)?
- bool m_testnet;
- bool m_fakechain;
- std::string m_checkpoints_path;
- time_t m_last_dns_checkpoints_update;
- time_t m_last_json_checkpoints_update;
+ std::string m_checkpoints_path; //!< path to json checkpoints file
+ time_t m_last_dns_checkpoints_update; //!< time when dns checkpoints were last updated
+ time_t m_last_json_checkpoints_update; //!< time when json checkpoints were last updated
- std::atomic_flag m_checkpoints_updating;
+ std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once
- boost::interprocess::file_lock db_lock;
+ boost::interprocess::file_lock db_lock; //!< a lock object for a file lock in the db directory
};
}
diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp
index 253e568b5..94f3d51d2 100644
--- a/src/cryptonote_core/cryptonote_format_utils.cpp
+++ b/src/cryptonote_core/cryptonote_format_utils.cpp
@@ -126,31 +126,26 @@ namespace cryptonote
return false;
}
- // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
- // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
- // emission schedule
- if (hard_fork_version >= 2)
- {
- block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
- }
-
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
LOG_PRINT_L1("Creating block template: reward " << block_reward <<
", fee " << fee)
#endif
block_reward += fee;
+ // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
+ // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
+ // emission schedule
+ if (hard_fork_version >= 2) {
+ block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
+ }
+
std::vector<uint64_t> out_amounts;
decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
- while (max_outs < out_amounts.size())
- {
- out_amounts[out_amounts.size() - 2] += out_amounts.back();
- out_amounts.resize(out_amounts.size() - 1);
- }
+ CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded");
uint64_t summary_amounts = 0;
for (size_t no = 0; no < out_amounts.size(); no++)
diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp
index 236e84481..54da77392 100644
--- a/src/cryptonote_core/difficulty.cpp
+++ b/src/cryptonote_core/difficulty.cpp
@@ -116,8 +116,8 @@ namespace cryptonote {
return !carry;
}
- difficulty_type next_difficulty(vector<uint64_t> timestamps, vector<difficulty_type> cumulative_difficulties, size_t target_seconds) {
- //cutoff DIFFICULTY_LAG
+ difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) {
+
if(timestamps.size() > DIFFICULTY_WINDOW)
{
timestamps.resize(DIFFICULTY_WINDOW);
@@ -151,6 +151,8 @@ namespace cryptonote {
assert(total_work > 0);
uint64_t low, high;
mul(total_work, target_seconds, low, high);
+ // blockchain errors "difficulty overhead" if this function returns zero.
+ // TODO: consider throwing an exception instead
if (high != 0 || low + time_span - 1 < low) {
return 0;
}
diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_core/difficulty.h
index d49c2f3b8..910f97035 100644
--- a/src/cryptonote_core/difficulty.h
+++ b/src/cryptonote_core/difficulty.h
@@ -39,6 +39,18 @@ namespace cryptonote
{
typedef std::uint64_t difficulty_type;
+ /**
+ * @brief checks if a hash fits the given difficulty
+ *
+ * The hash passes if (hash * difficulty) < 2^192.
+ * Phrased differently, if (hash * difficulty) fits without overflow into
+ * the least significant 192 bits of the 256 bit multiplication result.
+ *
+ * @param hash the hash to check
+ * @param difficulty the difficulty to check against
+ *
+ * @return true if valid, else false
+ */
bool check_hash(const crypto::hash &hash, difficulty_type difficulty);
difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
}
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 5d67acdd2..a06826163 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -54,6 +54,11 @@ namespace cryptonote
{
namespace
{
+ //TODO: constants such as these should at least be in the header,
+ // but probably somewhere more accessible to the rest of the
+ // codebase. As it stands, it is at best nontrivial to test
+ // whether or not changing these parameters (or adding new)
+ // will work correctly.
size_t const TRANSACTION_SIZE_LIMIT_V1 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
size_t const TRANSACTION_SIZE_LIMIT_V2 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
time_t const MIN_RELAY_TIME = (60 * 5); // only start re-relaying transactions after that many seconds
@@ -97,6 +102,7 @@ namespace cryptonote
if(!check_inputs_types_supported(tx))
{
tvc.m_verifivation_failed = true;
+ tvc.m_invalid_input = true;
return false;
}
@@ -113,17 +119,20 @@ namespace cryptonote
{
LOG_PRINT_L1("transaction use more money then it has: use " << print_money(outputs_amount) << ", have " << print_money(inputs_amount));
tvc.m_verifivation_failed = true;
+ tvc.m_overspend = true;
return false;
}
+ // fee per kilobyte, size rounded up.
uint64_t fee = inputs_amount - outputs_amount;
uint64_t needed_fee = blob_size / 1024;
needed_fee += (blob_size % 1024) ? 1 : 0;
needed_fee *= FEE_PER_KB;
- if (!kept_by_block && fee < needed_fee /*&& fee < MINING_ALLOWED_LEGACY_FEE*/)
+ if (!kept_by_block && fee < needed_fee)
{
LOG_PRINT_L1("transaction fee is not enough: " << print_money(fee) << ", minimum fee: " << print_money(needed_fee));
tvc.m_verifivation_failed = true;
+ tvc.m_fee_too_low = true;
return false;
}
@@ -132,40 +141,46 @@ namespace cryptonote
{
LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << tx_size_limit);
tvc.m_verifivation_failed = true;
+ tvc.m_too_big = true;
return false;
}
- //check key images for transaction if it is not kept by block
+ // if the transaction came from a block popped from the chain,
+ // don't check if we have its key images as spent.
+ // TODO: Investigate why not?
if(!kept_by_block)
{
if(have_tx_keyimges_as_spent(tx))
{
LOG_PRINT_L1("Transaction with id= "<< id << " used already spent key images");
tvc.m_verifivation_failed = true;
+ tvc.m_double_spend = true;
return false;
}
}
- if (!m_blockchain.check_tx_outputs(tx))
+ if (!m_blockchain.check_tx_outputs(tx, tvc))
{
LOG_PRINT_L1("Transaction with id= "<< id << " has at least one invalid outout");
tvc.m_verifivation_failed = true;
+ tvc.m_invalid_output = true;
return false;
}
crypto::hash max_used_block_id = null_hash;
uint64_t max_used_block_height = 0;
#if BLOCKCHAIN_DB == DB_LMDB
- bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, kept_by_block);
+ bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block);
#else
bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id);
#endif
CRITICAL_REGION_LOCAL(m_transactions_lock);
if(!ch_inp_res)
{
+ // if the transaction was valid before (kept_by_block), then it
+ // may become valid again, so ignore the failed inputs check.
if(kept_by_block)
{
- //anyway add this transaction to pool, because it related to block
auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details()));
CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool");
txd_p.first->second.blob_size = blob_size;
@@ -207,13 +222,14 @@ namespace cryptonote
tvc.m_should_be_relayed = true;
}
+ // assume failure during verification steps until success is certain
tvc.m_verifivation_failed = true;
- //update image_keys container, here should everything goes ok.
+
BOOST_FOREACH(const auto& in, tx.vin)
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false);
std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image];
- CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: keeped_by_block=" << kept_by_block
+ CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: kept_by_block=" << kept_by_block
<< ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL
<< "tx_id=" << id );
auto ins_res = kei_image_set.insert(id);
@@ -223,7 +239,7 @@ namespace cryptonote
tvc.m_verifivation_failed = false;
m_txs_by_fee.emplace((double)blob_size / fee, id);
- //succeed
+
return true;
}
//---------------------------------------------------------------------------------
@@ -235,6 +251,9 @@ namespace cryptonote
return add_tx(tx, h, blob_size, tvc, keeped_by_block, relayed, version);
}
//---------------------------------------------------------------------------------
+ //FIXME: Can return early before removal of all of the key images.
+ // At the least, need to make sure that a false return here
+ // is treated properly. Should probably not return early, however.
bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -301,7 +320,7 @@ namespace cryptonote
);
}
//---------------------------------------------------------------------------------
- //proper tx_pool handling courtesy of CryptoZoidberg and Boolberry
+ //TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::remove_stuck_transactions()
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -332,6 +351,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ //TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::get_relayable_transactions(std::list<std::pair<crypto::hash, cryptonote::transaction>> &txs) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -380,6 +400,7 @@ namespace cryptonote
txs.push_back(tx_vt.second.tx);
}
//------------------------------------------------------------------
+ //TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -480,7 +501,8 @@ namespace cryptonote
if(txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height))
return false;//we already sure that this tx is broken for this height
- if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id))
+ tx_verification_context tvc;
+ if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id, tvc))
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@@ -496,7 +518,12 @@ namespace cryptonote
if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height))
return false;
//check ring signature again, it is possible (with very small chance) that this transaction become again valid
+#if BLOCKCHAIN_DB == DB_LMDB
+ tx_verification_context tvc;
+ if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id, tvc))
+#else
if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id))
+#endif
{
txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1;
txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height);
@@ -556,6 +583,7 @@ namespace cryptonote
return ss.str();
}
//---------------------------------------------------------------------------------
+ //TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee)
{
// Warning: This function takes already_generated_
@@ -646,6 +674,7 @@ namespace cryptonote
return n_removed;
}
//---------------------------------------------------------------------------------
+ //TODO: investigate whether only ever returning true is correct
bool tx_memory_pool::init(const std::string& config_folder)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -679,6 +708,7 @@ namespace cryptonote
}
//---------------------------------------------------------------------------------
+ //TODO: investigate whether only ever returning true is correct
bool tx_memory_pool::deinit()
{
if (m_config_folder.empty())
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 71febcab6..84e11eeff 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -57,7 +57,9 @@ namespace cryptonote
/* */
/************************************************************************/
+ //! pair of <transaction fee, transaction hash> for organization
typedef std::pair<double, crypto::hash> tx_by_fee_entry;
+
class txCompare
{
public:
@@ -71,47 +73,255 @@ namespace cryptonote
}
};
+ //! container for sorting transactions by fee per unit size
typedef std::set<tx_by_fee_entry, txCompare> sorted_tx_container;
+ /**
+ * @brief Transaction pool, handles transactions which are not part of a block
+ *
+ * This class handles all transactions which have been received, but not as
+ * part of a block.
+ *
+ * This handling includes:
+ * storing the transactions
+ * organizing the transactions by fee per size
+ * taking/giving transactions to and from various other components
+ * saving the transactions to disk on shutdown
+ * helping create a new block template by choosing transactions for it
+ *
+ */
class tx_memory_pool: boost::noncopyable
{
public:
#if BLOCKCHAIN_DB == DB_LMDB
+ /**
+ * @brief Constructor
+ *
+ * @param bchs a Blockchain class instance, for getting chain info
+ */
tx_memory_pool(Blockchain& bchs);
#else
tx_memory_pool(blockchain_storage& bchs);
#endif
- bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, uint8_t version);
- bool add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, uint8_t version);
- //gets tx and remove it from pool
+
+
+ /**
+ * @copydoc add_tx(const transaction&, tx_verification_context&, bool, bool, uint8_t)
+ *
+ * @param id the transaction's hash
+ * @param blob_size the transaction's size
+ */
+ bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, uint8_t version);
+
+ /**
+ * @brief add a transaction to the transaction pool
+ *
+ * Most likely the transaction will come from the network, but it is
+ * also possible for transactions to come from popped blocks during
+ * a reorg, or from local clients creating a transaction and
+ * submitting it to the network
+ *
+ * @param tx the transaction to be added
+ * @param tvc return-by-reference status about the transaction verification
+ * @param kept_by_block has this transaction been in a block?
+ * @param relayed was this transaction from the network or a local client?
+ * @param version the version used to create the transaction
+ *
+ * @return true if the transaction passes validations, otherwise false
+ */
+ bool add_tx(const transaction &tx, tx_verification_context& tvc, bool kept_by_block, bool relayed, uint8_t version);
+
+ /**
+ * @brief takes a transaction with the given hash from the pool
+ *
+ * @param id the hash of the transaction
+ * @param tx return-by-reference the transaction taken
+ * @param blob_size return-by-reference the transaction's size
+ * @param fee the transaction fee
+ * @param relayed return-by-reference was transaction relayed to us by the network?
+ *
+ * @return true unless the transaction cannot be found in the pool
+ */
bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed);
+ /**
+ * @brief checks if the pool has a transaction with the given hash
+ *
+ * @param id the hash to look for
+ *
+ * @return true if the transaction is in the pool, otherwise false
+ */
bool have_tx(const crypto::hash &id) const;
+
+ /**
+ * @brief action to take when notified of a block added to the blockchain
+ *
+ * Currently does nothing
+ *
+ * @param new_block_height the height of the blockchain after the change
+ * @param top_block_id the hash of the new top block
+ *
+ * @return true
+ */
bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id);
+
+ /**
+ * @brief action to take when notified of a block removed from the blockchain
+ *
+ * Currently does nothing
+ *
+ * @param new_block_height the height of the blockchain after the change
+ * @param top_block_id the hash of the new top block
+ *
+ * @return true
+ */
bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id);
+
+ /**
+ * @brief action to take periodically
+ *
+ * Currently checks transaction pool for stale ("stuck") transactions
+ */
void on_idle();
+ /**
+ * @brief locks the transaction pool
+ */
void lock() const;
+
+ /**
+ * @brief unlocks the transaction pool
+ */
void unlock() const;
// load/store operations
+
+ /**
+ * @brief loads pool state (if any) from disk, and initializes pool
+ *
+ * @param config_folder folder name where pool state will be
+ *
+ * @return true
+ */
bool init(const std::string& config_folder);
+
+ /**
+ * @brief attempts to save the transaction pool state to disk
+ *
+ * Currently fails (returns false) if the data directory from init()
+ * does not exist and cannot be created, but returns true even if
+ * saving to disk is unsuccessful.
+ *
+ * @return true in most cases (see above)
+ */
bool deinit();
+
+ /**
+ * @brief Chooses transactions for a block to include
+ *
+ * @param bl return-by-reference the block to fill in with transactions
+ * @param median_size the current median block size
+ * @param already_generated_coins the current total number of coins "minted"
+ * @param total_size return-by-reference the total size of the new block
+ * @param fee return-by-reference the total of fees from the included transactions
+ *
+ * @return true
+ */
bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee);
+
+ /**
+ * @brief get a list of all transactions in the pool
+ *
+ * @param txs return-by-reference the list of transactions
+ */
void get_transactions(std::list<transaction>& txs) const;
+
+ /**
+ * @brief get information about all transactions and key images in the pool
+ *
+ * see documentation on tx_info and spent_key_image_info for more details
+ *
+ * @param tx_infos return-by-reference the transactions' information
+ * @param key_image_infos return-by-reference the spent key images' information
+ *
+ * @return true
+ */
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 a specific transaction from the pool
+ *
+ * @param h the hash of the transaction to get
+ * @param tx return-by-reference the transaction requested
+ *
+ * @return true if the transaction is found, otherwise false
+ */
bool get_transaction(const crypto::hash& h, transaction& tx) const;
+
+ /**
+ * @brief get a list of all relayable transactions and their hashes
+ *
+ * "relayable" in this case means:
+ * nonzero fee
+ * hasn't been relayed too recently
+ * isn't old enough that relaying it is considered harmful
+ *
+ * @param txs return-by-reference the transactions and their hashes
+ *
+ * @return true
+ */
bool get_relayable_transactions(std::list<std::pair<crypto::hash, cryptonote::transaction>>& txs) const;
+
+ /**
+ * @brief tell the pool that certain transactions were just relayed
+ *
+ * @param txs the list of transactions (and their hashes)
+ */
void set_relayed(const std::list<std::pair<crypto::hash, cryptonote::transaction>>& txs);
+
+ /**
+ * @brief get the total number of transactions in the pool
+ *
+ * @return the number of transactions in the pool
+ */
size_t get_transactions_count() const;
+
+ /**
+ * @brief get a string containing human-readable pool information
+ *
+ * @param short_format whether to use a shortened format for the info
+ *
+ * @return the string
+ */
std::string print_pool(bool short_format) const;
+
+ /**
+ * @brief remove transactions from the pool which are no longer valid
+ *
+ * With new versions of the currency, what conditions render a transaction
+ * invalid may change. This function clears those which were received
+ * before a version change and no longer conform to requirements.
+ *
+ * @param version the version the transactions must conform to
+ *
+ * @return the number of transactions removed
+ */
size_t validate(uint8_t version);
- /*bool flush_pool(const std::strig& folder);
- bool inflate_pool(const std::strig& folder);*/
#define CURRENT_MEMPOOL_ARCHIVE_VER 11
+ /**
+ * @brief serialize the transaction pool to/from disk
+ *
+ * If the archive version passed is older than the version compiled
+ * in, this function does nothing, as it cannot deserialize after a
+ * format change.
+ *
+ * @tparam archive_t the archive class
+ * @param a the archive to serialize to/from
+ * @param version the archive version
+ */
template<class archive_t>
void serialize(archive_t & a, const unsigned int version)
{
@@ -123,97 +333,169 @@ namespace cryptonote
a & m_timed_out_transactions;
}
+ /**
+ * @brief information about a single transaction
+ */
struct tx_details
{
- transaction tx;
- size_t blob_size;
- uint64_t fee;
- crypto::hash max_used_block_id;
- uint64_t max_used_block_height;
- bool kept_by_block;
- //
- uint64_t last_failed_height;
+ transaction tx; //!< the transaction
+ size_t blob_size; //!< the transaction's size
+ uint64_t fee; //!< the transaction's fee amount
+ crypto::hash max_used_block_id; //!< the hash of the highest block referenced by an input
+ uint64_t max_used_block_height; //!< the height of the highest block referenced by an input
+
+ //! whether or not the transaction has been in a block before
+ /*! if the transaction was returned to the pool from the blockchain
+ * due to a reorg, then this will be true
+ */
+ bool kept_by_block;
+
+ //! the highest block the transaction referenced when last checking it failed
+ /*! if verifying a transaction's inputs fails, it's possible this is due
+ * to a reorg since it was created (if it used recently created outputs
+ * as inputs).
+ */
+ uint64_t last_failed_height;
+
+ //! the hash of the highest block the transaction referenced when last checking it failed
+ /*! if verifying a transaction's inputs fails, it's possible this is due
+ * to a reorg since it was created (if it used recently created outputs
+ * as inputs).
+ */
crypto::hash last_failed_id;
- time_t receive_time;
- time_t last_relayed_time;
- bool relayed;
+ time_t receive_time; //!< the time when the transaction entered the pool
+
+ time_t last_relayed_time; //!< the last time the transaction was relayed to the network
+ bool relayed; //!< whether or not the transaction has been relayed to the network
};
private:
+
+ /**
+ * @brief remove old transactions from the pool
+ *
+ * After a certain time, it is assumed that a transaction which has not
+ * yet been mined will likely not be mined. These transactions are removed
+ * from the pool to avoid buildup.
+ *
+ * @return true
+ */
bool remove_stuck_transactions();
+
+ /**
+ * @brief check if a transaction in the pool has a given spent key image
+ *
+ * @param key_im the spent key image to look for
+ *
+ * @return true if the spent key image is present, otherwise false
+ */
bool have_tx_keyimg_as_spent(const crypto::key_image& key_im) const;
+
+ /**
+ * @brief check if any spent key image in a transaction is in the pool
+ *
+ * Checks if any of the spent key images in a given transaction are present
+ * in any of the transactions in the transaction pool.
+ *
+ * @note see tx_pool::have_tx_keyimg_as_spent
+ *
+ * @param tx the transaction to check spent key images of
+ *
+ * @return true if any spent key images are present in the pool, otherwise false
+ */
bool have_tx_keyimges_as_spent(const transaction& tx) const;
+
+ /**
+ * @brief forget a transaction's spent key images
+ *
+ * Spent key images are stored separately from transactions for
+ * convenience/speed, so this is part of the process of removing
+ * a transaction from the pool.
+ *
+ * @param tx the transaction
+ *
+ * @return false if any key images to be removed cannot be found, otherwise true
+ */
bool remove_transaction_keyimages(const transaction& tx);
+
+ /**
+ * @brief check if any of a transaction's spent key images are present in a given set
+ *
+ * @param kic the set of key images to check against
+ * @param tx the transaction to check
+ *
+ * @return true if any key images present in the set, otherwise false
+ */
static bool have_key_images(const std::unordered_set<crypto::key_image>& kic, const transaction& tx);
+
+ /**
+ * @brief append the key images from a transaction to the given set
+ *
+ * @param kic the set of key images to append to
+ * @param tx the transaction
+ *
+ * @return false if any append fails, otherwise true
+ */
static bool append_key_images(std::unordered_set<crypto::key_image>& kic, const transaction& tx);
+ /**
+ * @brief check if a transaction is a valid candidate for inclusion in a block
+ *
+ * @param txd the transaction to check (and info about it)
+ *
+ * @return true if the transaction is good to go, otherwise false
+ */
bool is_transaction_ready_to_go(tx_details& txd) const;
+
+ //! map transactions (and related info) by their hashes
typedef std::unordered_map<crypto::hash, tx_details > transactions_container;
+
+ //TODO: confirm the below comments and investigate whether or not this
+ // is the desired behavior
+ //! map key images to transactions which spent them
+ /*! this seems odd, but it seems that multiple transactions can exist
+ * in the pool which both have the same spent key. This would happen
+ * in the event of a reorg where someone creates a new/different
+ * transaction on the assumption that the original will not be in a
+ * block again.
+ */
typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash> > key_images_container;
- mutable epee::critical_section m_transactions_lock;
- transactions_container m_transactions;
- key_images_container m_spent_key_images;
+ mutable epee::critical_section m_transactions_lock; //!< lock for the pool
+ transactions_container m_transactions; //!< container for transactions in the pool
+
+ //! container for spent key images from the transactions in the pool
+ key_images_container m_spent_key_images;
+
+ //TODO: this time should be a named constant somewhere, not hard-coded
+ //! interval on which to check for stale/"stuck" transactions
epee::math_helper::once_a_time_seconds<30> m_remove_stuck_tx_interval;
- //TODO: add fee_per_kb element to type tx_details and replace this
- //functionality by just making m_transactions a std::set
- sorted_tx_container m_txs_by_fee;
+ //TODO: look into doing this better
+ sorted_tx_container m_txs_by_fee; //!< container for transactions organized by fee per size
+ /**
+ * @brief get an iterator to a transaction in the sorted container
+ *
+ * @param id the hash of the transaction to look for
+ *
+ * @return an iterator, possibly to the end of the container if not found
+ */
sorted_tx_container::iterator find_tx_in_sorted_container(const crypto::hash& id) const;
+ //! transactions which are unlikely to be included in blocks
+ /*! These transactions are kept in RAM in case they *are* included
+ * in a block eventually, but this container is not saved to disk.
+ */
std::unordered_set<crypto::hash> m_timed_out_transactions;
- //transactions_container m_alternative_transactions;
-
- std::string m_config_folder;
+ std::string m_config_folder; //!< the folder to save state to
#if BLOCKCHAIN_DB == DB_LMDB
- Blockchain& m_blockchain;
+ Blockchain& m_blockchain; //!< reference to the Blockchain object
#else
blockchain_storage& m_blockchain;
#endif
- /************************************************************************/
- /* */
- /************************************************************************/
- /*class inputs_visitor: public boost::static_visitor<bool>
- {
- key_images_container& m_spent_keys;
- public:
- inputs_visitor(key_images_container& spent_keys): m_spent_keys(spent_keys)
- {}
- bool operator()(const txin_to_key& tx) const
- {
- auto pr = m_spent_keys.insert(tx.k_image);
- CHECK_AND_ASSERT_MES(pr.second, false, "Tried to insert transaction with input seems already spent, input: " << epee::string_tools::pod_to_hex(tx.k_image));
- return true;
- }
- bool operator()(const txin_gen& tx) const
- {
- CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool");
- return false;
- }
- bool operator()(const txin_to_script& tx) const {return false;}
- bool operator()(const txin_to_scripthash& tx) const {return false;}
- }; */
- /************************************************************************/
- /* */
- /************************************************************************/
- class amount_visitor: public boost::static_visitor<uint64_t>
- {
- public:
- uint64_t operator()(const txin_to_key& tx) const
- {
- return tx.amount;
- }
- uint64_t operator()(const txin_gen& tx) const
- {
- CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool");
- return 0;
- }
- uint64_t operator()(const txin_to_script& tx) const {return 0;}
- uint64_t operator()(const txin_to_scripthash& tx) const {return 0;}
- };
#if BLOCKCHAIN_DB == DB_LMDB
#else
diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h
index fcfd2a3e2..e58291ea9 100644
--- a/src/cryptonote_core/verification_context.h
+++ b/src/cryptonote_core/verification_context.h
@@ -40,6 +40,13 @@ namespace cryptonote
bool m_verifivation_failed; //bad tx, should drop connection
bool m_verifivation_impossible; //the transaction is related with an alternative blockchain
bool m_added_to_pool;
+ bool m_low_mixin;
+ bool m_double_spend;
+ bool m_invalid_input;
+ bool m_invalid_output;
+ bool m_too_big;
+ bool m_overspend;
+ bool m_fee_too_low;
};
struct block_verification_context
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h
index c2d428025..1c92958c9 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -134,7 +134,7 @@ namespace cryptonote
bool m_one_request = true;
// static std::ofstream m_logreq;
- std::mutex m_buffer_mutex;
+ boost::mutex m_buffer_mutex;
double get_avg_block_size();
boost::circular_buffer<size_t> m_avg_buffer = boost::circular_buffer<size_t>(10);
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index f917f4c3f..9f0ea0e83 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -281,7 +281,7 @@ namespace cryptonote
<< " [" << std::abs(diff) << " blocks (" << diff / (24 * 60 * 60 / DIFFICULTY_TARGET_V1) << " days) "
<< (0 <= diff ? std::string("behind") : std::string("ahead"))
<< "] " << ENDL << "SYNCHRONIZATION started", (is_inital ? LOG_LEVEL_0:LOG_LEVEL_1));
- LOG_PRINT_L1("Remote top block height: " << hshd.current_height << ", id: " << hshd.top_id);
+ LOG_PRINT_L1("Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id);
context.m_state = cryptonote_connection_context::state_synchronizing;
context.m_remote_blockchain_height = hshd.current_height;
//let the socket to send response to handshake, but request callback, to let send request data after response
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 0b42257e7..166fe04ca 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -439,5 +439,23 @@ bool t_command_parser_executor::flush_txpool(const std::vector<std::string>& arg
return m_executor.flush_txpool(txid);
}
+bool t_command_parser_executor::output_histogram(const std::vector<std::string>& args)
+{
+ if (args.size() > 2) return false;
+
+ uint64_t min_count = 3;
+ uint64_t max_count = 0;
+
+ if (args.size() >= 1)
+ {
+ min_count = boost::lexical_cast<uint64_t>(args[0]);
+ }
+ if (args.size() >= 2)
+ {
+ max_count = boost::lexical_cast<uint64_t>(args[1]);
+ }
+ return m_executor.output_histogram(min_count, max_count);
+}
+
} // namespace daemonize
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 51c55a8c0..11df92a5e 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -114,6 +114,8 @@ public:
bool unban(const std::vector<std::string>& args);
bool flush_txpool(const std::vector<std::string>& args);
+
+ bool output_histogram(const std::vector<std::string>& args);
};
} // namespace daemonize
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 206de9d4a..aabc2f09a 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -214,6 +214,11 @@ t_command_server::t_command_server(
, std::bind(&t_command_parser_executor::flush_txpool, &m_parser, p::_1)
, "Flush a transaction from the tx pool by its txid, or the whole tx pool"
);
+ m_command_lookup.set_handler(
+ "output_histogram"
+ , std::bind(&t_command_parser_executor::output_histogram, &m_parser, p::_1)
+ , "Print output histogram (amount, instances)"
+ );
}
bool t_command_server::process_command_str(const std::string& cmd)
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 2208ef25a..2b7f0d177 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -28,7 +28,6 @@
#pragma once
-#include "cryptonote_core/checkpoints_create.h"
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "misc_log_ex.h"
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 361808cf7..933c93ed7 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -66,7 +66,7 @@ namespace {
<< "is orphan: " << header.orphan_status << std::endl
<< "height: " << boost::lexical_cast<std::string>(header.height) << std::endl
<< "depth: " << boost::lexical_cast<std::string>(header.depth) << std::endl
- << "hash: " << header.hash
+ << "hash: " << header.hash << std::endl
<< "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl
<< "reward: " << boost::lexical_cast<std::string>(header.reward);
}
@@ -558,6 +558,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) {
std::string fail_message = "Problem fetching transaction";
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(transaction_hash));
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(req, res, "/gettransactions", fail_message.c_str()))
@@ -567,7 +568,6 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) {
}
else
{
- req.txs_hashes.push_back(epee::string_tools::pod_to_hex(transaction_hash));
if (!m_rpc_server->on_get_transactions(req, res) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << fail_message.c_str();
@@ -1203,5 +1203,41 @@ bool t_rpc_command_executor::flush_txpool(const std::string &txid)
return true;
}
+bool t_rpc_command_executor::output_histogram(uint64_t min_count, uint64_t max_count)
+{
+ cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req;
+ cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response res;
+ std::string fail_message = "Unsuccessful";
+ epee::json_rpc::error error_resp;
+
+ req.min_count = min_count;
+ req.max_count = max_count;
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(req, res, "get_output_histogram", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_output_histogram(req, res, error_resp))
+ {
+ tools::fail_msg_writer() << fail_message.c_str();
+ return true;
+ }
+ }
+
+ std::sort(res.histogram.begin(), res.histogram.end(),
+ [](const cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry &e1, const cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry &e2)->bool { return e1.instances < e2.instances; });
+ for (const auto &e: res.histogram)
+ {
+ tools::msg_writer() << e.instances << " " << cryptonote::print_money(e.amount);
+ }
+
+ return true;
+}
+
}// namespace daemonize
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index bc3f2d5c5..7e73e7faf 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -132,6 +132,8 @@ public:
bool unban(const std::string &ip);
bool flush_txpool(const std::string &txid);
+
+ bool output_histogram(uint64_t min_count, uint64_t max_count);
};
} // namespace daemonize
diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp
index 88f05a305..f8cc0c6c7 100644
--- a/src/daemonizer/windows_service.cpp
+++ b/src/daemonizer/windows_service.cpp
@@ -99,8 +99,8 @@ namespace {
// to allow the user to read any output.
void pause_to_display_admin_window_messages()
{
- std::chrono::milliseconds how_long{1500};
- std::this_thread::sleep_for(how_long);
+ boost::chrono::milliseconds how_long{1500};
+ boost::this_thread::sleep_for(how_long);
}
}
diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h
index 2d21509ec..f4258a215 100644
--- a/src/daemonizer/windows_service_runner.h
+++ b/src/daemonizer/windows_service_runner.h
@@ -56,7 +56,7 @@ namespace windows {
private:
SERVICE_STATUS_HANDLE m_status_handle{nullptr};
SERVICE_STATUS m_status{};
- std::mutex m_lock{};
+ boost::mutex m_lock{};
std::string m_name;
T_handler m_handler;
diff --git a/src/miner/CMakeLists.txt b/src/miner/CMakeLists.txt
index b09ad47aa..d065b9950 100644
--- a/src/miner/CMakeLists.txt
+++ b/src/miner/CMakeLists.txt
@@ -49,6 +49,7 @@ target_link_libraries(simpleminer
${Boost_FILESYSTEM_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_REGEX_LIBRARY}
+ ${Boost_CHRONO_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
${CMAKE_THREAD_LIBS_INIT}
diff --git a/src/p2p/data_logger.cpp b/src/p2p/data_logger.cpp
index 7fc85e3bc..ca0726c5f 100644
--- a/src/p2p/data_logger.cpp
+++ b/src/p2p/data_logger.cpp
@@ -40,7 +40,7 @@ namespace epee
namespace net_utils
{
data_logger &data_logger::get_instance() {
- std::call_once(m_singleton,
+ boost::call_once(m_singleton,
[] {
_info_c("dbg/data","Creating singleton of data_logger");
if (m_state != data_logger_state::state_before_init) { _erro_c("dbg/data","Internal error in singleton"); throw std::runtime_error("data_logger singleton"); }
@@ -61,7 +61,7 @@ namespace net_utils
data_logger::data_logger() {
_note_c("dbg/data","Starting data logger (for graphs data)");
if (m_state != data_logger_state::state_during_init) { _erro_c("dbg/data","Singleton ctor state"); throw std::runtime_error("data_logger ctor state"); }
- std::lock_guard<std::mutex> lock(mMutex); // lock
+ boost::lock_guard<boost::mutex> lock(mMutex); // lock
// prepare all the files for given data channels:
mFilesMap["peers"] = data_logger::fileData("log/dr-monero/peers.data");
@@ -89,11 +89,11 @@ namespace net_utils
std::shared_ptr<boost::thread> logger_thread(new boost::thread([&]() {
_info_c("dbg/data","Inside thread for data logger");
while (m_state == data_logger_state::state_during_init) { // wait for creation to be done (in other thread, in singleton) before actually running
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ boost::this_thread::sleep_for(boost::chrono::seconds(1));
}
_info_c("dbg/data","Inside thread for data logger - going into main loop");
while (m_state == data_logger_state::state_ready_to_use) { // run as long as we are not closing the single object
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ boost::this_thread::sleep_for(boost::chrono::seconds(1));
saveToFile(); // save all the pending data
}
_info_c("dbg/data","Inside thread for data logger - done the main loop");
@@ -106,12 +106,12 @@ namespace net_utils
data_logger::~data_logger() {
_note_c("dbg/data","Destructor of the data logger");
{
- std::lock_guard<std::mutex> lock(mMutex);
+ boost::lock_guard<boost::mutex> lock(mMutex);
m_state = data_logger_state::state_dying;
}
_info_c("dbg/data","State was set to dying");
while(m_thread_maybe_running) { // wait for the thread to exit
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ boost::this_thread::sleep_for(boost::chrono::seconds(1));
_info_c("dbg/data","Waiting for background thread to exit");
}
_info_c("dbg/data","Thread exited");
@@ -123,7 +123,7 @@ namespace net_utils
}
void data_logger::add_data(std::string filename, unsigned int data) {
- std::lock_guard<std::mutex> lock(mMutex);
+ boost::lock_guard<boost::mutex> lock(mMutex);
if (m_state != data_logger_state::state_ready_to_use) { _info_c("dbg/data","Data logger is not ready, returning."); return; }
if (mFilesMap.find(filename) == mFilesMap.end()) { // no such file/counter
@@ -151,7 +151,7 @@ namespace net_utils
void data_logger::saveToFile() {
_dbg2_c("dbg/data","saving to files");
- std::lock_guard<std::mutex> lock(mMutex);
+ boost::lock_guard<boost::mutex> lock(mMutex);
if (m_state != data_logger_state::state_ready_to_use) { _info_c("dbg/data","Data logger is not ready, returning."); return; }
nOT::nUtils::cFilesystemUtils::CreateDirTree("log/dr-monero/net/");
for (auto &element : mFilesMap)
@@ -194,7 +194,7 @@ namespace net_utils
data_logger_state data_logger::m_state(data_logger_state::state_before_init); ///< (static) state of the singleton object
std::atomic<bool> data_logger::m_save_graph(false); // (static)
std::atomic<bool> data_logger::m_thread_maybe_running(false); // (static)
-std::once_flag data_logger::m_singleton; // (static)
+boost::once_flag data_logger::m_singleton; // (static)
std::unique_ptr<data_logger> data_logger::m_obj; // (static)
} // namespace
diff --git a/src/p2p/data_logger.hpp b/src/p2p/data_logger.hpp
index affc97f5f..148afc0ab 100644
--- a/src/p2p/data_logger.hpp
+++ b/src/p2p/data_logger.hpp
@@ -33,8 +33,9 @@
#include <map>
#include <fstream>
#include <memory>
-#include <thread>
-#include <mutex>
+#include <boost/thread/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/once.hpp>
#include <atomic>
namespace epee
@@ -71,7 +72,7 @@ enum class data_logger_state { state_before_init, state_during_init, state_ready
static bool is_dying();
private:
- static std::once_flag m_singleton; ///< to guarantee singleton creates the object exactly once
+ static boost::once_flag m_singleton; ///< to guarantee singleton creates the object exactly once
static data_logger_state m_state; ///< state of the singleton object
static std::atomic<bool> m_thread_maybe_running; ///< is the background thread (more or less) running, or is it fully finished
static std::unique_ptr<data_logger> m_obj; ///< the singleton object. Only use it via get_instance(). Can be killed by kill_instance()
@@ -94,7 +95,7 @@ enum class data_logger_state { state_before_init, state_during_init, state_ready
};
std::map<std::string, fileData> mFilesMap;
- std::mutex mMutex;
+ boost::mutex mMutex;
void saveToFile(); ///< write data to the target files. do not use this directly
};
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 260dd813d..5943c248f 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -120,7 +120,7 @@ namespace nodetool
void delete_connections(size_t count);
virtual bool block_ip(uint32_t adress, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_ip(uint32_t address);
- virtual std::map<uint32_t, time_t> get_blocked_ips() const { return m_blocked_ips; }
+ virtual std::map<uint32_t, time_t> get_blocked_ips() { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); return m_blocked_ips; }
private:
const std::vector<std::string> m_seed_nodes_list =
{ "seeds.moneroseeds.se"
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 56717ec66..6278db891 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -585,7 +585,7 @@ namespace nodetool
break;
epee::net_utils::data_logger::get_instance().add_data("peers", number_of_peers);
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ boost::this_thread::sleep_for(boost::chrono::seconds(1));
} // main loop of thread
_note("Thread monitor number of peers - done");
})); // lambda
diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h
index 68b0bd1ee..5e7645365 100644
--- a/src/p2p/net_node_common.h
+++ b/src/p2p/net_node_common.h
@@ -52,7 +52,7 @@ namespace nodetool
virtual void for_each_connection(std::function<bool(t_connection_context&, peerid_type)> f)=0;
virtual bool block_ip(uint32_t adress, time_t seconds = 0)=0;
virtual bool unblock_ip(uint32_t adress)=0;
- virtual std::map<uint32_t, time_t> get_blocked_ips()const=0;
+ virtual std::map<uint32_t, time_t> get_blocked_ips()=0;
virtual bool add_ip_fail(uint32_t adress)=0;
};
@@ -96,7 +96,7 @@ namespace nodetool
{
return true;
}
- virtual std::map<uint32_t, time_t> get_blocked_ips() const
+ virtual std::map<uint32_t, time_t> get_blocked_ips()
{
return std::map<uint32_t, time_t>();
}
diff --git a/src/p2p/network_throttle-detail.cpp b/src/p2p/network_throttle-detail.cpp
index 54c82dd8d..ed3c8e7b4 100644
--- a/src/p2p/network_throttle-detail.cpp
+++ b/src/p2p/network_throttle-detail.cpp
@@ -243,7 +243,7 @@ network_time_seconds network_throttle::get_sleep_time_after_tick(size_t packet_s
void network_throttle::logger_handle_net(const std::string &filename, double time, size_t size) {
if (! epee::net_utils::data_logger::m_save_graph)
return;
- std::mutex mutex;
+ boost::mutex mutex;
mutex.lock(); {
std::fstream file;
file.open(filename.c_str(), std::ios::app | std::ios::out );
diff --git a/src/p2p/network_throttle.cpp b/src/p2p/network_throttle.cpp
index 42f54964b..30538bb3c 100644
--- a/src/p2p/network_throttle.cpp
+++ b/src/p2p/network_throttle.cpp
@@ -67,9 +67,9 @@ namespace net_utils
// ================================================================================================
// static:
-std::mutex network_throttle_manager::m_lock_get_global_throttle_in;
-std::mutex network_throttle_manager::m_lock_get_global_throttle_inreq;
-std::mutex network_throttle_manager::m_lock_get_global_throttle_out;
+boost::mutex network_throttle_manager::m_lock_get_global_throttle_in;
+boost::mutex network_throttle_manager::m_lock_get_global_throttle_inreq;
+boost::mutex network_throttle_manager::m_lock_get_global_throttle_out;
int network_throttle_manager::xxx;
@@ -77,27 +77,27 @@ int network_throttle_manager::xxx;
// ================================================================================================
// methods:
i_network_throttle & network_throttle_manager::get_global_throttle_in() {
- std::call_once(m_once_get_global_throttle_in, [] { m_obj_get_global_throttle_in.reset(new network_throttle("in/all","<<< global-IN",10)); } );
+ boost::call_once(m_once_get_global_throttle_in, [] { m_obj_get_global_throttle_in.reset(new network_throttle("in/all","<<< global-IN",10)); } );
return * m_obj_get_global_throttle_in;
}
-std::once_flag network_throttle_manager::m_once_get_global_throttle_in;
+boost::once_flag network_throttle_manager::m_once_get_global_throttle_in;
std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_in;
i_network_throttle & network_throttle_manager::get_global_throttle_inreq() {
- std::call_once(m_once_get_global_throttle_inreq, [] { m_obj_get_global_throttle_inreq.reset(new network_throttle("inreq/all", "<== global-IN-REQ",10)); } );
+ boost::call_once(m_once_get_global_throttle_inreq, [] { m_obj_get_global_throttle_inreq.reset(new network_throttle("inreq/all", "<== global-IN-REQ",10)); } );
return * m_obj_get_global_throttle_inreq;
}
-std::once_flag network_throttle_manager::m_once_get_global_throttle_inreq;
+boost::once_flag network_throttle_manager::m_once_get_global_throttle_inreq;
std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_inreq;
i_network_throttle & network_throttle_manager::get_global_throttle_out() {
- std::call_once(m_once_get_global_throttle_out, [] { m_obj_get_global_throttle_out.reset(new network_throttle("out/all", ">>> global-OUT",10)); } );
+ boost::call_once(m_once_get_global_throttle_out, [] { m_obj_get_global_throttle_out.reset(new network_throttle("out/all", ">>> global-OUT",10)); } );
return * m_obj_get_global_throttle_out;
}
-std::once_flag network_throttle_manager::m_once_get_global_throttle_out;
+boost::once_flag network_throttle_manager::m_once_get_global_throttle_out;
std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_out;
diff --git a/src/p2p/network_throttle.hpp b/src/p2p/network_throttle.hpp
index 6135ed72b..b954c5b3a 100644
--- a/src/p2p/network_throttle.hpp
+++ b/src/p2p/network_throttle.hpp
@@ -113,16 +113,16 @@ class network_throttle_manager {
//protected:
public: // XXX
// [[note1]]
- static std::once_flag m_once_get_global_throttle_in;
- static std::once_flag m_once_get_global_throttle_inreq; // [[note2]]
- static std::once_flag m_once_get_global_throttle_out;
+ static boost::once_flag m_once_get_global_throttle_in;
+ static boost::once_flag m_once_get_global_throttle_inreq; // [[note2]]
+ static boost::once_flag m_once_get_global_throttle_out;
static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_in;
static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_inreq;
static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_out;
- static std::mutex m_lock_get_global_throttle_in;
- static std::mutex m_lock_get_global_throttle_inreq;
- static std::mutex m_lock_get_global_throttle_out;
+ static boost::mutex m_lock_get_global_throttle_in;
+ static boost::mutex m_lock_get_global_throttle_inreq;
+ static boost::mutex m_lock_get_global_throttle_out;
friend class cryptonote::cryptonote_protocol_handler_base; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS!
friend class connection_basic; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS!
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 3ce4e6006..165a24c22 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -355,24 +355,40 @@ namespace cryptonote
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))
+ if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false) || tvc.m_verifivation_failed)
{
- LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx");
- res.status = "Failed";
- return true;
- }
-
- if(tvc.m_verifivation_failed)
- {
- LOG_PRINT_L0("[on_send_raw_tx]: tx verification 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 = "Failed";
+ if ((res.low_mixin = tvc.m_low_mixin))
+ res.reason = "mixin too low";
+ if ((res.double_spend = tvc.m_double_spend))
+ res.reason = "double spend";
+ if ((res.invalid_input = tvc.m_invalid_input))
+ res.reason = "invalid input";
+ if ((res.invalid_output = tvc.m_invalid_output))
+ res.reason = "invalid output";
+ if ((res.too_big = tvc.m_too_big))
+ res.reason = "too big";
+ if ((res.overspend = tvc.m_overspend))
+ res.reason = "overspend";
+ if ((res.fee_too_low = tvc.m_fee_too_low))
+ res.reason = "fee too low";
return true;
}
if(!tvc.m_should_be_relayed)
{
LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed");
- res.status = "Not relayed";
+ res.reason = "Not relayed";
+ res.not_relayed = true;
+ res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -544,7 +560,7 @@ namespace cryptonote
if(m_core.get_current_blockchain_height() <= h)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
+ error_resp.message = std::string("Too big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
}
res = string_tools::pod_to_hex(m_core.get_block_id_by_height(h));
return true;
@@ -627,8 +643,10 @@ namespace cryptonote
LOG_ERROR("Failed to calculate offset for ");
return false;
}
+ blobdata hashing_blob = get_block_hashing_blob(b);
res.prev_hash = string_tools::pod_to_hex(b.prev_id);
res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob);
+ res.blockhashing_blob = string_tools::buff_to_hex_nodelimer(hashing_blob);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -792,7 +810,7 @@ namespace cryptonote
if(m_core.get_current_blockchain_height() <= req.height)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
+ error_resp.message = std::string("Too big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
return false;
}
crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
@@ -838,7 +856,7 @@ namespace cryptonote
if(m_core.get_current_blockchain_height() <= req.height)
{
error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT;
- error_resp.message = std::string("To big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
+ error_resp.message = std::string("Too big height: ") + std::to_string(req.height) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height());
return false;
}
block_hash = m_core.get_block_id_by_height(req.height);
@@ -950,13 +968,16 @@ namespace cryptonote
return false;
}
+ auto now = time(nullptr);
std::map<uint32_t, time_t> blocked_ips = m_p2p.get_blocked_ips();
for (std::map<uint32_t, time_t>::const_iterator i = blocked_ips.begin(); i != blocked_ips.end(); ++i)
{
- COMMAND_RPC_GETBANS::ban b;
- b.ip = i->first;
- b.seconds = i->second;
- res.bans.push_back(b);
+ if (i->second > now) {
+ COMMAND_RPC_GETBANS::ban b;
+ b.ip = i->first;
+ b.seconds = i->second - now;
+ res.bans.push_back(b);
+ }
}
res.status = CORE_RPC_STATUS_OK;
@@ -1038,6 +1059,38 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp)
+ {
+ if(!check_core_busy())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY;
+ error_resp.message = "Core is busy.";
+ return false;
+ }
+
+ std::map<uint64_t, uint64_t> histogram;
+ try
+ {
+ histogram = m_core.get_blockchain_storage().get_output_histogram(req.amounts);
+ }
+ catch (const std::exception &e)
+ {
+ res.status = "Failed to get output histogram";
+ return true;
+ }
+
+ res.histogram.clear();
+ res.histogram.reserve(histogram.size());
+ for (const auto &i: histogram)
+ {
+ if (i.second >= req.min_count && (i.second <= req.max_count || req.max_count == 0))
+ res.histogram.push_back(COMMAND_RPC_GET_OUTPUT_HISTOGRAM::entry(i.first, i.second));
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_fast_exit(const COMMAND_RPC_FAST_EXIT::request& req, COMMAND_RPC_FAST_EXIT::response& res)
{
cryptonote::core::set_fast_exit();
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index f79087030..5c3707209 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -109,6 +109,7 @@ namespace cryptonote
MAP_JON_RPC_WE("setbans", on_set_bans, COMMAND_RPC_SETBANS)
MAP_JON_RPC_WE("getbans", on_get_bans, COMMAND_RPC_GETBANS)
MAP_JON_RPC_WE("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL)
+ MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -149,6 +150,7 @@ namespace cryptonote
bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp);
bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp);
bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp);
+ bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp);
//-----------------------
private:
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 72e399ec4..91f5e2c90 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -234,9 +234,27 @@ namespace cryptonote
struct response
{
std::string status;
+ std::string reason;
+ bool not_relayed;
+ bool low_mixin;
+ bool double_spend;
+ bool invalid_input;
+ bool invalid_output;
+ bool too_big;
+ bool overspend;
+ bool fee_too_low;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
+ KV_SERIALIZE(reason)
+ KV_SERIALIZE(not_relayed)
+ KV_SERIALIZE(low_mixin)
+ KV_SERIALIZE(double_spend)
+ KV_SERIALIZE(invalid_input)
+ KV_SERIALIZE(invalid_output)
+ KV_SERIALIZE(too_big)
+ KV_SERIALIZE(overspend)
+ KV_SERIALIZE(fee_too_low)
END_KV_SERIALIZE_MAP()
};
};
@@ -427,6 +445,7 @@ namespace cryptonote
uint64_t reserved_offset;
std::string prev_hash;
blobdata blocktemplate_blob;
+ blobdata blockhashing_blob;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
@@ -435,6 +454,7 @@ namespace cryptonote
KV_SERIALIZE(reserved_offset)
KV_SERIALIZE(prev_hash)
KV_SERIALIZE(blocktemplate_blob)
+ KV_SERIALIZE(blockhashing_blob)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
@@ -986,5 +1006,46 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
+
+ struct COMMAND_RPC_GET_OUTPUT_HISTOGRAM
+ {
+ struct request
+ {
+ std::vector<uint64_t> amounts;
+ uint64_t min_count;
+ uint64_t max_count;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amounts);
+ KV_SERIALIZE(min_count);
+ KV_SERIALIZE(max_count);
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct entry
+ {
+ uint64_t amount;
+ uint64_t instances;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount);
+ KV_SERIALIZE(instances);
+ END_KV_SERIALIZE_MAP()
+
+ entry(uint64_t amount, uint64_t instances): amount(amount), instances(instances) {}
+ entry() {}
+ };
+
+ struct response
+ {
+ std::string status;
+ std::vector<entry> histogram;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(histogram)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
}
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 65508b9d5..aa571755f 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -57,6 +57,8 @@
#include "version.h"
#include "crypto/crypto.h" // for crypto::secret_key definition
#include "mnemonics/electrum-words.h"
+#include "rapidjson/document.h"
+#include "common/json_util.h"
#include <stdexcept>
#if defined(WIN32)
@@ -81,6 +83,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg> or <address>.wallet by default"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
+ const command_line::arg_descriptor<std::string> arg_generate_from_json = {"generate-from-json", sw::tr("Generate wallet from JSON format file"), ""};
const command_line::arg_descriptor<std::string> arg_daemon_address = {"daemon-address", sw::tr("Use daemon instance at <host>:<port>"), ""};
const command_line::arg_descriptor<std::string> arg_daemon_host = {"daemon-host", sw::tr("Use daemon instance at host <arg> instead of localhost"), ""};
const command_line::arg_descriptor<std::string> arg_password = {"password", sw::tr("Wallet password"), "", true};
@@ -255,6 +258,8 @@ bool simple_wallet::seed(const std::vector<std::string> &args/* = std::vector<st
if (m_wallet->get_seed_language().empty())
{
std::string mnemonic_language = get_mnemonic_language();
+ if (mnemonic_language.empty())
+ return true;
m_wallet->set_seed_language(mnemonic_language);
}
@@ -302,6 +307,8 @@ bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = s
}
std::string mnemonic_language = get_mnemonic_language();
+ if (mnemonic_language.empty())
+ return true;
m_wallet->set_seed_language(mnemonic_language);
m_wallet->rewrite(m_wallet_file, pwd_container.password());
return true;
@@ -445,11 +452,12 @@ bool simple_wallet::set_auto_refresh(const std::vector<std::string> &args/* = st
if (auto_refresh && !m_auto_refresh_run.load(std::memory_order_relaxed))
{
m_auto_refresh_run.store(true, std::memory_order_relaxed);
- m_auto_refresh_thread = std::thread([&]{wallet_refresh_thread();});
+ m_auto_refresh_thread = boost::thread([&]{wallet_refresh_thread();});
}
else if (!auto_refresh && m_auto_refresh_run.load(std::memory_order_relaxed))
{
m_auto_refresh_run.store(false, std::memory_order_relaxed);
+ m_wallet->stop();
m_auto_refresh_thread.join();
}
@@ -538,7 +546,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height"));
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 0 to maximum available)"));
m_cmd_binder.set_handler("transfer_new", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer, but using a new transaction building algorithm"));
- m_cmd_binder.set_handler("sweep_dust", boost::bind(&simple_wallet::sweep_dust, this, _1), tr("Send all dust outputs to yourself with mixin 0"));
+ m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0"));
m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log <level> - Change current log detail level, <0-4>"));
m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), tr("Show current wallet public address"));
m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::print_integrated_address, this, _1), tr("integrated_address [PID] - Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID"));
@@ -695,6 +703,10 @@ bool simple_wallet::ask_wallet_create_if_needed()
tr("Specify wallet file name (e.g., MyWallet). If the wallet doesn't exist, it will be created.\n"
"Wallet file name: ")
);
+ if (std::cin.eof())
+ {
+ return false;
+ }
valid_path = tools::wallet2::wallet_valid_path_format(wallet_path);
if (!valid_path)
{
@@ -715,7 +727,7 @@ bool simple_wallet::ask_wallet_create_if_needed()
// add logic to error out if new wallet requested but named wallet file exists
if (keys_file_exists || wallet_file_exists)
{
- if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty())
+ if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty() || !m_generate_from_json.empty())
{
fail_msg_writer() << tr("attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.");
return false;
@@ -760,52 +772,28 @@ void simple_wallet::print_seed(std::string seed)
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::init(const boost::program_options::variables_map& vm)
+static bool get_password(const boost::program_options::variables_map& vm, bool allow_entry, tools::password_container &pwd_container)
{
- if (!handle_command_line(vm))
- return false;
-
- if (!m_daemon_address.empty() && !m_daemon_host.empty() && 0 != m_daemon_port)
- {
- fail_msg_writer() << tr("can't specify daemon host or port more than once");
- return false;
- }
-
- if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_keys.empty()) > 1)
- {
- fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\" and --generate-from-keys=\"wallet_name\"");
- return false;
- }
- else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_keys.empty())
- {
- if(!ask_wallet_create_if_needed()) return false;
- }
-
- bool testnet = command_line::get_arg(vm, arg_testnet);
-
- if (m_daemon_host.empty())
- m_daemon_host = "localhost";
-
- if (!m_daemon_port)
- {
- m_daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT;
+ // has_arg returns true even when the parameter is not passed ??
+ const std::string gfj = command_line::get_arg(vm, arg_generate_from_json);
+ if (!gfj.empty()) {
+ // will be in the json file, if any
+ return true;
}
- if (m_daemon_address.empty())
- m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port);
-
if (has_arg(vm, arg_password) && has_arg(vm, arg_password_file))
{
fail_msg_writer() << tr("can't specify more than one of --password and --password-file");
return false;
}
- tools::password_container pwd_container;
if (command_line::has_arg(vm, arg_password))
{
pwd_container.password(command_line::get_arg(vm, arg_password));
+ return true;
}
- else if (command_line::has_arg(vm, arg_password_file))
+
+ if (command_line::has_arg(vm, arg_password_file))
{
std::string password;
bool r = epee::file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_password_file),
@@ -820,8 +808,10 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
password.erase(std::remove(password.begin() - 1, password.end(), '\n'), password.end());
password.erase(std::remove(password.end() - 1, password.end(), '\r'), password.end());
pwd_container.password(password.c_str());
+ return true;
}
- else
+
+ if (allow_entry)
{
bool r = pwd_container.read_password();
if (!r)
@@ -829,9 +819,231 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("failed to read wallet password");
return false;
}
+ return true;
+ }
+
+ fail_msg_writer() << tr("Wallet password not set.");
+ return false;
+}
+
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::generate_from_json(const boost::program_options::variables_map& vm, std::string &wallet_file, std::string &password)
+{
+ std::string buf;
+ bool r = epee::file_io_utils::load_file_to_string(m_generate_from_json, buf);
+ if (!r) {
+ fail_msg_writer() << tr("Failed to load file ") << m_generate_from_json;
+ return false;
+ }
+
+ rapidjson::Document json;
+ if (json.Parse(buf.c_str()).HasParseError()) {
+ fail_msg_writer() << tr("Failed to parse JSON");
+ return false;
+ }
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, version, unsigned, Uint, true);
+ const int current_version = 1;
+ if (field_version > current_version) {
+ fail_msg_writer() << boost::format(tr("Version %u too new, we can only grok up to %u")) % field_version % current_version;
+ return false;
+ }
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, filename, std::string, String, true);
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, scan_from_height, uint64_t, Uint64, false);
+ bool recover = field_scan_from_height_found;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, password, std::string, String, false);
+ password = field_password;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, viewkey, std::string, String, false);
+ crypto::secret_key viewkey;
+ if (field_viewkey_found)
+ {
+ cryptonote::blobdata viewkey_data;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data))
+ {
+ fail_msg_writer() << tr("failed to parse view key secret key");
+ return false;
+ }
+ viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
+ }
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, spendkey, std::string, String, false);
+ crypto::secret_key spendkey;
+ if (field_spendkey_found)
+ {
+ cryptonote::blobdata spendkey_data;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data))
+ {
+ fail_msg_writer() << tr("failed to parse spend key secret key");
+ return false;
+ }
+ spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
+ }
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed, std::string, String, false);
+ std::string old_language;
+ if (field_seed_found)
+ {
+ if (!crypto::ElectrumWords::words_to_bytes(field_seed, m_recovery_key, old_language))
+ {
+ fail_msg_writer() << tr("Electrum-style word list failed verification");
+ return false;
+ }
+ m_electrum_seed = field_seed;
+ m_restore_deterministic_wallet = true;
+ }
+
+ // compatibility checks
+ if (!field_seed_found && !field_viewkey_found)
+ {
+ fail_msg_writer() << tr("At least one of Electrum-style word list and private view key must be specified");
+ return false;
+ }
+ if (field_seed_found && (field_viewkey_found || field_spendkey_found))
+ {
+ fail_msg_writer() << tr("Both Electrum-style word list and private key(s) specified");
+ return false;
+ }
+
+ m_wallet_file = field_filename;
+
+ bool was_deprecated_wallet = m_restore_deterministic_wallet && ((old_language == crypto::ElectrumWords::old_language_name) ||
+ crypto::ElectrumWords::get_is_old_style_seed(m_electrum_seed));
+ if (was_deprecated_wallet) {
+ fail_msg_writer() << tr("Cannot create deprecated wallets from JSON");
+ return false;
+ }
+
+ bool testnet = command_line::get_arg(vm, arg_testnet);
+ m_wallet.reset(new tools::wallet2(testnet));
+ m_wallet->callback(this);
+
+ try
+ {
+ if (!field_seed.empty())
+ {
+ m_wallet->generate(m_wallet_file, password, m_recovery_key, recover, false);
+ }
+ else
+ {
+ cryptonote::account_public_address address;
+ if (!crypto::secret_key_to_public_key(viewkey, address.m_view_public_key)) {
+ fail_msg_writer() << tr("failed to verify view key secret key");
+ return false;
+ }
+ if (!crypto::secret_key_to_public_key(spendkey, address.m_spend_public_key)) {
+ fail_msg_writer() << tr("failed to verify spend key secret key");
+ return false;
+ }
+
+ if (field_spendkey.empty())
+ {
+ m_wallet->generate(m_wallet_file, password, address, viewkey);
+ }
+ else
+ {
+ m_wallet->generate(m_wallet_file, password, address, spendkey, viewkey);
+ }
+ }
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
+ return false;
+ }
+
+ m_wallet->set_refresh_from_block_height(field_scan_from_height);
+
+ wallet_file = m_wallet_file;
+
+ return r;
+}
+
+static bool is_local_daemon(const std::string &address)
+{
+ // extract host
+ epee::net_utils::http::url_content u_c;
+ if (!epee::net_utils::parse_url(address, u_c))
+ {
+ LOG_PRINT_L1("Failed to determine whether daemon is local, assuming not");
+ return false;
+ }
+ if (u_c.host.empty())
+ {
+ LOG_PRINT_L1("Failed to determine whether daemon is local, assuming not");
+ return false;
+ }
+
+ // resolve to IP
+ boost::asio::io_service io_service;
+ boost::asio::ip::tcp::resolver resolver(io_service);
+ boost::asio::ip::tcp::resolver::query query(u_c.host, "");
+ boost::asio::ip::tcp::resolver::iterator i = resolver.resolve(query);
+ while (i != boost::asio::ip::tcp::resolver::iterator())
+ {
+ const boost::asio::ip::tcp::endpoint &ep = *i;
+ if (ep.address().is_loopback())
+ return true;
+ ++i;
+ }
+
+ return false;
+}
+
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::init(const boost::program_options::variables_map& vm)
+{
+ if (!handle_command_line(vm))
+ return false;
+
+ if (!m_daemon_address.empty() && !m_daemon_host.empty() && 0 != m_daemon_port)
+ {
+ fail_msg_writer() << tr("can't specify daemon host or port more than once");
+ return false;
+ }
+
+ if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_json.empty()) > 1)
+ {
+ fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-json=\"jsonfilename\" and --generate-from-keys=\"wallet_name\"");
+ return false;
+ }
+ else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_view_key.empty() && m_generate_from_keys.empty() && m_generate_from_json.empty())
+ {
+ if(!ask_wallet_create_if_needed()) return false;
+ }
+
+ bool testnet = command_line::get_arg(vm, arg_testnet);
+
+ if (m_daemon_host.empty())
+ m_daemon_host = "localhost";
+
+ if (!m_daemon_port)
+ {
+ m_daemon_port = testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT;
+ }
+
+ if (m_daemon_address.empty())
+ m_daemon_address = std::string("http://") + m_daemon_host + ":" + std::to_string(m_daemon_port);
+
+ // set --trusted-daemon if local
+ try
+ {
+ if (is_local_daemon(m_daemon_address))
+ {
+ LOG_PRINT_L1(tr("Daemon is local, assuming trusted"));
+ m_trusted_daemon = true;
+ }
}
+ catch (const std::exception &e) { }
- if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty())
+ tools::password_container pwd_container;
+ if (!get_password(vm, true, pwd_container))
+ return false;
+
+ if (!m_generate_new.empty() || m_restore_deterministic_wallet || !m_generate_from_view_key.empty() || !m_generate_from_keys.empty() || !m_generate_from_json.empty())
{
if (m_wallet_file.empty()) m_wallet_file = m_generate_new; // alias for simplicity later
@@ -848,6 +1060,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (m_electrum_seed.empty())
{
m_electrum_seed = command_line::input_line("Specify Electrum seed: ");
+ if (std::cin.eof())
+ return false;
if (m_electrum_seed.empty())
{
fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
@@ -865,6 +1079,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
// parse address
std::string address_string = command_line::input_line("Standard address: ");
+ if (std::cin.eof())
+ return false;
if (address_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
@@ -880,6 +1096,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
// parse view secret key
std::string viewkey_string = command_line::input_line("View key: ");
+ if (std::cin.eof())
+ return false;
if (viewkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
@@ -912,6 +1130,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
// parse address
std::string address_string = command_line::input_line("Standard address: ");
+ if (std::cin.eof())
+ return false;
if (address_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
@@ -927,6 +1147,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
// parse spend secret key
std::string spendkey_string = command_line::input_line("Spend key: ");
+ if (std::cin.eof())
+ return false;
if (spendkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
@@ -941,6 +1163,8 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
// parse view secret key
std::string viewkey_string = command_line::input_line("View key: ");
+ if (std::cin.eof())
+ return false;
if (viewkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
@@ -977,6 +1201,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
bool r = new_wallet(m_wallet_file, pwd_container.password(), address, spendkey, viewkey, testnet);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
}
+ else if (!m_generate_from_json.empty())
+ {
+ std::string wallet_file, password; // we don't need to remember them
+ if (!generate_from_json(vm, wallet_file, password))
+ return false;
+ }
else
{
bool r = new_wallet(m_wallet_file, pwd_container.password(), m_recovery_key, m_restore_deterministic_wallet,
@@ -1007,6 +1237,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
m_generate_from_view_key = command_line::get_arg(vm, arg_generate_from_view_key);
m_generate_from_keys = command_line::get_arg(vm, arg_generate_from_keys);
+ m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json);
m_daemon_address = command_line::get_arg(vm, arg_daemon_address);
m_daemon_host = command_line::get_arg(vm, arg_daemon_host);
m_daemon_port = command_line::get_arg(vm, arg_daemon_port);
@@ -1053,6 +1284,8 @@ std::string simple_wallet::get_mnemonic_language()
while (language_number < 0)
{
language_choice = command_line::input_line(tr("Enter the number corresponding to the language of your choice: "));
+ if (std::cin.eof())
+ return std::string();
try
{
language_number = std::stoi(language_choice);
@@ -1091,6 +1324,8 @@ bool simple_wallet::new_wallet(const std::string &wallet_file, const std::string
"a deprecated version of the wallet. Please use the new seed that we provide.\n");
}
mnemonic_language = get_mnemonic_language();
+ if (mnemonic_language.empty())
+ return false;
}
m_wallet_file = wallet_file;
@@ -1218,6 +1453,8 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa
message_writer(epee::log_space::console_color_green, false) << "\n" << tr("You had been using "
"a deprecated version of the wallet. Please proceed to upgrade your wallet.\n");
std::string mnemonic_language = get_mnemonic_language();
+ if (mnemonic_language.empty())
+ return false;
m_wallet->set_seed_language(mnemonic_language);
m_wallet->rewrite(m_wallet_file, password);
@@ -1256,7 +1493,7 @@ bool simple_wallet::close_wallet()
m_auto_refresh_run.store(false, std::memory_order_relaxed);
m_wallet->stop();
{
- std::unique_lock<std::mutex> lock(m_auto_refresh_mutex);
+ boost::unique_lock<boost::mutex> lock(m_auto_refresh_mutex);
m_auto_refresh_cond.notify_one();
}
m_auto_refresh_thread.join();
@@ -1341,7 +1578,7 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
bool ok = true;
- size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast<unsigned>(2));
+ size_t max_mining_threads_count = (std::max)(boost::thread::hardware_concurrency(), static_cast<unsigned>(2));
if (0 == args.size())
{
req.threads_count = 1;
@@ -1458,7 +1695,7 @@ bool simple_wallet::refresh_main(uint64_t start_height, bool reset)
m_auto_refresh_run.store(false, std::memory_order_relaxed);
// stop any background refresh, and take over
m_wallet->stop();
- std::unique_lock<std::mutex> lock(m_auto_refresh_mutex);
+ boost::unique_lock<boost::mutex> lock(m_auto_refresh_mutex);
m_auto_refresh_cond.notify_one();
if (reset)
@@ -1543,8 +1780,7 @@ bool simple_wallet::refresh(const std::vector<std::string>& args)
bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/)
{
success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance()) << ", "
- << tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance()) << ", "
- << tr("including unlocked dust: ") << print_money(m_wallet->unlocked_dust_balance(tools::tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD)));
+ << tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance());
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -1835,6 +2071,10 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
// prompt the user for confirmation given the dns query and dnssec status
std::string confirm_dns_ok = command_line::input_line(prompt.str());
+ if (std::cin.eof())
+ {
+ return true;
+ }
if (confirm_dns_ok != "Y" && confirm_dns_ok != "y" && confirm_dns_ok != "Yes" && confirm_dns_ok != "yes"
&& confirm_dns_ok != tr("yes") && confirm_dns_ok != tr("no"))
{
@@ -1919,6 +2159,8 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
print_money(total_fee)).str();
}
std::string accepted = command_line::input_line(prompt_str);
+ if (std::cin.eof())
+ return true;
if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes")
{
fail_msg_writer() << tr("transaction cancelled.");
@@ -1981,6 +2223,9 @@ bool simple_wallet::transfer_main(bool new_algorithm, const std::vector<std::str
catch (const tools::error::tx_rejected& e)
{
fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
+ std::string reason = e.reason();
+ if (!reason.empty())
+ fail_msg_writer() << tr("Reason: ") << reason;
}
catch (const tools::error::tx_sum_overflow& e)
{
@@ -2029,7 +2274,7 @@ bool simple_wallet::transfer_new(const std::vector<std::string> &args_)
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::sweep_dust(const std::vector<std::string> &args_)
+bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
{
if (!try_connect_to_daemon())
return true;
@@ -2042,31 +2287,42 @@ bool simple_wallet::sweep_dust(const std::vector<std::string> &args_)
try
{
- uint64_t total_dust = m_wallet->unlocked_dust_balance(tools::tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD));
-
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_dust_sweep_transactions();
+ auto ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
+
+ if (ptx_vector.empty())
+ {
+ fail_msg_writer() << tr("No unmixable outputs found");
+ return true;
+ }
// give user total and fee, and prompt to confirm
- uint64_t total_fee = 0;
+ uint64_t total_fee = 0, total_unmixable = 0;
for (size_t n = 0; n < ptx_vector.size(); ++n)
{
total_fee += ptx_vector[n].fee;
+ for (const auto &vin: ptx_vector[n].tx.vin)
+ {
+ if (vin.type() == typeid(txin_to_key))
+ total_unmixable += boost::get<txin_to_key>(vin).amount;
+ }
}
- std::string prompt_str = tr("Sweeping ") + print_money(total_dust);
+ std::string prompt_str = tr("Sweeping ") + print_money(total_unmixable);
if (ptx_vector.size() > 1) {
prompt_str = (boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay? (Y/Yes/N/No)")) %
- print_money(total_dust) %
+ print_money(total_unmixable) %
((unsigned long long)ptx_vector.size()) %
print_money(total_fee)).str();
}
else {
prompt_str = (boost::format(tr("Sweeping %s for a total fee of %s. Is this okay? (Y/Yes/N/No)")) %
- print_money(total_dust) %
+ print_money(total_unmixable) %
print_money(total_fee)).str();
}
std::string accepted = command_line::input_line(prompt_str);
+ if (std::cin.eof())
+ return true;
if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes")
{
fail_msg_writer() << tr("transaction cancelled.");
@@ -2106,11 +2362,12 @@ bool simple_wallet::sweep_dust(const std::vector<std::string> &args_)
}
catch (const tools::error::not_enough_money& e)
{
- fail_msg_writer() << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)")) %
+ fail_msg_writer() << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee).\n%s")) %
print_money(e.available()) %
print_money(e.tx_amount() + e.fee()) %
print_money(e.tx_amount()) %
- print_money(e.fee());
+ print_money(e.fee()) %
+ tr("This is usually due to dust which is so small it cannot pay for itself in fees");
}
catch (const tools::error::not_enough_outs_to_mix& e)
{
@@ -2128,6 +2385,9 @@ bool simple_wallet::sweep_dust(const std::vector<std::string> &args_)
catch (const tools::error::tx_rejected& e)
{
fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
+ std::string reason = e.reason();
+ if (!reason.empty())
+ fail_msg_writer() << tr("Reason: ") << reason;
}
catch (const tools::error::tx_sum_overflow& e)
{
@@ -2434,7 +2694,7 @@ void simple_wallet::wallet_refresh_thread()
{
while (true)
{
- std::unique_lock<std::mutex> lock(m_auto_refresh_mutex);
+ boost::unique_lock<boost::mutex> lock(m_auto_refresh_mutex);
if (!m_auto_refresh_run.load(std::memory_order_relaxed))
break;
m_auto_refresh_refreshing = true;
@@ -2447,7 +2707,7 @@ void simple_wallet::wallet_refresh_thread()
m_auto_refresh_refreshing = false;
if (!m_auto_refresh_run.load(std::memory_order_relaxed))
break;
- m_auto_refresh_cond.wait_for(lock, chrono::seconds(90));
+ m_auto_refresh_cond.wait_for(lock, boost::chrono::seconds(90));
}
}
//----------------------------------------------------------------------------------------------------
@@ -2457,7 +2717,7 @@ bool simple_wallet::run()
m_auto_refresh_run = m_wallet->auto_refresh();
if (m_auto_refresh_run)
{
- m_auto_refresh_thread = std::thread([&]{wallet_refresh_thread();});
+ m_auto_refresh_thread = boost::thread([&]{wallet_refresh_thread();});
}
else
{
@@ -2559,6 +2819,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_generate_new_wallet);
command_line::add_arg(desc_params, arg_generate_from_view_key);
command_line::add_arg(desc_params, arg_generate_from_keys);
+ command_line::add_arg(desc_params, arg_generate_from_json);
command_line::add_arg(desc_params, arg_password);
command_line::add_arg(desc_params, arg_password_file);
command_line::add_arg(desc_params, arg_daemon_address);
@@ -2674,16 +2935,13 @@ int main(int argc, char* argv[])
LOG_ERROR(sw::tr("Daemon address not set."));
return 1;
}
- if(!command_line::has_arg(vm, arg_password) )
- {
- LOG_ERROR(sw::tr("Wallet password not set."));
- return 1;
- }
bool testnet = command_line::get_arg(vm, arg_testnet);
bool restricted = command_line::get_arg(vm, arg_restricted);
std::string wallet_file = command_line::get_arg(vm, arg_wallet_file);
- std::string wallet_password = command_line::get_arg(vm, arg_password);
+ tools::password_container pwd_container;
+ if (!get_password(vm, false, pwd_container))
+ return 1;
std::string daemon_address = command_line::get_arg(vm, arg_daemon_address);
std::string daemon_host = command_line::get_arg(vm, arg_daemon_host);
int daemon_port = command_line::get_arg(vm, arg_daemon_port);
@@ -2694,11 +2952,21 @@ int main(int argc, char* argv[])
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
+ std::string password;
+ const std::string gfj = command_line::get_arg(vm, arg_generate_from_json);
+ if (!gfj.empty()) {
+ if (!w.generate_from_json(vm, wallet_file, password))
+ return 1;
+ }
+ else {
+ password = pwd_container.password();
+ }
+
tools::wallet2 wal(testnet,restricted);
try
{
LOG_PRINT_L0(sw::tr("Loading wallet..."));
- wal.load(wallet_file, wallet_password);
+ wal.load(wallet_file, password);
wal.init(daemon_address);
wal.refresh();
LOG_PRINT_GREEN(sw::tr("Loaded ok"), LOG_LEVEL_0);
@@ -2746,7 +3014,11 @@ int main(int argc, char* argv[])
else
{
tools::signal_handler::install([&w](int type) {
+#ifdef WIN32
+ if (type == CTRL_C_EVENT)
+#else
if (type == SIGINT)
+#endif
{
// if we're pressing ^C when refreshing, just stop refreshing
w.interrupt();
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 5dac60447..21bbfa566 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -69,6 +69,7 @@ namespace cryptonote
bool run();
void stop();
void interrupt();
+ bool generate_from_json(const boost::program_options::variables_map& vm, std::string &wallet_file, std::string &password);
//wallet *create_wallet();
bool process_command(const std::vector<std::string> &args);
@@ -120,7 +121,7 @@ namespace cryptonote
bool transfer_main(bool new_algorithm, const std::vector<std::string> &args);
bool transfer(const std::vector<std::string> &args);
bool transfer_new(const std::vector<std::string> &args);
- bool sweep_dust(const std::vector<std::string> &args);
+ bool sweep_unmixable(const std::vector<std::string> &args);
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
);
@@ -221,6 +222,7 @@ namespace cryptonote
std::string m_generate_new;
std::string m_generate_from_view_key;
std::string m_generate_from_keys;
+ std::string m_generate_from_json;
std::string m_import_path;
std::string m_electrum_seed; // electrum-style seed parameter
@@ -242,9 +244,9 @@ namespace cryptonote
std::atomic<bool> m_auto_refresh_run;
bool m_auto_refresh_refreshing;
- std::thread m_auto_refresh_thread;
- std::mutex m_auto_refresh_mutex;
- std::condition_variable m_auto_refresh_cond;
+ boost::thread m_auto_refresh_thread;
+ boost::mutex m_auto_refresh_mutex;
+ boost::condition_variable m_auto_refresh_cond;
std::atomic<bool> m_in_manual_refresh;
};
}
diff --git a/src/version.h.in b/src/version.h.in
index 08e39c00c..65b899ed2 100644
--- a/src/version.h.in
+++ b/src/version.h.in
@@ -1,4 +1,4 @@
#define MONERO_VERSION_TAG "@VERSIONTAG@"
-#define MONERO_VERSION "0.9.1.0"
+#define MONERO_VERSION "0.9.4.0"
#define MONERO_RELEASE_NAME "Hydrogen Helix"
#define MONERO_VERSION_FULL MONERO_VERSION "-" MONERO_VERSION_TAG
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index e000c582d..a6fc37dec 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -26,9 +26,13 @@
# 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(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+
set(wallet_sources
wallet2.cpp
- wallet_rpc_server.cpp)
+ wallet_rpc_server.cpp
+ wallet2_api.cpp)
set(wallet_headers)
@@ -37,7 +41,8 @@ set(wallet_private_headers
wallet_errors.h
wallet_rpc_server.h
wallet_rpc_server_commands_defs.h
- wallet_rpc_server_error_codes.h)
+ wallet_rpc_server_error_codes.h
+ wallet2_api.h)
bitmonero_private_headers(wallet
${wallet_private_headers})
@@ -53,4 +58,6 @@ target_link_libraries(wallet
${Boost_SERIALIZATION_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
+ ${Boost_REGEX_LIBRARY}
${EXTRA_LIBRARIES})
+
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index a86f2ffdf..a9a65535f 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -52,6 +52,7 @@ using namespace epee;
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
+#include "common/json_util.h"
extern "C"
{
@@ -92,6 +93,13 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
}
}
+uint64_t calculate_fee(const cryptonote::blobdata &blob)
+{
+ uint64_t bytes = blob.size();
+ uint64_t kB = (bytes + 1023) / 1024;
+ return kB * FEE_PER_KB;
+}
+
} //namespace
namespace tools
@@ -204,7 +212,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_
tx_pub_key = pub_key_field.pub_key;
bool r = true;
- int threads = std::thread::hardware_concurrency();
+ int threads = boost::thread::hardware_concurrency();
if (miner_tx && m_refresh_type == RefreshNoCoinbase)
{
// assume coinbase isn't for us
@@ -490,7 +498,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
//handle transactions from new block
//optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
- if(b.timestamp + 60*60*24 > m_account.get_createtime())
+ if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
process_new_transaction(b.miner_tx, height, true);
@@ -574,7 +582,7 @@ void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::
size_t current_index = start_height;
blocks_added = 0;
- int threads = std::thread::hardware_concurrency();
+ int threads = boost::thread::hardware_concurrency();
if (threads > 1)
{
std::vector<crypto::hash> round_block_hashes(threads);
@@ -771,7 +779,7 @@ 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() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash;
std::list<crypto::hash> short_chain_history;
- std::thread pull_thread;
+ boost::thread pull_thread;
uint64_t blocks_start_height;
std::list<cryptonote::block_complete_entry> blocks;
@@ -788,7 +796,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re
uint64_t next_blocks_start_height;
std::list<cryptonote::block_complete_entry> next_blocks;
bool error = false;
- pull_thread = std::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, error);});
+ pull_thread = boost::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks, error);});
process_blocks(blocks_start_height, blocks, added_blocks);
blocks_fetched += added_blocks;
@@ -997,7 +1005,7 @@ namespace
* \param keys_file_name Name of wallet file
* \param password Password of wallet file
*/
-void wallet2::load_keys(const std::string& keys_file_name, const std::string& password)
+bool wallet2::load_keys(const std::string& keys_file_name, const std::string& password)
{
wallet2::keys_file_data keys_file_data;
std::string buf;
@@ -1026,34 +1034,44 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
}
else
{
- account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() +
- json["key_data"].GetStringLength());
- if (json.HasMember("seed_language"))
+ if (!json.HasMember("key_data"))
{
- set_seed_language(std::string(json["seed_language"].GetString(), json["seed_language"].GetString() +
- json["seed_language"].GetStringLength()));
+ LOG_ERROR("Field key_data not found in JSON");
+ return false;
}
- if (json.HasMember("watch_only"))
+ if (!json["key_data"].IsString())
{
- m_watch_only = json["watch_only"].GetInt() != 0;
+ LOG_ERROR("Field key_data found in JSON, but not String");
+ return false;
}
- else
+ const char *field_key_data = json["key_data"].GetString();
+ account_data = std::string(field_key_data, field_key_data + json["key_data"].GetStringLength());
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false);
+ if (field_seed_language_found)
{
- m_watch_only = false;
+ set_seed_language(field_seed_language);
}
- m_always_confirm_transfers = json.HasMember("always_confirm_transfers") && (json["always_confirm_transfers"].GetInt() != 0);
- m_store_tx_info = (json.HasMember("store_tx_keys") && (json["store_tx_keys"].GetInt() != 0))
- || (json.HasMember("store_tx_info") && (json["store_tx_info"].GetInt() != 0));
- m_default_mixin = json.HasMember("default_mixin") ? json["default_mixin"].GetUint() : 0;
- m_auto_refresh = !json.HasMember("auto_refresh") || (json["auto_refresh"].GetInt() != 0);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false);
+ m_watch_only = field_watch_only_found && field_watch_only;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false);
+ m_always_confirm_transfers = field_always_confirm_transfers_found && field_always_confirm_transfers;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false);
+ m_store_tx_info = (field_store_tx_keys_found && (field_store_tx_keys != 0))
+ || (field_store_tx_info_found && (field_store_tx_info != 0));
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false);
+ m_default_mixin = field_default_mixin_found ? field_default_mixin : 0;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false);
+ m_auto_refresh = !field_auto_refresh_found || (field_auto_refresh != 0);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false);
m_refresh_type = RefreshType::RefreshDefault;
- if (json.HasMember("refresh_type"))
+ if (field_refresh_type_found)
{
- int type = json["refresh_type"].GetInt();
- if (type == RefreshFull || type == RefreshOptimizeCoinbase || type == RefreshNoCoinbase)
- m_refresh_type = (RefreshType)type;
+ if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase)
+ m_refresh_type = (RefreshType)field_refresh_type;
else
- LOG_PRINT_L0("Unknown refresh-type value (" << type << "), using default");
+ LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default");
}
}
@@ -1063,6 +1081,7 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
if(!m_watch_only)
r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key);
THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password);
+ return true;
}
/*!
@@ -1207,7 +1226,7 @@ void wallet2::generate(const std::string& wallet_, const std::string& password,
m_account_public_address = account_public_address;
m_watch_only = false;
- bool r = store_keys(m_keys_file, password, true);
+ bool r = store_keys(m_keys_file, password, false);
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet));
@@ -1312,7 +1331,7 @@ bool wallet2::prepare_file_names(const std::string& file_path)
//----------------------------------------------------------------------------------------------------
bool wallet2::check_connection()
{
- std::lock_guard<std::mutex> lock(m_daemon_rpc_mutex);
+ boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex);
if(m_http_client.is_connected())
return true;
@@ -1351,7 +1370,10 @@ void wallet2::load(const std::string& wallet_, const std::string& password)
bool exists = boost::filesystem::exists(m_keys_file, e);
THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file);
- load_keys(m_keys_file, password);
+ if (!load_keys(m_keys_file, password))
+ {
+ THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file);
+ }
LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_testnet));
//keys loaded ok!
@@ -1461,6 +1483,78 @@ void wallet2::store()
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
boost::filesystem::remove(old_file);
}
+
+void wallet2::store_to(const std::string &path, const std::string &password)
+{
+ // TODO: merge it with wallet2::store() function
+
+ // check if we want to store to directory which doesn't exists yet
+ boost::filesystem::path parent_path = boost::filesystem::path(path).parent_path();
+
+ // if path is not exists, try to create it
+ if (!parent_path.empty() && !boost::filesystem::exists(parent_path)) {
+ boost::system::error_code ec;
+ if (!boost::filesystem::create_directories(parent_path, ec)) {
+ throw std::logic_error(ec.message());
+ }
+ }
+
+
+ std::stringstream oss;
+ boost::archive::binary_oarchive ar(oss);
+ ar << *this;
+
+ wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
+ cache_file_data.cache_data = oss.str();
+ crypto::chacha8_key key;
+ generate_chacha8_key_from_secret_keys(key);
+ std::string cipher;
+ cipher.resize(cache_file_data.cache_data.size());
+ cache_file_data.iv = crypto::rand<crypto::chacha8_iv>();
+ crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]);
+ cache_file_data.cache_data = cipher;
+
+
+ const std::string new_file = path;
+ const std::string old_file = m_wallet_file;
+ const std::string old_keys_file = m_keys_file;
+ const std::string old_address_file = m_wallet_file + ".address.txt";
+
+ // save to new file
+ std::ofstream ostr;
+ ostr.open(new_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ binary_archive<true> oar(ostr);
+ bool success = ::serialization::serialize(oar, cache_file_data);
+ ostr.close();
+ THROW_WALLET_EXCEPTION_IF(!success || !ostr.good(), error::file_save_error, new_file);
+
+ // save keys to the new file
+ // if we here, main wallet file is saved and we only need to save keys and address files
+ prepare_file_names(path);
+ store_keys(m_keys_file, password, false);
+
+ // save address to the new file
+ const std::string address_file = m_wallet_file + ".address.txt";
+ bool r = file_io_utils::save_string_to_file(address_file, m_account.get_public_address_str(m_testnet));
+ THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file);
+
+
+ // remove old wallet file
+ r = boost::filesystem::remove(old_file);
+ if (!r) {
+ LOG_ERROR("error removing file: " << old_file);
+ }
+ // remove old keys file
+ r = boost::filesystem::remove(old_keys_file);
+ if (!r) {
+ LOG_ERROR("error removing file: " << old_keys_file);
+ }
+ // remove old address file
+ r = boost::filesystem::remove(old_address_file);
+ if (!r) {
+ LOG_ERROR("error removing file: " << old_address_file);
+ }
+}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::unlocked_balance() const
{
@@ -1886,7 +1980,7 @@ void wallet2::commit_tx(pending_tx& ptx)
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction");
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
- THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status, daemon_send_resp.reason);
txid = get_transaction_hash(ptx.tx);
crypto::hash payment_id = cryptonote::null_hash;
@@ -1958,13 +2052,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{
transfer(dst_vector, fake_outs_count, unlock_time, needed_fee, extra, tx, ptx);
auto txBlob = t_serializable_object_to_blob(ptx.tx);
- uint64_t txSize = txBlob.size();
- uint64_t numKB = txSize / 1024;
- if (txSize % 1024)
- {
- numKB++;
- }
- needed_fee = numKB * FEE_PER_KB;
+ needed_fee = calculate_fee(txBlob);
} while (ptx.fee < needed_fee);
ptx_vector.push_back(ptx);
@@ -2336,15 +2424,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
- uint64_t txSize = txBlob.size();
- uint64_t numKB = txSize / 1024;
- if (txSize % 1024)
- {
- numKB++;
- }
- needed_fee = numKB * FEE_PER_KB;
+ needed_fee = calculate_fee(txBlob);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
- LOG_PRINT_L2("Made a " << numKB << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
+ LOG_PRINT_L2("Made a " << txBlob.size() << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)");
if (needed_fee > available_for_fee && dsts[0].amount > 0)
@@ -2441,7 +2523,7 @@ uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
}
template<typename T>
-void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
+void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
{
using namespace cryptonote;
@@ -2451,6 +2533,19 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
// throw if there are none
uint64_t money = 0;
std::list<transfer_container::iterator> selected_transfers;
+#if 1
+ for (size_t n = 0; n < outs.size(); ++n)
+ {
+ const transfer_details& td = m_transfers[outs[n]];
+ if (!td.m_spent)
+ {
+ selected_transfers.push_back (m_transfers.begin() + outs[n]);
+ money += td.amount();
+ if (selected_transfers.size() >= num_outputs)
+ break;
+ }
+ }
+#else
for (transfer_container::iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
{
const transfer_details& td = *i;
@@ -2462,6 +2557,7 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
break;
}
}
+#endif
// we don't allow no output to self, easier, but one may want to burn the dust if = fee
THROW_WALLET_EXCEPTION_IF(money <= needed_fee, error::not_enough_money, money, needed_fee, needed_fee);
@@ -2555,8 +2651,8 @@ bool wallet2::use_fork_rules(uint8_t version)
r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, false, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, false, "Failed to get hard fork status");
+ CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, false, "Failed to get hard fork status");
bool close_enough = res.height >= resp_t.result.earliest_height - 10; // start using the rules a bit beforehand
if (close_enough)
@@ -2574,20 +2670,85 @@ uint64_t wallet2::get_upper_tranaction_size_limit()
return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
-std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
+std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f)
+{
+ std::vector<size_t> outputs;
+ size_t n = 0;
+ for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
+ {
+ if (i->m_spent)
+ continue;
+ if (!is_transfer_unlocked(*i))
+ continue;
+ if (f(*i))
+ outputs.push_back(n);
+ }
+ return outputs;
+}
+//----------------------------------------------------------------------------------------------------
+std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
+{
+ std::set<uint64_t> set;
+ for (const auto &td: m_transfers)
+ {
+ if (!td.m_spent)
+ set.insert(td.amount());
+ }
+ std::vector<uint64_t> vector;
+ vector.reserve(set.size());
+ for (const auto &i: set)
+ {
+ vector.push_back(i);
+ }
+ return vector;
+}
+//----------------------------------------------------------------------------------------------------
+std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon)
+{
+ // request all outputs with at least 3 instances, so we can use mixin 2 with
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
+ m_daemon_rpc_mutex.lock();
+ req_t.jsonrpc = "2.0";
+ req_t.id = epee::serialization::storage_entry(0);
+ req_t.method = "get_output_histogram";
+ if (trusted_daemon)
+ req_t.params.amounts = get_unspent_amounts_vector();
+ req_t.params.min_count = 3;
+ req_t.params.max_count = 0;
+ bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_unmixable_outputs");
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
+ THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status);
+
+ std::set<uint64_t> mixable;
+ for (const auto &i: resp_t.result.histogram)
+ {
+ mixable.insert(i.amount);
+ }
+
+ return select_available_outputs([mixable](const transfer_details &td) {
+ const uint64_t amount = td.amount();
+ if (mixable.find(amount) == mixable.end())
+ return true;
+ return false;
+ });
+}
+//----------------------------------------------------------------------------------------------------
+std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bool trusted_daemon)
{
// From hard fork 1, we don't consider small amounts to be dust anymore
const bool hf1_rules = use_fork_rules(2); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
- size_t num_dust_outputs = 0;
- for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
+ // may throw
+ std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(trusted_daemon);
+ size_t num_dust_outputs = unmixable_outputs.size();
+
+ if (num_dust_outputs == 0)
{
- const transfer_details& td = *i;
- if (!td.m_spent && (td.amount() < dust_policy.dust_threshold || !is_valid_decomposed_amount(td.amount())) && is_transfer_unlocked(td))
- {
- num_dust_outputs++;
- }
+ return std::vector<wallet2::pending_tx>();
}
// failsafe split attempt counter
@@ -2610,23 +2771,18 @@ std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
// loop until fee is met without increasing tx size to next KB boundary.
uint64_t needed_fee = 0;
- if (1)
+ do
{
- transfer_dust(num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
+ transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
auto txBlob = t_serializable_object_to_blob(ptx.tx);
- uint64_t txSize = txBlob.size();
- uint64_t numKB = txSize / 1024;
- if (txSize % 1024)
- {
- numKB++;
- }
- needed_fee = numKB * FEE_PER_KB;
+ needed_fee = calculate_fee(txBlob);
// reroll the tx with the actual amount minus the fee
// if there's not enough for the fee, it'll throw
- transfer_dust(num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
+ transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
txBlob = t_serializable_object_to_blob(ptx.tx);
- }
+ needed_fee = calculate_fee(txBlob);
+ } while (ptx.fee < needed_fee);
ptx_vector.push_back(ptx);
@@ -2695,6 +2851,16 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) c
return true;
}
+std::string wallet2::get_wallet_file() const
+{
+ return m_wallet_file;
+}
+
+std::string wallet2::get_keys_file() const
+{
+ return m_keys_file;
+}
+
//----------------------------------------------------------------------------------------------------
void wallet2::generate_genesis(cryptonote::block& b) {
if (m_testnet)
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index ceeef492e..b6466d3f6 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -88,10 +88,10 @@ namespace tools
};
private:
- wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true) {}
+ wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0) {}
public:
- wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true) {}
+ wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0) {}
struct transfer_details
{
uint64_t m_block_height;
@@ -212,6 +212,12 @@ namespace tools
void write_watch_only_wallet(const std::string& wallet_name, const std::string& password);
void load(const std::string& wallet, const std::string& password);
void store();
+ /*!
+ * \brief store_to - stores wallet to another file(s), deleting old ones
+ * \param path - path to the wallet file (keys and address filenames will be generated based on this filename)
+ * \param password - password to protect new wallet (TODO: probably better save the password in the wallet object?)
+ */
+ void store_to(const std::string &path, const std::string &password);
/*!
* \brief verifies given password is correct for default wallet keys file
@@ -220,6 +226,8 @@ namespace tools
cryptonote::account_base& get_account(){return m_account;}
const cryptonote::account_base& get_account()const{return m_account;}
+ void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;}
+
// upper_transaction_size_limit as defined below is set to
// approximately 125% of the fixed minimum allowable penalty
// free block size. TODO: fix this so that it actually takes
@@ -272,7 +280,7 @@ namespace tools
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra);
void transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx);
template<typename T>
- void transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
+ void transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
template<typename T>
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
@@ -281,7 +289,7 @@ namespace tools
void commit_tx(std::vector<pending_tx>& ptx_vector);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector<uint8_t> extra);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra);
- std::vector<pending_tx> create_dust_sweep_transactions();
+ std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
bool check_connection();
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
@@ -313,6 +321,9 @@ namespace tools
if(ver < 9)
return;
a & m_confirmed_txs;
+ if(ver < 11)
+ return;
+ a & m_refresh_from_block_height;
}
/*!
@@ -348,6 +359,11 @@ namespace tools
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
+
+ bool use_fork_rules(uint8_t version);
+
+ std::string get_wallet_file() const;
+ std::string get_keys_file() const;
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -362,7 +378,7 @@ namespace tools
* \param keys_file_name Name of wallet file
* \param password Password of wallet file
*/
- void load_keys(const std::string& keys_file_name, const std::string& password);
+ bool load_keys(const std::string& keys_file_name, const std::string& password);
void process_new_transaction(const cryptonote::transaction& tx, uint64_t height, bool miner_tx);
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height);
void detach_blockchain(uint64_t height);
@@ -384,9 +400,11 @@ namespace tools
crypto::hash get_payment_id(const pending_tx &ptx) const;
void check_acc_out(const cryptonote::account_keys &acc, const cryptonote::tx_out &o, const crypto::public_key &tx_pub_key, size_t i, uint64_t &money_transfered, bool &error) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
- bool use_fork_rules(uint8_t version);
uint64_t get_upper_tranaction_size_limit();
void check_pending_txes();
+ std::vector<uint64_t> get_unspent_amounts_vector();
+ std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
+ std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
cryptonote::account_base m_account;
std::string m_daemon_address;
@@ -407,7 +425,7 @@ namespace tools
std::atomic<bool> m_run;
- std::mutex m_daemon_rpc_mutex;
+ boost::mutex m_daemon_rpc_mutex;
i_wallet2_callback* m_callback;
bool m_testnet;
@@ -420,9 +438,10 @@ namespace tools
uint32_t m_default_mixin;
RefreshType m_refresh_type;
bool m_auto_refresh;
+ uint64_t m_refresh_from_block_height;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 10)
+BOOST_CLASS_VERSION(tools::wallet2, 11)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 0)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 2)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 1)
diff --git a/src/wallet/wallet2_api.cpp b/src/wallet/wallet2_api.cpp
new file mode 100644
index 000000000..0644e3690
--- /dev/null
+++ b/src/wallet/wallet2_api.cpp
@@ -0,0 +1,346 @@
+// Copyright (c) 2014-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.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "wallet2_api.h"
+#include "wallet2.h"
+#include "mnemonics/electrum-words.h"
+#include <memory>
+
+namespace epee {
+ unsigned int g_test_dbg_lock_sleep = 0;
+}
+
+namespace Bitmonero {
+
+struct WalletManagerImpl;
+
+namespace {
+ static WalletManagerImpl * g_walletManager = nullptr;
+
+
+
+}
+
+Wallet::~Wallet() {}
+
+///////////////////////// Wallet implementation ////////////////////////////////
+class WalletImpl : public Wallet
+{
+public:
+ WalletImpl();
+ ~WalletImpl();
+ bool create(const std::string &path, const std::string &password,
+ const std::string &language);
+ bool open(const std::string &path, const std::string &password);
+ bool recover(const std::string &path, const std::string &seed);
+ bool close();
+ std::string seed() const;
+ std::string getSeedLanguage() const;
+ void setSeedLanguage(const std::string &arg);
+ void setListener(Listener *) {}
+ int status() const;
+ std::string errorString() const;
+ bool setPassword(const std::string &password);
+ std::string address() const;
+ bool store(const std::string &path);
+
+private:
+ void clearStatus();
+
+private:
+ //std::unique_ptr<tools::wallet2> m_wallet;
+ tools::wallet2 * m_wallet;
+ int m_status;
+ std::string m_errorString;
+ std::string m_password;
+};
+
+WalletImpl::WalletImpl()
+ :m_wallet(nullptr), m_status(Wallet::Status_Ok)
+{
+ m_wallet = new tools::wallet2();
+}
+
+WalletImpl::~WalletImpl()
+{
+ delete m_wallet;
+}
+
+bool WalletImpl::create(const std::string &path, const std::string &password, const std::string &language)
+{
+
+ clearStatus();
+
+ bool keys_file_exists;
+ bool wallet_file_exists;
+ tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists);
+ // TODO: figure out how to setup logger;
+ LOG_PRINT_L3("wallet_path: " << path << "");
+ LOG_PRINT_L3("keys_file_exists: " << std::boolalpha << keys_file_exists << std::noboolalpha
+ << " wallet_file_exists: " << std::boolalpha << wallet_file_exists << std::noboolalpha);
+
+
+ // add logic to error out if new wallet requested but named wallet file exists
+ if (keys_file_exists || wallet_file_exists) {
+ m_errorString = "attempting to generate or restore wallet, but specified file(s) exist. Exiting to not risk overwriting.";
+ LOG_ERROR(m_errorString);
+ m_status = Status_Error;
+ return false;
+ }
+ // TODO: validate language
+ m_wallet->set_seed_language(language);
+ crypto::secret_key recovery_val, secret_key;
+ try {
+ recovery_val = m_wallet->generate(path, password, secret_key, false, false);
+ m_password = password;
+ m_status = Status_Ok;
+ } catch (const std::exception &e) {
+ LOG_ERROR("Error creating wallet: " << e.what());
+ m_status = Status_Error;
+ m_errorString = e.what();
+ return false;
+ }
+
+ return true;
+}
+
+bool WalletImpl::open(const std::string &path, const std::string &password)
+{
+ clearStatus();
+ try {
+ // TODO: handle "deprecated"
+ m_wallet->load(path, password);
+
+ m_password = password;
+ } catch (const std::exception &e) {
+ LOG_ERROR("Error opening wallet: " << e.what());
+ m_status = Status_Error;
+ m_errorString = e.what();
+ }
+ return m_status == Status_Ok;
+}
+
+bool WalletImpl::recover(const std::string &path, const std::string &seed)
+{
+ clearStatus();
+ m_errorString.clear();
+ if (seed.empty()) {
+ m_errorString = "Electrum seed is empty";
+ LOG_ERROR(m_errorString);
+ m_status = Status_Error;
+ return false;
+ }
+
+ crypto::secret_key recovery_key;
+ std::string old_language;
+ if (!crypto::ElectrumWords::words_to_bytes(seed, recovery_key, old_language)) {
+ m_errorString = "Electrum-style word list failed verification";
+ m_status = Status_Error;
+ return false;
+ }
+
+ try {
+ m_wallet->set_seed_language(old_language);
+ m_wallet->generate(path, "", recovery_key, true, false);
+ // TODO: wallet->init(daemon_address);
+ } catch (const std::exception &e) {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ }
+ return m_status == Status_Ok;
+}
+
+bool WalletImpl::close()
+{
+ clearStatus();
+ bool result = false;
+ try {
+ m_wallet->store();
+ m_wallet->stop();
+ result = true;
+ } catch (const std::exception &e) {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ LOG_ERROR("Error closing wallet: " << e.what());
+ }
+ return result;
+}
+
+std::string WalletImpl::seed() const
+{
+ std::string seed;
+ if (m_wallet)
+ m_wallet->get_seed(seed);
+ return seed;
+}
+
+std::string WalletImpl::getSeedLanguage() const
+{
+ return m_wallet->get_seed_language();
+}
+
+void WalletImpl::setSeedLanguage(const std::string &arg)
+{
+ m_wallet->set_seed_language(arg);
+}
+
+int WalletImpl::status() const
+{
+ return m_status;
+}
+
+std::string WalletImpl::errorString() const
+{
+ return m_errorString;
+}
+
+bool WalletImpl::setPassword(const std::string &password)
+{
+ clearStatus();
+ try {
+ m_wallet->rewrite(m_wallet->get_wallet_file(), password);
+ m_password = password;
+ } catch (const std::exception &e) {
+ m_status = Status_Error;
+ m_errorString = e.what();
+ }
+ return m_status == Status_Ok;
+}
+
+std::string WalletImpl::address() const
+{
+ return m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+}
+
+bool WalletImpl::store(const std::string &path)
+{
+ clearStatus();
+ try {
+ if (path.empty()) {
+ m_wallet->store();
+ } else {
+ m_wallet->store_to(path, m_password);
+ }
+ } catch (const std::exception &e) {
+ LOG_ERROR("Error storing wallet: " << e.what());
+ m_status = Status_Error;
+ m_errorString = e.what();
+ }
+
+ return m_status == Status_Ok;
+}
+
+void WalletImpl::clearStatus()
+{
+ m_status = Status_Ok;
+ m_errorString.clear();
+}
+
+
+
+///////////////////////// WalletManager implementation /////////////////////////
+class WalletManagerImpl : public WalletManager
+{
+public:
+ Wallet * createWallet(const std::string &path, const std::string &password,
+ const std::string &language);
+ Wallet * openWallet(const std::string &path, const std::string &password);
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo);
+ virtual bool closeWallet(Wallet *wallet);
+ bool walletExists(const std::string &path);
+ std::string errorString() const;
+
+
+private:
+ WalletManagerImpl() {}
+ friend struct WalletManagerFactory;
+
+ std::string m_errorString;
+};
+
+Wallet *WalletManagerImpl::createWallet(const std::string &path, const std::string &password,
+ const std::string &language)
+{
+ WalletImpl * wallet = new WalletImpl();
+ wallet->create(path, password, language);
+ return wallet;
+}
+
+Wallet *WalletManagerImpl::openWallet(const std::string &path, const std::string &password)
+{
+ WalletImpl * wallet = new WalletImpl();
+ wallet->open(path, password);
+ return wallet;
+}
+
+Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::string &memo)
+{
+ WalletImpl * wallet = new WalletImpl();
+ wallet->recover(path, memo);
+ return wallet;
+}
+
+bool WalletManagerImpl::closeWallet(Wallet *wallet)
+{
+ WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet);
+ bool result = wallet_->close();
+ if (!result) {
+ m_errorString = wallet_->errorString();
+ } else {
+ delete wallet_;
+ }
+ return result;
+}
+
+bool WalletManagerImpl::walletExists(const std::string &path)
+{
+ return false;
+}
+
+std::string WalletManagerImpl::errorString() const
+{
+ return m_errorString;
+}
+
+
+
+///////////////////// WalletManagerFactory implementation //////////////////////
+WalletManager *WalletManagerFactory::getWalletManager()
+{
+
+ if (!g_walletManager) {
+ epee::log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_0);
+ g_walletManager = new WalletManagerImpl();
+ }
+
+ return g_walletManager;
+}
+
+}
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
new file mode 100644
index 000000000..c7e7c536c
--- /dev/null
+++ b/src/wallet/wallet2_api.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2014-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.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+
+#include <string>
+
+// Public interface for libwallet library
+namespace Bitmonero {
+
+
+/**
+ * @brief Interface for wallet operations.
+ * TODO: check if /include/IWallet.h is still actual
+ */
+struct Wallet
+{
+ // TODO define wallet interface (decide what needed from wallet2)
+
+ enum Status {
+ Status_Ok,
+ Status_Error
+ };
+
+ struct Listener
+ {
+ // TODO
+ };
+
+ virtual ~Wallet() = 0;
+ virtual std::string seed() const = 0;
+ virtual std::string getSeedLanguage() const = 0;
+ virtual void setSeedLanguage(const std::string &arg) = 0;
+ virtual void setListener(Listener * listener) = 0;
+ //! returns wallet status (Status_Ok | Status_Error)
+ virtual int status() const = 0;
+ //! in case error status, returns error string
+ virtual std::string errorString() const = 0;
+ virtual bool setPassword(const std::string &password) = 0;
+ virtual std::string address() const = 0;
+ virtual bool store(const std::string &path) = 0;
+};
+
+/**
+ * @brief WalletManager - provides functions to manage wallets
+ */
+struct WalletManager
+{
+
+ /*!
+ * \brief Creates new wallet
+ * \param path Name of wallet file
+ * \param password Password of wallet file
+ * \param language Language to be used to generate electrum seed memo
+ * \return Wallet instance (Wallet::status() needs to be called to check if created successfully)
+ */
+ virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language) = 0;
+
+ /*!
+ * \brief Opens existing wallet
+ * \param path Name of wallet file
+ * \param password Password of wallet file
+ * \return Wallet instance (Wallet::status() needs to be called to check if opened successfully)
+ */
+ virtual Wallet * openWallet(const std::string &path, const std::string &password) = 0;
+
+ /*!
+ * \brief recovers existing wallet using memo (electrum seed)
+ * \param path Name of wallet file to be created
+ * \param memo memo (25 words electrum seed)
+ * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
+ */
+ virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo) = 0;
+
+ /*!
+ * \brief Closes wallet. In case operation succeded, wallet object deleted. in case operation failed, wallet object not deleted
+ * \param wallet previously opened / created wallet instance
+ * \return None
+ */
+ virtual bool closeWallet(Wallet *wallet) = 0;
+
+ //! checks if wallet with the given name already exists
+ virtual bool walletExists(const std::string &path) = 0;
+
+ virtual std::string errorString() const = 0;
+
+};
+
+
+struct WalletManagerFactory
+{
+ static WalletManager * getWalletManager();
+};
+
+}
+
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 10d27651f..3de97f49d 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -31,6 +31,7 @@
#pragma once
#include <stdexcept>
+#include <system_error>
#include <string>
#include <vector>
@@ -75,6 +76,7 @@ namespace tools
// daemon_busy
// no_connection_to_daemon
// is_key_image_spent_error
+ // get_histogram_error
// wallet_files_doesnt_correspond
//
// * - class with protected ctor
@@ -209,7 +211,6 @@ namespace tools
//----------------------------------------------------------------------------------------------------
typedef file_error_base<file_exists_message_index> file_exists;
typedef file_error_base<file_not_found_message_index> file_not_found;
- typedef file_error_base<file_not_found_message_index> file_not_found;
typedef file_error_base<file_read_error_message_index> file_read_error;
typedef file_error_base<file_save_error_message_index> file_save_error;
//----------------------------------------------------------------------------------------------------
@@ -457,15 +458,17 @@ namespace tools
//----------------------------------------------------------------------------------------------------
struct tx_rejected : public transfer_error
{
- explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, const std::string& status)
+ explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, const std::string& status, const std::string& reason)
: transfer_error(std::move(loc), "transaction was rejected by daemon")
, m_tx(tx)
, m_status(status)
+ , m_reason(reason)
{
}
const cryptonote::transaction& tx() const { return m_tx; }
const std::string& status() const { return m_status; }
+ const std::string& reason() const { return m_reason; }
std::string to_string() const
{
@@ -473,12 +476,17 @@ namespace tools
ss << transfer_error::to_string() << ", status = " << m_status << ", tx:\n";
cryptonote::transaction tx = m_tx;
ss << cryptonote::obj_to_json_str(tx);
+ if (!m_reason.empty())
+ {
+ ss << " (" << m_reason << ")";
+ }
return ss.str();
}
private:
cryptonote::transaction m_tx;
std::string m_status;
+ std::string m_reason;
};
//----------------------------------------------------------------------------------------------------
struct tx_sum_overflow : public transfer_error
@@ -600,6 +608,14 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
+ struct get_histogram_error : public wallet_rpc_error
+ {
+ explicit get_histogram_error(std::string&& loc, const std::string& request)
+ : wallet_rpc_error(std::move(loc), "failed to get output histogram", request)
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
struct wallet_files_doesnt_correspond : public wallet_logic_error
{
explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file)
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index ac13d8021..d7d99c2ae 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -227,7 +227,12 @@ namespace tools
try
{
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra);
+ uint64_t mixin = req.mixin;
+ if (mixin < 2 && m_wallet.use_fork_rules(2)) {
+ LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
+ mixin = 2;
+ }
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions(dsts, mixin, req.unlock_time, req.fee, extra);
// reject proposed transactions if there are more than one. see on_transfer_split below.
if (ptx_vector.size() != 1)
@@ -287,11 +292,16 @@ namespace tools
try
{
+ uint64_t mixin = req.mixin;
+ if (mixin < 2 && m_wallet.use_fork_rules(2)) {
+ LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
+ mixin = 2;
+ }
std::vector<wallet2::pending_tx> ptx_vector;
if (req.new_algorithm)
- ptx_vector = m_wallet.create_transactions_2(dsts, req.mixin, req.unlock_time, req.fee, extra);
+ ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.fee, extra);
else
- ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra);
+ ptx_vector = m_wallet.create_transactions(dsts, mixin, req.unlock_time, req.fee, extra);
m_wallet.commit_tx(ptx_vector);
@@ -337,7 +347,7 @@ namespace tools
try
{
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_dust_sweep_transactions();
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_unmixable_sweep_transactions(req.trusted_daemon);
m_wallet.commit_tx(ptx_vector);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 40d6fd8f8..2c4e26406 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -178,9 +178,11 @@ namespace wallet_rpc
struct request
{
bool get_tx_keys;
+ bool trusted_daemon;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(get_tx_keys)
+ KV_SERIALIZE(trusted_daemon)
END_KV_SERIALIZE_MAP()
};
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 990be9143..d1be97afc 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -63,6 +63,7 @@ add_subdirectory(unit_tests)
add_subdirectory(difficulty)
add_subdirectory(hash)
add_subdirectory(net_load_tests)
+add_subdirectory(libwallet_api_tests)
# add_subdirectory(daemon_tests)
diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h
index d0d912cbb..44170d116 100644
--- a/tests/core_tests/chaingen.h
+++ b/tests/core_tests/chaingen.h
@@ -472,7 +472,7 @@ inline bool replay_events_through_core(cryptonote::core& cr, const std::vector<t
//--------------------------------------------------------------------------
template<class t_test_class>
struct get_test_options {
- const std::pair<uint8_t, uint64_t> hard_forks[1] = {std::make_pair(1, 0)};
+ const std::pair<uint8_t, uint64_t> hard_forks[1] = {std::make_pair((uint8_t)1, (uint64_t)0)};
const cryptonote::test_options test_options = {
hard_forks
};
diff --git a/tests/crypto/CMakeLists.txt b/tests/crypto/CMakeLists.txt
index f42428a65..86946aa5c 100644
--- a/tests/crypto/CMakeLists.txt
+++ b/tests/crypto/CMakeLists.txt
@@ -40,7 +40,8 @@ set(crypto_headers
add_executable(crypto-tests
${crypto_sources}
${crypto_headers})
-target_link_libraries(crypto-tests)
+target_link_libraries(crypto-tests
+ ${Boost_SYSTEM_LIBRARY})
set_property(TARGET crypto-tests
PROPERTY
FOLDER "tests")
diff --git a/src/connectivity_tool/CMakeLists.txt b/tests/libwallet_api_tests/CMakeLists.txt
index 223d6e67f..e60947084 100644
--- a/src/connectivity_tool/CMakeLists.txt
+++ b/tests/libwallet_api_tests/CMakeLists.txt
@@ -26,20 +26,34 @@
# 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(connectivity_tool_sources
- conn_tool.cpp)
+set(libwallet_api_tests_sources
+ main.cpp
+)
-set(connectivity_tool_private_headers)
+set(libwallet_api_tests_headers
+ )
-bitmonero_add_executable(connectivity_tool
- ${connectivity_tool_sources}
- ${connectivity_tool_private_headers})
-target_link_libraries(connectivity_tool
+add_executable(libwallet_api_tests
+ ${libwallet_api_tests_sources}
+ ${libwallet_api_tests_headers})
+
+target_link_libraries(libwallet_api_tests
LINK_PRIVATE
- cryptonote_core
- crypto
- common
- ${CMAKE_THREAD_LIBS_INIT}
- ${Boost_PROGRAM_OPTIONS_LIBRARY}
- ${Boost_REGEX_LIBRARY}
- ${Boost_SYSTEM_LIBRARY})
+ wallet
+ ${GTEST_MAIN_LIBRARIES}
+ ${EXTRA_LIBRARIES})
+
+set_property(TARGET libwallet_api_tests
+ PROPERTY
+ FOLDER "tests")
+
+if (NOT MSVC)
+ set_property(TARGET libwallet_api_tests
+ APPEND_STRING
+ PROPERTY
+ COMPILE_FLAGS " -Wno-undef -Wno-sign-compare")
+endif ()
+
+add_test(
+ NAME libwallet_api_tests
+ COMMAND libwallet_api_tests)
diff --git a/tests/libwallet_api_tests/main.cpp b/tests/libwallet_api_tests/main.cpp
new file mode 100644
index 000000000..9701c300c
--- /dev/null
+++ b/tests/libwallet_api_tests/main.cpp
@@ -0,0 +1,212 @@
+// Copyright (c) 2014-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.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "gtest/gtest.h"
+#include "wallet/wallet2_api.h"
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <iostream>
+#include <vector>
+
+
+using namespace std;
+//unsigned int epee::g_test_dbg_lock_sleep = 0;
+
+
+
+struct WalletManagerTest : public testing::Test
+{
+ Bitmonero::WalletManager * wmgr;
+
+ const char * WALLET_NAME = "testwallet";
+ const char * WALLET_NAME_COPY = "testwallet_copy";
+ const char * WALLET_NAME_WITH_DIR = "walletdir/testwallet_test";
+ const char * WALLET_NAME_WITH_DIR_NON_WRITABLE = "/var/walletdir/testwallet_test";
+ const char * WALLET_PASS = "password";
+ const char * WALLET_PASS2 = "password22";
+ const char * WALLET_LANG = "English";
+
+ WalletManagerTest()
+ {
+ std::cout << __FUNCTION__ << std::endl;
+ wmgr = Bitmonero::WalletManagerFactory::getWalletManager();
+ deleteWallet(WALLET_NAME);
+ deleteDir(boost::filesystem::path(WALLET_NAME_WITH_DIR).parent_path().string());
+ }
+
+
+ ~WalletManagerTest()
+ {
+ std::cout << __FUNCTION__ << std::endl;
+ //deleteWallet(WALLET_NAME);
+ }
+
+
+ void deleteWallet(const std::string & walletname)
+ {
+ std::cout << "** deleting wallet: " << walletname << std::endl;
+ boost::filesystem::remove(walletname);
+ boost::filesystem::remove(walletname + ".address.txt");
+ boost::filesystem::remove(walletname + ".keys");
+ }
+
+ void deleteDir(const std::string &path)
+ {
+ std::cout << "** removing dir recursively: " << path << std::endl;
+ boost::filesystem::remove_all(path);
+ }
+
+};
+
+
+TEST_F(WalletManagerTest, WalletManagerCreatesWallet)
+{
+
+ Bitmonero::Wallet * wallet = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
+ ASSERT_TRUE(wallet->status() == Bitmonero::Wallet::Status_Ok);
+ ASSERT_TRUE(!wallet->seed().empty());
+ std::vector<std::string> words;
+ std::string seed = wallet->seed();
+ boost::split(words, seed, boost::is_any_of(" "), boost::token_compress_on);
+ ASSERT_TRUE(words.size() == 25);
+ std::cout << "** seed: " << wallet->seed() << std::endl;
+ ASSERT_FALSE(wallet->address().empty());
+ std::cout << "** address: " << wallet->address() << std::endl;
+ ASSERT_TRUE(wmgr->closeWallet(wallet));
+
+}
+
+TEST_F(WalletManagerTest, WalletManagerOpensWallet)
+{
+
+ Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
+ std::string seed1 = wallet1->seed();
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+ Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
+ ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);
+ ASSERT_TRUE(wallet2->seed() == seed1);
+ std::cout << "** seed: " << wallet2->seed() << std::endl;
+}
+
+
+TEST_F(WalletManagerTest, WalletManagerChangesPassword)
+{
+ Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
+ std::string seed1 = wallet1->seed();
+ ASSERT_TRUE(wallet1->setPassword(WALLET_PASS2));
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+ Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME, WALLET_PASS2);
+ ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);
+ ASSERT_TRUE(wallet2->seed() == seed1);
+ ASSERT_TRUE(wmgr->closeWallet(wallet2));
+ Bitmonero::Wallet * wallet3 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
+ ASSERT_FALSE(wallet3->status() == Bitmonero::Wallet::Status_Ok);
+}
+
+
+
+TEST_F(WalletManagerTest, WalletManagerRecoversWallet)
+{
+ Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
+ std::string seed1 = wallet1->seed();
+ std::string address1 = wallet1->address();
+ ASSERT_FALSE(address1.empty());
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+ deleteWallet(WALLET_NAME);
+ Bitmonero::Wallet * wallet2 = wmgr->recoveryWallet(WALLET_NAME, seed1);
+ ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);
+ ASSERT_TRUE(wallet2->seed() == seed1);
+ ASSERT_TRUE(wallet2->address() == address1);
+ ASSERT_TRUE(wmgr->closeWallet(wallet2));
+}
+
+
+TEST_F(WalletManagerTest, WalletManagerStoresWallet1)
+{
+ Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
+ std::string seed1 = wallet1->seed();
+ std::string address1 = wallet1->address();
+
+ ASSERT_TRUE(wallet1->store(""));
+ ASSERT_TRUE(wallet1->store(WALLET_NAME_COPY));
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+ Bitmonero::Wallet * wallet2 = wmgr->openWallet(WALLET_NAME_COPY, WALLET_PASS);
+ ASSERT_TRUE(wallet2->status() == Bitmonero::Wallet::Status_Ok);
+ ASSERT_TRUE(wallet2->seed() == seed1);
+ ASSERT_TRUE(wallet2->address() == address1);
+ ASSERT_TRUE(wmgr->closeWallet(wallet2));
+}
+
+TEST_F(WalletManagerTest, WalletManagerStoresWallet2)
+{
+ Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
+ std::string seed1 = wallet1->seed();
+ std::string address1 = wallet1->address();
+
+ ASSERT_TRUE(wallet1->store(WALLET_NAME_WITH_DIR));
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+
+ wallet1 = wmgr->openWallet(WALLET_NAME_WITH_DIR, WALLET_PASS);
+ ASSERT_TRUE(wallet1->status() == Bitmonero::Wallet::Status_Ok);
+ ASSERT_TRUE(wallet1->seed() == seed1);
+ ASSERT_TRUE(wallet1->address() == address1);
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+}
+
+TEST_F(WalletManagerTest, WalletManagerStoresWallet3)
+{
+ Bitmonero::Wallet * wallet1 = wmgr->createWallet(WALLET_NAME, WALLET_PASS, WALLET_LANG);
+ std::string seed1 = wallet1->seed();
+ std::string address1 = wallet1->address();
+
+ ASSERT_FALSE(wallet1->store(WALLET_NAME_WITH_DIR_NON_WRITABLE));
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+
+ wallet1 = wmgr->openWallet(WALLET_NAME_WITH_DIR_NON_WRITABLE, WALLET_PASS);
+ ASSERT_FALSE(wallet1->status() == Bitmonero::Wallet::Status_Ok);
+
+ ASSERT_FALSE(wmgr->closeWallet(wallet1));
+
+ wallet1 = wmgr->openWallet(WALLET_NAME, WALLET_PASS);
+ ASSERT_TRUE(wallet1->status() == Bitmonero::Wallet::Status_Ok);
+ ASSERT_TRUE(wallet1->seed() == seed1);
+ ASSERT_TRUE(wallet1->address() == address1);
+ ASSERT_TRUE(wmgr->closeWallet(wallet1));
+
+}
+
+
+int main(int argc, char** argv)
+{
+ //epee::debug::get_set_enable_assert(true, false);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index db20bbc2d..2e5100229 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -49,7 +49,8 @@ set(unit_tests_sources
test_format_utils.cpp
test_peerlist.cpp
test_protocol_pack.cpp
- hardfork.cpp)
+ hardfork.cpp
+ unbound.cpp)
set(unit_tests_headers
unit_tests_utils.h)
@@ -69,6 +70,7 @@ target_link_libraries(unit_tests
${Boost_REGEX_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
+ ${UNBOUND_LIBRARIES}
${EXTRA_LIBRARIES})
set_property(TARGET unit_tests
PROPERTY
diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp
index 1faef2082..6d4a100df 100644
--- a/tests/unit_tests/hardfork.cpp
+++ b/tests/unit_tests/hardfork.cpp
@@ -109,6 +109,7 @@ public:
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const { return true; }
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const { return true; }
virtual bool is_read_only() const { return false; }
+ virtual std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts) const { return std::map<uint64_t, uint64_t>(); }
virtual void add_block( const block& blk
, const size_t& block_size
diff --git a/src/cryptonote_core/checkpoints_create.h b/tests/unit_tests/unbound.cpp
index 83830f8a2..25026ec58 100644
--- a/src/cryptonote_core/checkpoints_create.h
+++ b/tests/unit_tests/unbound.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2016, The Monero Project
//
// All rights reserved.
//
@@ -25,24 +25,25 @@
// 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.
-//
+//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#pragma once
-
-#include "checkpoints.h"
-#include "misc_log_ex.h"
+#include "gtest/gtest.h"
-#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(checkpoints.add_checkpoint(h, hash), false);
-#define JSON_HASH_FILE_NAME "checkpoints.json"
+extern "C" int dnskey_algo_id_is_supported(int);
-namespace cryptonote
+TEST(unbound, supported_algorithms)
{
+ // Monero causes these to be tried, but we don't have access
+ // to this internal unbound header here, so we use raw numbers
+ // LDNS_RSASHA1 = 5,
+ // LDNS_RSASHA1_NSEC3 = 7,
+ // LDNS_RSASHA256 = 8, /* RFC 5702 */
+ // LDNS_ECDSAP256SHA256 = 13, /* RFC 6605 */
- bool create_checkpoints(cryptonote::checkpoints& checkpoints);
-
- bool load_checkpoints_from_json(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath);
- bool load_checkpoints_from_dns(cryptonote::checkpoints& checkpoints, bool testnet = false);
- bool load_new_checkpoints(cryptonote::checkpoints& checkpoints, std::string json_hashfile_fullpath);
+ ASSERT_TRUE(dnskey_algo_id_is_supported(5));
+ ASSERT_TRUE(dnskey_algo_id_is_supported(7));
+ ASSERT_TRUE(dnskey_algo_id_is_supported(8));
+ ASSERT_TRUE(dnskey_algo_id_is_supported(13));
+}
-} // namespace cryptonote
diff --git a/utils/gpg_keys/aabramov.asc b/utils/gpg_keys/aabramov.asc
deleted file mode 100644
index 1626f95b2..000000000
--- a/utils/gpg_keys/aabramov.asc
+++ /dev/null
@@ -1,31 +0,0 @@
------BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v1
-
-mQENBFQwICMBCACyHKYHaSEdNyE8kqYm+V37IsX5xxqMTkOCuVmaWt8i8FP2aPDz
-TgsITQ8tHoJubL0wh/y6Z/KQY8cSWT/JU39gP64I1XdGVgaPErF3kZ7n2jh+K5HY
-vBWrFvw4Xj2XvHaJZW6rVVX5dc4rxqnnOjfKTPI6/tQhT16dwbMuCyZOyMfStAF9
-q+14I9M2MAbMlgNO0el0aR3GMVjoDeaWBk/qWxTaHjyOBAVyryoFpwIl7Gmwkk1d
-+jv4542LjJOiiwBEDcWUmNvlsRk5sXMcXImJSpaCN1Ta9Eyu2NBIK/qAbuC6YgLe
-nn/0DaA7TOpeIMU/WNq/Dpbathst+fMJNK6BABEBAAG0JkFsZXhhbmRlciBBYnJh
-bW92IDxhYWJyYW1vdkBnbWFpbC5jb20+iQE+BBMBAgAoBQJUMCAjAhsDBQkDwmcA
-BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRA/57nKbsMV+F/+B/9f7C1iiEFD
-18Af48WLJXZaJJTqxMeWzztWhZ2LS27rFOGJcj50UeON7aVu8LWU8gvyPJct6wJA
-T1hvPWc6TTsbYp8KHP/nUfUWWV+np0qVSj91eGHp7NjAxXYe4NnWt1JUtINxvNll
-BKY2l0MHHHKwuWxky6zug48CToB8Y0RBDUgFM0pmnCzmySs6OAJbLmnnvesTDFZk
-m6wycq4/SrbVByTZHVqBmZNnypMQxbqXTti9ltpXWqO89RpQQ+QBQQlrNy9QRHJq
-VW9Qn5eL4O4IdmXpHqln1ppSu2il+pqQCGc5O9xFxqG6YRHljQEngBfgKLxOsqa1
-OGJHu1pLyUYRuQENBFQwICMBCACmQ+1DRgm9AMNo4iDur47obDzUTQ/1g2ah1+AT
-IYNhhltBD3jURsRZ6lMeancxU2/6hyUzCon5ETKfnF0+ujqldSDuo+XY9CDgmXLC
-s3wPAKZvyjScwMFTyW1hGfyT9SFwYVDaXOeW+VrJ97Xh5FWHx8LU7eieYIl8tOUY
-YWEeIps/y5zBkk7bB4LVNVc3AlKPS4vb/py5szA5fMg8ix/ggvpq8be4BnImP3a1
-yrl+W9HUtcTs8jBnFLMoZYaUklZ9LR7yfnjhhtKqW833UB07qYQY//dJGy4kVnit
-M5LX9VKa+wnWsgP+w5jElPX0bweV6+V/yFRhjLLwV7dv/MJnABEBAAGJASUEGAEC
-AA8FAlQwICMCGwwFCQPCZwAACgkQP+e5ym7DFfgrvAf/bnQD5iR/HiNsH0HQE96G
-Wsrr9VW/ucnm06pd1D/r7ogYcVWuNIglx3QdvFla4uZDvYR0PhyjNrOyPzUgLWp/
-fjQw9buyI3BawAScbPuN06zHLUIvDQpaPq2+uBRq7sCcdsox6sMiUH6x5QcD3lbR
-nh6aAGwSkTabW5a/GVZTIHSicGbhXUaIIh3w+sQZ/y5xTVjgVi4I4XdrxOZoo5RN
-nF5oyn74n5p9euxJMWgvC/tz/bD7VKYWJHRnuyeumLTPudLjPFFQpTX+I+gMJ5FT
-Vs7byaOlrZbQxzXd4G0uBnCvJ1FH3cL6Ddqlsz/l2WzddMknMbVMn7+fSVMdpbIm
-hw==
-=7XDh
------END PGP PUBLIC KEY BLOCK-----
diff --git a/utils/gpg_keys/hyc.asc b/utils/gpg_keys/hyc.asc
new file mode 100644
index 000000000..4d174e989
--- /dev/null
+++ b/utils/gpg_keys/hyc.asc
@@ -0,0 +1,44 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1
+
+mQGiBERuNuERBACY9KGBcxSvy6D9Sxk51L59/7ACWQf/Mk2B5jWCxcadEZHokKf4
+mYBBH963B1v04S8QKsm1avt4amS3dEEfxJ920RWtg5XkVCW0HfErsZ58XobIcPLD
+pd7u3btGHObQfJ5fzyRuIaWpBg59gGJAw13Rz/ILuDpXHBP0ppI9OOq6nwCgnG+I
+B56YTz9oGLiVXFsa8LItTI0D/jBTd5UWQ+BYCU8HdHq+fRm6l/N921yrFO4hF1jU
+4Mi6t9srHkQVW5ro9cxVhAWKCbVum5ogylcfH1WK//R2HkSdKJXNVxZdBieGD/tL
+YYSJj3qU7UA9ZTH5QGUkNqcHH7bV+o+5oZdhIhpcMuG1TFudGkQcnTWW0X69S6bR
+uBa1A/9Zg6Hk2uL0ytbHtASWKZHr80f1hhe6catT314nqJNU5rufYHHHCvQJVDPs
+NWkj4OfkBE5xG1VxMPulvMTJIKm1FdXK+1B9OfcXUk/+zyCiYLGbrAjuUlHDmVDH
+P1YQEn+a8S8QowXl+tAL62lO3mDCiPny6ohuIJiTfLLPrE1+bLQaSG93YXJkIENo
+dSA8aHljQHN5bWFzLmNvbT6IRgQQEQIABgUCRukakgAKCRCZSHtC+KtbW2rTAJ9/
+j5IiB0Fvq7gAs2Zf/eqc4wqLzwCeLicSAHhaXSRrKd7ZEFTnAFW/qWSIRgQQEQIA
+BgUCSDbdawAKCRBsEnep072iJdceAJ44GfpthT4ZPN8XEob5pn8qsFPKIgCfbcNI
+wsWoHybiovYiHBZTpW50/QqIRgQQEQIABgUCSDbe8wAKCRBDbTC1hOUd3UFUAKCn
+CAhM4Hpe2QNGZM8lYewn2bVRogCeKs5eOlG4vqzYsH1ro1evBsKFXLyIZAQTEQIA
+JAUCRG424QIbAwUJEswDAAYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRD9KnC0SrEb
+p72rAJ44Keweij6d4EFjROG6PdF8J64vYACfZ99qH7+wzvBxaMuAIAw558lBaY2I
+XgQTEQIAHgIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAUCVpAQGAAKCRD9KnC0SrEb
+p+3XAJwNjg2sFjNTwzf5LaTT/wl2sWwqbgCgiX9jv1OS+pzJHH4OpjTt5+lHuUO5
+AQ0ERG424RAEAIrOpREkyhB6FTmYo+Tw3r1/pfO0gHC1FcuTrL0lsRVOYwiwVwmk
+gVME4gvHRNqnT1gG/RMQm6kKGz07RvzVaRrrT7B/nOG9kbVMzRQIqBbk1o+u7TFr
+EYSvXF9xfl3Sq2/UYqIURCW9ZIOF6JLGcF0qGrhheg9eDq97tAfZDwvXAAMFA/4u
+38QVNe0Izqc9FTGkO6VijsfXvsn3tCKqm7v/vkJa8Rpu3dumChwMtiGwkzLCJMpC
+TbnL0dW4hChHjT/DOWKV84WZwP6vmuMlhNFFrsRh9wqi6LjBNdhVHHfjx2JU0R4L
+kID4FSiU20TH4Z/Vm+JSC1E272kw3xfacga4e3DpN4hPBBgRAgAPBQJEbjbhAhsM
+BQkSzAMAAAoJEP0qcLRKsRun3wAAn1BnosO00oGZ0sVX5N+m8haWI6YeAJ9HV/RG
+L4t04ykgAnU8LLAOaNCWV7kCDQRWkBA0EAgAmQ81PiXqJmk475469zKLWyxjrvG/
+utPTzE4jgah7Ib3I591J6cGmpoOWeM9bdaLc2DGxWqpjcZd/6dFo4CyIFLMPXsgv
+JH0iwh5gUt/YBUHkz3kVpN+UqoE+5d+6LVoJQrio/lHAUpFBtpnrx2BeolWRmG6m
+uTQwOkyNXUbFgSIwpKuN1rqe2U4KuvFEBTX1QCCzGIOEYtbmtBdGtg/FLwt/+pIF
+QHY4tQe/bykX+XLYyLCChoq5Kkq+KZv0Ebafg3vm+xUMwbeqQwUNFLPjIFpHeeKi
+nQk8BPNvFjf+MmZ/y/tbsA/156WfMldHsXOV/nD6JWk2HPv8/BL7Fz4bvwADBQf+
+M+6GDIWUpZFfix7eDBZhtyNbehQ6sqSGu97LljKZXb0SxOM5jTkAguuVmjOM8bi4
+cVs9LJt1nFD+PmNRLuju4tda4xk61fvueqv1T0c59fFak5STejzrpWhDeEJZmJMw
+EwSv4e0OTFsKlmWJSvfoOavPhbw+Tuhgza1WFLjedqo+RQnCQmXMNlcblD6otgSV
+X1uadZBWODmHX/nBmgyDzSILZsy3mJkspAIx/bH95oTMaGQAkVH++WLLGUmjr6tb
+uru3e8+mCUvpEptaaBcIq8minoVvfkIZKUgISKf4Yize1PoEuCQbnETcfekZyY94
+m/RwmduzBwMXzMikPOyRwYhPBBgRAgAPBQJWkBA0AhsMBQkSzAMAAAoJEP0qcLRK
+sRunqSUAnRenNWORvzTRRy0qmF5xVFlDIUGpAJ4pUZgkE7YRyjSzdhxcaRBOjAQa
+GQ==
+=sUzZ
+-----END PGP PUBLIC KEY BLOCK----- \ No newline at end of file
diff --git a/utils/munin_plugins/alt_blocks_count b/utils/munin_plugins/alt_blocks_count
index d68289c34..cf6b3651b 100644
--- a/utils/munin_plugins/alt_blocks_count
+++ b/utils/munin_plugins/alt_blocks_count
@@ -42,4 +42,4 @@ EOM
esac
printf "alt_blocks_count.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep alt_blocks_count | cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep alt_blocks_count | cut -d ' ' -f2
diff --git a/utils/munin_plugins/difficulty b/utils/munin_plugins/difficulty
index e22b76829..edfd2bf16 100644
--- a/utils/munin_plugins/difficulty
+++ b/utils/munin_plugins/difficulty
@@ -42,4 +42,4 @@ EOM
esac
printf "difficulty.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep difficulty | cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep difficulty | cut -d ' ' -f2
diff --git a/utils/munin_plugins/grey_peerlist_size b/utils/munin_plugins/grey_peerlist_size
index e18c4cd06..d7b1bd6a7 100644
--- a/utils/munin_plugins/grey_peerlist_size
+++ b/utils/munin_plugins/grey_peerlist_size
@@ -42,4 +42,4 @@ EOM
esac
printf "grey_peerlist_size.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep grey_peerlist_size | cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep grey_peerlist_size | cut -d ' ' -f2
diff --git a/utils/munin_plugins/height b/utils/munin_plugins/height
index ca68b31b9..3f79fa302 100644
--- a/utils/munin_plugins/height
+++ b/utils/munin_plugins/height
@@ -43,4 +43,4 @@ EOM
esac
printf "height.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep height | cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep height | cut -d ' ' -f2
diff --git a/utils/munin_plugins/incoming_connections_count b/utils/munin_plugins/incoming_connections_count
index 2521d4c99..7232e5736 100644
--- a/utils/munin_plugins/incoming_connections_count
+++ b/utils/munin_plugins/incoming_connections_count
@@ -42,4 +42,4 @@ EOM
esac
printf "incoming_connections_count.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep incoming_connections_count | cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep incoming_connections_count | cut -d ' ' -f2
diff --git a/utils/munin_plugins/outgoing_connections_count b/utils/munin_plugins/outgoing_connections_count
index 92edb3d67..773ce4624 100644
--- a/utils/munin_plugins/outgoing_connections_count
+++ b/utils/munin_plugins/outgoing_connections_count
@@ -42,4 +42,4 @@ EOM
esac
printf "outgoing_connections_count.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep outgoing_connections_count | cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep outgoing_connections_count | cut -d ' ' -f2
diff --git a/utils/munin_plugins/tx_count b/utils/munin_plugins/tx_count
index 95bb8b540..f2504cd5f 100644
--- a/utils/munin_plugins/tx_count
+++ b/utils/munin_plugins/tx_count
@@ -43,4 +43,4 @@ EOM
esac
printf "tx_count.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep tx_count| cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep tx_count| cut -d ' ' -f2
diff --git a/utils/munin_plugins/tx_pool_size b/utils/munin_plugins/tx_pool_size
index 58717cf1f..3bf952071 100644
--- a/utils/munin_plugins/tx_pool_size
+++ b/utils/munin_plugins/tx_pool_size
@@ -42,4 +42,4 @@ EOM
esac
printf "tx_pool_size.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep tx_pool_size| cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep tx_pool_size| cut -d ' ' -f2
diff --git a/utils/munin_plugins/white_peerlist_size b/utils/munin_plugins/white_peerlist_size
index 2b92622e5..9e5771cc3 100644
--- a/utils/munin_plugins/white_peerlist_size
+++ b/utils/munin_plugins/white_peerlist_size
@@ -42,4 +42,4 @@ EOM
esac
printf "white_peerlist_size.value "
-/home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep white_peerlist_size | cut -d ' ' -f2
+# rewrite using curl or similar: /home/user/bitmonero/build/release/src/connectivity_tool --ip=127.0.0.1 --rpc_port=8081 --timeout=1000 --rpc_get_daemon_info | grep white_peerlist_size | cut -d ' ' -f2