aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--cmake/FindReadline.cmake47
-rw-r--r--contrib/epee/include/memwipe.h (renamed from src/common/memwipe.h)0
-rw-r--r--contrib/epee/include/net/http_auth.h7
-rw-r--r--contrib/epee/include/net/http_protocol_handler.h3
-rw-r--r--contrib/epee/include/net/http_server_impl_base.h3
-rw-r--r--contrib/epee/include/net/network_throttle.hpp3
-rw-r--r--contrib/epee/include/serialization/keyvalue_serialization.h1
-rw-r--r--contrib/epee/include/storages/portable_storage_val_converters.h6
-rw-r--r--contrib/epee/src/CMakeLists.txt7
-rw-r--r--contrib/epee/src/connection_basic.cpp1
-rw-r--r--contrib/epee/src/http_auth.cpp7
-rw-r--r--contrib/epee/src/memwipe.c (renamed from src/common/memwipe.c)0
-rw-r--r--src/blockchain_db/blockchain_db.h5
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp70
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h2
-rw-r--r--src/common/CMakeLists.txt6
-rw-r--r--src/common/i18n.cpp43
-rw-r--r--src/common/password.cpp2
-rw-r--r--src/common/threadpool.cpp8
-rw-r--r--src/common/util.cpp1
-rw-r--r--src/crypto/CMakeLists.txt1
-rw-r--r--src/crypto/blake256.c2
-rw-r--r--src/crypto/chacha.h2
-rw-r--r--src/crypto/crypto.h2
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h1
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.cpp2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp1
-rw-r--r--src/cryptonote_core/blockchain.cpp4
-rw-r--r--src/cryptonote_core/blockchain.h2
-rw-r--r--src/cryptonote_core/tx_pool.cpp39
-rw-r--r--src/mnemonics/CMakeLists.txt1
-rw-r--r--src/p2p/CMakeLists.txt1
-rw-r--r--src/ringct/rctTypes.h2
-rw-r--r--src/rpc/CMakeLists.txt2
-rw-r--r--src/rpc/core_rpc_server.cpp3
-rw-r--r--src/serialization/container.h113
-rw-r--r--src/serialization/deque.h64
-rw-r--r--src/serialization/list.h72
-rw-r--r--src/serialization/serialization.h16
-rw-r--r--src/serialization/set.h85
-rw-r--r--src/serialization/unordered_set.h58
-rw-r--r--src/serialization/vector.h82
-rw-r--r--src/simplewallet/simplewallet.cpp209
-rw-r--r--src/simplewallet/simplewallet.h3
-rw-r--r--src/wallet/wallet2.cpp221
-rw-r--r--src/wallet/wallet2.h10
-rw-r--r--src/wallet/wallet_errors.h11
-rw-r--r--src/wallet/wallet_rpc_server.cpp407
-rw-r--r--src/wallet/wallet_rpc_server.h5
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h12
-rw-r--r--tests/fuzz/cold-outputs.cpp10
-rw-r--r--tests/fuzz/cold-transaction.cpp10
-rw-r--r--tests/fuzz/signature.cpp10
-rw-r--r--tests/performance_tests/CMakeLists.txt2
-rw-r--r--tests/performance_tests/main.cpp3
-rw-r--r--tests/performance_tests/subaddress_expand.h65
-rw-r--r--tests/unit_tests/crypto.cpp24
-rw-r--r--tests/unit_tests/hardfork.cpp2
-rw-r--r--tests/unit_tests/http.cpp22
-rw-r--r--tests/unit_tests/memwipe.cpp2
-rw-r--r--tests/unit_tests/serialization.cpp1
-rw-r--r--tests/unit_tests/subaddress.cpp17
-rw-r--r--tests/unit_tests/varint.cpp1
-rw-r--r--translations/CMakeLists.txt54
-rw-r--r--translations/generate_translations_header.c86
-rw-r--r--utils/build_scripts/android32.Dockerfile2
-rw-r--r--utils/build_scripts/android64.Dockerfile2
68 files changed, 1191 insertions, 781 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ef2677cbf..5b7d1bf77 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -397,6 +397,10 @@ endif()
add_definitions(-DAUTO_INITIALIZE_EASYLOGGINGPP)
+# Generate header for embedded translations
+add_subdirectory(translations)
+include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations")
+
add_subdirectory(external)
# Final setup for miniupnpc
diff --git a/cmake/FindReadline.cmake b/cmake/FindReadline.cmake
index cdce0bfca..7a11a270a 100644
--- a/cmake/FindReadline.cmake
+++ b/cmake/FindReadline.cmake
@@ -36,14 +36,18 @@ find_library(Readline_LIBRARY
NO_DEFAULT_PATH
)
-if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+find_library(Termcap_LIBRARY
+ NAMES tinfo termcap ncursesw ncurses cursesw curses
+)
+
+if(Readline_INCLUDE_DIR AND Readline_LIBRARY)
set(READLINE_FOUND TRUE)
-else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+else(Readline_INCLUDE_DIR AND Readline_LIBRARY)
FIND_LIBRARY(Readline_LIBRARY NAMES readline PATHS Readline_ROOT_DIR)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY )
MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY)
-endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY)
+endif(Readline_INCLUDE_DIR AND Readline_LIBRARY)
mark_as_advanced(
Readline_ROOT_DIR
@@ -53,22 +57,25 @@ mark_as_advanced(
set(CMAKE_REQUIRED_INCLUDES ${Readline_INCLUDE_DIR})
set(CMAKE_REQUIRED_LIBRARIES ${Readline_LIBRARY})
-INCLUDE(CheckCXXSourceCompiles)
-CHECK_CXX_SOURCE_COMPILES(
-"
-#include <stdio.h>
-#include <readline/readline.h>
-int
-main()
-{
- char * s = rl_copy_text(0, 0);
-}
-" GNU_READLINE_FOUND)
-if(NOT Readline_LIBRARY)
- set(Readline_LIBRARY "")
-endif()
+include(CheckFunctionExists)
+check_function_exists(rl_copy_text HAVE_COPY_TEXT)
+check_function_exists(rl_filename_completion_function HAVE_COMPLETION_FUNCTION)
+
+if(NOT HAVE_COMPLETION_FUNCTION)
+ unset(READLINE_FOUND)
+ set(CMAKE_REQUIRED_LIBRARIES ${Readline_LIBRARY} ${Termcap_LIBRARY})
+ check_function_exists(rl_copy_text HAVE_COPY_TEXT_TC)
+ check_function_exists(rl_filename_completion_function HAVE_COMPLETION_FUNCTION_TC)
+ set(HAVE_COMPLETION_FUNCTION ${HAVE_COMPLETION_FUNCTION_TC})
+ set(HAVE_COPY_TEXT ${HAVE_COPY_TEXT_TC})
+ if(HAVE_COMPLETION_FUNCTION)
+ set(Readline_LIBRARY ${Readline_LIBRARY} ${Termcap_LIBRARY})
+ endif(HAVE_COMPLETION_FUNCTION)
+endif(NOT HAVE_COMPLETION_FUNCTION)
+
+if(HAVE_COMPLETION_FUNCTION AND HAVE_COPY_TEXT)
+ set(GNU_READLINE_FOUND TRUE)
+ set(READLINE_FOUND TRUE)
+endif(HAVE_COMPLETION_FUNCTION AND HAVE_COPY_TEXT)
-if(Readline_LIBRARY AND OPENBSD)
- list(APPEND EXTRA_LIBRARIES curses)
-endif()
diff --git a/src/common/memwipe.h b/contrib/epee/include/memwipe.h
index c3b4ce8ab..c3b4ce8ab 100644
--- a/src/common/memwipe.h
+++ b/contrib/epee/include/memwipe.h
diff --git a/contrib/epee/include/net/http_auth.h b/contrib/epee/include/net/http_auth.h
index 841cebc17..71f56b570 100644
--- a/contrib/epee/include/net/http_auth.h
+++ b/contrib/epee/include/net/http_auth.h
@@ -71,8 +71,8 @@ namespace net_utils
std::uint32_t counter;
};
- http_server_auth() : user() {}
- http_server_auth(login credentials);
+ http_server_auth() : user(), rng() {}
+ http_server_auth(login credentials, std::function<void(size_t, uint8_t*)> r);
//! \return Auth response, or `boost::none` iff `request` had valid auth.
boost::optional<http_response_info> get_response(const http_request_info& request)
@@ -81,10 +81,13 @@ namespace net_utils
return do_get_response(request);
return boost::none;
}
+
private:
boost::optional<http_response_info> do_get_response(const http_request_info& request);
boost::optional<session> user;
+
+ std::function<void(size_t, uint8_t*)> rng;
};
//! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added.
diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h
index 652d8ff6f..b4485d1cd 100644
--- a/contrib/epee/include/net/http_protocol_handler.h
+++ b/contrib/epee/include/net/http_protocol_handler.h
@@ -160,6 +160,7 @@ namespace net_utils
struct custum_handler_config: public http_server_config
{
i_http_server_handler<t_connection_context>* m_phandler;
+ std::function<void(size_t, uint8_t*)> rng;
};
/************************************************************************/
@@ -176,7 +177,7 @@ namespace net_utils
: simple_http_connection_handler<t_connection_context>(psnd_hndlr, config),
m_config(config),
m_conn_context(conn_context),
- m_auth(m_config.m_user ? http_server_auth{*m_config.m_user} : http_server_auth{})
+ m_auth(m_config.m_user ? http_server_auth{*m_config.m_user, config.rng} : http_server_auth{})
{}
inline bool handle_request(const http_request_info& query_info, http_response_info& response)
{
diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h
index 8b8e31b51..1a97e610a 100644
--- a/contrib/epee/include/net/http_server_impl_base.h
+++ b/contrib/epee/include/net/http_server_impl_base.h
@@ -55,13 +55,14 @@ namespace epee
: m_net_server(external_io_service)
{}
- bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
+ bool init(std::function<void(size_t, uint8_t*)> rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
std::vector<std::string> access_control_origins = std::vector<std::string>(),
boost::optional<net_utils::http::login> user = boost::none)
{
//set self as callback handler
m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
+ m_net_server.get_config_object().rng = std::move(rng);
//here set folder for hosting reqests
m_net_server.get_config_object().m_folder = "";
diff --git a/contrib/epee/include/net/network_throttle.hpp b/contrib/epee/include/net/network_throttle.hpp
index fffd22a6a..225ffee04 100644
--- a/contrib/epee/include/net/network_throttle.hpp
+++ b/contrib/epee/include/net/network_throttle.hpp
@@ -99,8 +99,6 @@ struct calculate_times_struct {
typedef calculate_times_struct calculate_times_struct;
-namespace cryptonote { class cryptonote_protocol_handler_base; } // a friend class // TODO friend not working
-
/***
@brief Access to simple throttles, with singlton to access global network limits
*/
@@ -117,7 +115,6 @@ class network_throttle_manager {
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!
friend class connection_basic_pimpl; // ditto
diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h
index d4413a71b..5791e1998 100644
--- a/contrib/epee/include/serialization/keyvalue_serialization.h
+++ b/contrib/epee/include/serialization/keyvalue_serialization.h
@@ -31,7 +31,6 @@
#include "misc_log_ex.h"
#include "enableable.h"
#include "keyvalue_serialization_overloads.h"
-#include "serialization/serialization.h"
namespace epee
{
diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h
index 52aa09eba..5d9664a65 100644
--- a/contrib/epee/include/storages/portable_storage_val_converters.h
+++ b/contrib/epee/include/storages/portable_storage_val_converters.h
@@ -28,6 +28,7 @@
#pragma once
+#include <time.h>
#include <boost/regex.hpp>
#include "misc_language.h"
@@ -149,9 +150,8 @@ POP_WARNINGS
else if (boost::regex_match (from, boost::regex("\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\dZ")))
{
// Convert to unix timestamp
- std::tm tm = {};
- std::istringstream ss(from);
- if (ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S"))
+ struct tm tm;
+ if (strptime(from.c_str(), "%Y-%m-%dT%H:%M:%S", &tm))
to = std::mktime(&tm);
} else
ASSERT_AND_THROW_WRONG_CONVERSION();
diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt
index b6967e8fc..9d104ceeb 100644
--- a/contrib/epee/src/CMakeLists.txt
+++ b/contrib/epee/src/CMakeLists.txt
@@ -26,12 +26,16 @@
# 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.
-add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp
+add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c
connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp)
if (USE_READLINE AND GNU_READLINE_FOUND)
add_library(epee_readline STATIC readline_buffer.cpp)
endif()
+if(HAVE_C11)
+SET_PROPERTY(SOURCE memwipe.c PROPERTY COMPILE_FLAGS -std=c11)
+endif()
+
# Build and install libepee if we're building for GUI
if (BUILD_GUI_DEPS)
if(IOS)
@@ -49,7 +53,6 @@ endif()
target_link_libraries(epee
PUBLIC
- cncrypto
easylogging
${Boost_FILESYSTEM_LIBRARY}
PRIVATE
diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp
index 534044a79..5848d1268 100644
--- a/contrib/epee/src/connection_basic.cpp
+++ b/contrib/epee/src/connection_basic.cpp
@@ -78,7 +78,6 @@
// TODO:
#include "net/network_throttle-detail.hpp"
-#include "cryptonote_core/cryptonote_core.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "net.p2p"
diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp
index f06f05528..5b8d892ff 100644
--- a/contrib/epee/src/http_auth.cpp
+++ b/contrib/epee/src/http_auth.cpp
@@ -66,7 +66,6 @@
#include <tuple>
#include <type_traits>
-#include "crypto/crypto.h"
#include "hex.h"
#include "md5_l.h"
#include "string_coding.h"
@@ -711,8 +710,8 @@ namespace epee
{
namespace http
{
- http_server_auth::http_server_auth(login credentials)
- : user(session{std::move(credentials)}) {
+ http_server_auth::http_server_auth(login credentials, std::function<void(size_t, uint8_t*)> r)
+ : user(session{std::move(credentials)}), rng(std::move(r)) {
}
boost::optional<http_response_info> http_server_auth::do_get_response(const http_request_info& request)
@@ -746,7 +745,7 @@ namespace epee
user->counter = 0;
{
std::array<std::uint8_t, 16> rand_128bit{{}};
- crypto::rand(rand_128bit.size(), rand_128bit.data());
+ rng(rand_128bit.size(), rand_128bit.data());
user->nonce = string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size());
}
return create_digest_response(user->nonce, is_stale);
diff --git a/src/common/memwipe.c b/contrib/epee/src/memwipe.c
index da7e9f346..da7e9f346 100644
--- a/src/common/memwipe.c
+++ b/contrib/epee/src/memwipe.c
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 88034a927..33c3341fa 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1333,10 +1333,11 @@ public:
* @brief get a txpool transaction's metadata
*
* @param txid the transaction id of the transation to lookup
+ * @param meta the metadata to return
*
- * @return the metadata associated with that transaction
+ * @return true if the tx meta was found, false otherwise
*/
- virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const = 0;
+ virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const = 0;
/**
* @brief get a txpool transaction's blob
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index ee4368e86..d19399bec 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1621,7 +1621,7 @@ void BlockchainLMDB::remove_txpool_tx(const crypto::hash& txid)
}
}
-txpool_tx_meta_t BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid) const
+bool BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1632,12 +1632,14 @@ txpool_tx_meta_t BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid) co
MDB_val k = {sizeof(txid), (void *)&txid};
MDB_val v;
auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ return false;
if (result != 0)
throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str()));
- const txpool_tx_meta_t meta = *(const txpool_tx_meta_t*)v.mv_data;
+ meta = *(const txpool_tx_meta_t*)v.mv_data;
TXN_POSTFIX_RDONLY();
- return meta;
+ return true;
}
bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const
@@ -2623,6 +2625,12 @@ bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks, uint64_t batch_bytes
m_batch_active = true;
memset(&m_wcursors, 0, sizeof(m_wcursors));
+ if (m_tinfo.get())
+ {
+ if (m_tinfo->m_ti_rflags.m_rf_txn)
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ }
LOG_PRINT_L3("batch transaction: begin");
return true;
@@ -2732,29 +2740,34 @@ void BlockchainLMDB::set_batch_transactions(bool batch_transactions)
bool BlockchainLMDB::block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const
{
bool ret = false;
+ mdb_threadinfo *tinfo;
if (m_write_txn && m_writer == boost::this_thread::get_id()) {
*mtxn = m_write_txn->m_txn;
*mcur = (mdb_txn_cursors *)&m_wcursors;
return ret;
}
- if (!m_tinfo.get())
+ /* Check for existing info and force reset if env doesn't match -
+ * only happens if env was opened/closed multiple times in same process
+ */
+ if (!(tinfo = m_tinfo.get()) || mdb_txn_env(tinfo->m_ti_rtxn) != m_env)
{
- m_tinfo.reset(new mdb_threadinfo);
- memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors));
- memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
- if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
+ tinfo = new mdb_threadinfo;
+ m_tinfo.reset(tinfo);
+ memset(&tinfo->m_ti_rcursors, 0, sizeof(tinfo->m_ti_rcursors));
+ memset(&tinfo->m_ti_rflags, 0, sizeof(tinfo->m_ti_rflags));
+ if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &tinfo->m_ti_rtxn))
throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str()));
ret = true;
- } else if (!m_tinfo->m_ti_rflags.m_rf_txn)
+ } else if (!tinfo->m_ti_rflags.m_rf_txn)
{
- if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn))
+ if (auto mdb_res = lmdb_txn_renew(tinfo->m_ti_rtxn))
throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str()));
ret = true;
}
if (ret)
- m_tinfo->m_ti_rflags.m_rf_txn = true;
- *mtxn = m_tinfo->m_ti_rtxn;
- *mcur = &m_tinfo->m_ti_rcursors;
+ tinfo->m_ti_rflags.m_rf_txn = true;
+ *mtxn = tinfo->m_ti_rtxn;
+ *mcur = &tinfo->m_ti_rcursors;
if (ret)
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -2772,28 +2785,9 @@ void BlockchainLMDB::block_txn_start(bool readonly)
{
if (readonly)
{
- bool didit = false;
- if (m_write_txn && m_writer == boost::this_thread::get_id())
- return;
- if (!m_tinfo.get())
- {
- m_tinfo.reset(new mdb_threadinfo);
- memset(&m_tinfo->m_ti_rcursors, 0, sizeof(m_tinfo->m_ti_rcursors));
- memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
- if (auto mdb_res = lmdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
- throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a read transaction for the db: ", mdb_res).c_str()));
- didit = true;
- } else if (!m_tinfo->m_ti_rflags.m_rf_txn)
- {
- if (auto mdb_res = lmdb_txn_renew(m_tinfo->m_ti_rtxn))
- throw0(DB_ERROR_TXN_START(lmdb_error("Failed to renew a read transaction for the db: ", mdb_res).c_str()));
- didit = true;
- }
- if (didit)
- {
- m_tinfo->m_ti_rflags.m_rf_txn = true;
- LOG_PRINT_L3("BlockchainLMDB::" << __func__ << " RO");
- }
+ MDB_txn *mtxn;
+ mdb_txn_cursors *mcur;
+ block_rtxn_start(&mtxn, &mcur);
return;
}
@@ -2818,6 +2812,12 @@ void BlockchainLMDB::block_txn_start(bool readonly)
throw0(DB_ERROR_TXN_START(lmdb_error("Failed to create a transaction for the db: ", mdb_res).c_str()));
}
memset(&m_wcursors, 0, sizeof(m_wcursors));
+ if (m_tinfo.get())
+ {
+ if (m_tinfo->m_ti_rflags.m_rf_txn)
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+ }
} else if (m_writer != boost::this_thread::get_id())
throw0(DB_ERROR_TXN_START((std::string("Attempted to start new write txn when batch txn already exists in ")+__FUNCTION__).c_str()));
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 85b62b5db..ecd14f11b 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -246,7 +246,7 @@ public:
virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
virtual bool txpool_has_tx(const crypto::hash &txid) const;
virtual void remove_txpool_tx(const crypto::hash& txid);
- virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const;
+ virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const;
virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const;
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 7ad08ea83..50887e35c 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -35,7 +35,6 @@ set(common_sources
download.cpp
util.cpp
i18n.cpp
- memwipe.c
password.cpp
perf_timer.cpp
threadpool.cpp
@@ -64,7 +63,6 @@ set(common_private_headers
util.h
varint.h
i18n.h
- memwipe.h
password.h
perf_timer.h
stack_trace.h
@@ -92,9 +90,5 @@ target_link_libraries(common
${OPENSSL_LIBRARIES}
${EXTRA_LIBRARIES})
-if(HAVE_C11)
-SET_PROPERTY(SOURCE memwipe.c PROPERTY COMPILE_FLAGS -std=c11)
-endif()
-
#monero_install_headers(common
# ${common_headers})
diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp
index 4a76e76fc..28a186bf0 100644
--- a/src/common/i18n.cpp
+++ b/src/common/i18n.cpp
@@ -35,6 +35,7 @@
#include "file_io_utils.h"
#include "common/util.h"
#include "common/i18n.h"
+#include "translation_files.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "i18n"
@@ -62,6 +63,7 @@ std::string i18n_get_language()
e = "en";
std::string language = e;
+ language = language.substr(0, language.find("."));
std::transform(language.begin(), language.end(), language.begin(), tolower);
return language;
}
@@ -137,25 +139,40 @@ int i18n_set_language(const char *directory, const char *base, std::string langu
i18n_log("Loading translations for language " << language);
boost::system::error_code ignored_ec;
- if (!boost::filesystem::exists(filename, ignored_ec)) {
+ if (boost::filesystem::exists(filename, ignored_ec)) {
+ if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
+ i18n_log("Failed to load translations file: " << filename);
+ return -1;
+ }
+ } else {
i18n_log("Translations file not found: " << filename);
- const char *underscore = strchr(language.c_str(), '_');
- if (underscore) {
- std::string fallback_language = std::string(language, 0, underscore - language.c_str());
- filename = std::string(directory) + "/" + base + "_" + fallback_language + ".qm";
- i18n_log("Not found, loading translations for language " << fallback_language);
- if (!boost::filesystem::exists(filename, ignored_ec)) {
- i18n_log("Translations file not found: " << filename);
+ filename = std::string(base) + "_" + language + ".qm";
+ if (!find_embedded_file(filename, contents)) {
+ i18n_log("Embedded translations file not found: " << filename);
+ const char *underscore = strchr(language.c_str(), '_');
+ if (underscore) {
+ std::string fallback_language = std::string(language, 0, underscore - language.c_str());
+ filename = std::string(directory) + "/" + base + "_" + fallback_language + ".qm";
+ i18n_log("Loading translations for language " << fallback_language);
+ if (boost::filesystem::exists(filename, ignored_ec)) {
+ if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
+ i18n_log("Failed to load translations file: " << filename);
+ return -1;
+ }
+ } else {
+ i18n_log("Translations file not found: " << filename);
+ filename = std::string(base) + "_" + fallback_language + ".qm";
+ if (!find_embedded_file(filename, contents)) {
+ i18n_log("Embedded translations file not found: " << filename);
+ return -1;
+ }
+ }
+ } else {
return -1;
}
}
}
- if (!epee::file_io_utils::load_file_to_string(filename, contents)) {
- i18n_log("Failed to load translations file: " << filename);
- return -1;
- }
-
data = (const unsigned char*)contents.c_str();
datalen = contents.size();
idx = 0;
diff --git a/src/common/password.cpp b/src/common/password.cpp
index dc0856160..011123300 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -46,7 +46,7 @@
#include "readline_buffer.h"
#endif
-#include "common/memwipe.h"
+#include "memwipe.h"
namespace
{
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index 20c5765b0..5d749e08e 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -34,6 +34,8 @@
#include "cryptonote_config.h"
#include "common/util.h"
+static __thread int depth = 0;
+
namespace tools
{
threadpool::threadpool() : running(true), active(0) {
@@ -60,11 +62,13 @@ threadpool::~threadpool() {
void threadpool::submit(waiter *obj, std::function<void()> f) {
entry e = {obj, f};
boost::unique_lock<boost::mutex> lock(mutex);
- if (active == max && !queue.empty()) {
+ if ((active == max && !queue.empty()) || depth > 0) {
// if all available threads are already running
// and there's work waiting, just run in current thread
lock.unlock();
+ ++depth;
f();
+ --depth;
} else {
if (obj)
obj->inc();
@@ -106,7 +110,9 @@ void threadpool::run() {
e = queue.front();
queue.pop_front();
lock.unlock();
+ ++depth;
e.f();
+ --depth;
if (e.wo)
e.wo->dec();
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 2a2f50c4f..a4a435104 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -39,6 +39,7 @@
#include "wipeable_string.h"
using namespace epee;
+#include "crypto/crypto.h"
#include "util.h"
#include "memwipe.h"
#include "cryptonote_config.h"
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index fd71a87e7..764b30273 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -76,6 +76,7 @@ monero_add_library(cncrypto
${crypto_private_headers})
target_link_libraries(cncrypto
PUBLIC
+ epee
${Boost_SYSTEM_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c
index 1e43f9c4d..95b2a6927 100644
--- a/src/crypto/blake256.c
+++ b/src/crypto/blake256.c
@@ -157,7 +157,7 @@ void blake256_update(state *S, const uint8_t *data, uint64_t datalen) {
int left = S->buflen >> 3;
int fill = 64 - left;
- if (left && (((datalen >> 3) & 0x3F) >= (unsigned) fill)) {
+ if (left && (((datalen >> 3)) >= (unsigned) fill)) {
memcpy((void *) (S->buf + left), (void *) data, fill);
S->t[0] += 512;
if (S->t[0] == 0) S->t[1]++;
diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h
index a9665030d..c11e4aa2f 100644
--- a/src/crypto/chacha.h
+++ b/src/crypto/chacha.h
@@ -39,7 +39,7 @@
#if defined(__cplusplus)
#include <memory.h>
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "hash.h"
namespace crypto {
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 0ce5e6d7a..a929302c1 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -41,7 +41,7 @@
#include "common/pod-class.h"
#include "common/util.h"
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "generic-ops.h"
#include "hex.h"
#include "span.h"
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index 821c21d84..c81901f4e 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -36,7 +36,6 @@
#include <cstring> // memcmp
#include <sstream>
#include <atomic>
-#include "serialization/serialization.h"
#include "serialization/variant.h"
#include "serialization/vector.h"
#include "serialization/binary_archive.h"
diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp
index 1183fda06..929be0d5a 100644
--- a/src/cryptonote_basic/cryptonote_basic_impl.cpp
+++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp
@@ -34,7 +34,7 @@ using namespace epee;
#include "cryptonote_basic_impl.h"
#include "string_tools.h"
#include "serialization/binary_utils.h"
-#include "serialization/vector.h"
+#include "serialization/container.h"
#include "cryptonote_format_utils.h"
#include "cryptonote_config.h"
#include "misc_language.h"
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 21fa63842..5f6dc3bd6 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -35,6 +35,7 @@ using namespace epee;
#include <boost/algorithm/string.hpp>
#include "wipeable_string.h"
#include "string_tools.h"
+#include "serialization/string.h"
#include "cryptonote_format_utils.h"
#include "cryptonote_config.h"
#include "crypto/crypto.h"
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 709c5e852..4af987c3b 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -4198,9 +4198,9 @@ uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const
return m_db->get_txpool_tx_count(include_unrelayed_txes);
}
-txpool_tx_meta_t Blockchain::get_txpool_tx_meta(const crypto::hash& txid) const
+bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const
{
- return m_db->get_txpool_tx_meta(txid);
+ return m_db->get_txpool_tx_meta(txid, meta);
}
bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 2d5307ac0..25e573a2c 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -914,7 +914,7 @@ namespace cryptonote
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
void remove_txpool_tx(const crypto::hash &txid);
uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
- txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const;
+ bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const;
bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const;
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index e6f217463..8773c1f74 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -371,7 +371,12 @@ namespace cryptonote
try
{
LockedTXN lock(m_blockchain);
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(id);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(id, meta))
+ {
+ MERROR("Failed to find tx in txpool");
+ return false;
+ }
cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(id);
if (!parse_and_validate_tx_from_blob(txblob, tx))
{
@@ -514,10 +519,13 @@ namespace cryptonote
{
try
{
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(it->first);
- meta.relayed = true;
- meta.last_relayed_time = now;
- m_blockchain.update_txpool_tx(it->first, meta);
+ txpool_tx_meta_t meta;
+ if (m_blockchain.get_txpool_tx_meta(it->first, meta))
+ {
+ meta.relayed = true;
+ meta.last_relayed_time = now;
+ m_blockchain.update_txpool_tx(it->first, meta);
+ }
}
catch (const std::exception &e)
{
@@ -696,7 +704,11 @@ namespace cryptonote
{
try
{
- meta = m_blockchain.get_txpool_tx_meta(tx_id_hash);
+ if (!m_blockchain.get_txpool_tx_meta(tx_id_hash, meta))
+ {
+ MERROR("Failed to get tx meta from txpool");
+ return false;
+ }
if (!meta.relayed)
// Do not include that transaction if in restricted mode and it's not relayed
continue;
@@ -918,7 +930,13 @@ namespace cryptonote
{
for (const crypto::hash &txid: it->second)
{
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(txid);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(txid, meta))
+ {
+ MERROR("Failed to find tx meta in txpool");
+ // continue, not fatal
+ continue;
+ }
if (!meta.double_spend_seen)
{
MDEBUG("Marking " << txid << " as double spending " << itk.k_image);
@@ -998,7 +1016,12 @@ namespace cryptonote
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
while (sorted_it != m_txs_by_fee_and_receive_time.end())
{
- txpool_tx_meta_t meta = m_blockchain.get_txpool_tx_meta(sorted_it->second);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta))
+ {
+ MERROR(" failed to find tx meta");
+ continue;
+ }
LOG_PRINT_L2("Considering " << sorted_it->second << ", size " << meta.blob_size << ", current block size " << total_size << "/" << max_total_size << ", current coinbase " << print_money(best_coinbase));
// Can not exceed maximum block size
diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt
index 5ce2198ae..79964e873 100644
--- a/src/mnemonics/CMakeLists.txt
+++ b/src/mnemonics/CMakeLists.txt
@@ -57,6 +57,7 @@ monero_add_library(mnemonics
${mnemonics_private_headers})
target_link_libraries(mnemonics
PUBLIC
+ epee
easylogging
${Boost_SYSTEM_LIBRARY}
PRIVATE
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt
index 123b0a272..3fc053dc7 100644
--- a/src/p2p/CMakeLists.txt
+++ b/src/p2p/CMakeLists.txt
@@ -46,5 +46,6 @@ target_link_libraries(p2p
${Boost_FILESYSTEM_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
+ ${Boost_SERIALIZATION_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 5ea2dcc7c..2df797360 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -47,7 +47,7 @@ extern "C" {
#include "hex.h"
#include "span.h"
-#include "serialization/serialization.h"
+#include "serialization/vector.h"
#include "serialization/debug_archive.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index 748c6b8c1..19ea93902 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -101,6 +101,7 @@ target_link_libraries(rpc_base
epee
${Boost_REGEX_LIBRARY}
${Boost_THREAD_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
@@ -125,6 +126,7 @@ target_link_libraries(daemon_messages
target_link_libraries(daemon_rpc_server
LINK_PRIVATE
+ rpc
cryptonote_core
cryptonote_protocol
daemon_messages
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index a6109cb89..4966b107d 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -106,8 +106,9 @@ namespace cryptonote
if (rpc_config->login)
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
+ auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
- std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
+ rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
diff --git a/src/serialization/container.h b/src/serialization/container.h
new file mode 100644
index 000000000..978a59d2a
--- /dev/null
+++ b/src/serialization/container.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include "serialization.h"
+
+namespace serialization
+{
+ namespace detail
+ {
+ template <typename Archive, class T>
+ bool serialize_container_element(Archive& ar, T& e)
+ {
+ return ::do_serialize(ar, e);
+ }
+
+ template <typename Archive>
+ bool serialize_container_element(Archive& ar, uint32_t& e)
+ {
+ ar.serialize_varint(e);
+ return true;
+ }
+
+ template <typename Archive>
+ bool serialize_container_element(Archive& ar, uint64_t& e)
+ {
+ ar.serialize_varint(e);
+ return true;
+ }
+
+ template <typename C>
+ void do_reserve(C &c, size_t N) {}
+ }
+}
+
+template <template <bool> class Archive, typename C>
+bool do_serialize_container(Archive<false> &ar, C &v)
+{
+ size_t cnt;
+ ar.begin_array(cnt);
+ if (!ar.stream().good())
+ return false;
+ v.clear();
+
+ // very basic sanity check
+ if (ar.remaining_bytes() < cnt) {
+ ar.stream().setstate(std::ios::failbit);
+ return false;
+ }
+
+ ::serialization::detail::do_reserve(v, cnt);
+
+ for (size_t i = 0; i < cnt; i++) {
+ if (i > 0)
+ ar.delimit_array();
+ typename C::value_type e;
+ if (!::serialization::detail::serialize_container_element(ar, e))
+ return false;
+ ::serialization::detail::do_add(v, std::move(e));
+ if (!ar.stream().good())
+ return false;
+ }
+ ar.end_array();
+ return true;
+}
+
+template <template <bool> class Archive, typename C>
+bool do_serialize_container(Archive<true> &ar, C &v)
+{
+ size_t cnt = v.size();
+ ar.begin_array(cnt);
+ for (auto i = v.begin(); i != v.end(); ++i)
+ {
+ if (!ar.stream().good())
+ return false;
+ if (i != v.begin())
+ ar.delimit_array();
+ if(!::serialization::detail::serialize_container_element(ar, const_cast<typename C::value_type&>(*i)))
+ return false;
+ if (!ar.stream().good())
+ return false;
+ }
+ ar.end_array();
+ return true;
+}
diff --git a/src/serialization/deque.h b/src/serialization/deque.h
new file mode 100644
index 000000000..994d3f195
--- /dev/null
+++ b/src/serialization/deque.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include <deque>
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::deque<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::deque<T> &v);
+
+namespace serialization
+{
+ namespace detail
+ {
+ template <typename T>
+ void do_reserve(std::deque<T> &c, size_t N)
+ {
+ c.reserve(N);
+ }
+
+ template <typename T>
+ void do_add(std::deque<T> &c, T &&e)
+ {
+ c.emplace_back(std::move(e));
+ }
+ }
+}
+
+#include "serialization.h"
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::deque<T> &v) { return do_serialize_container(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::deque<T> &v) { return do_serialize_container(ar, v); }
+
diff --git a/src/serialization/list.h b/src/serialization/list.h
index d0fb72163..d725458e7 100644
--- a/src/serialization/list.h
+++ b/src/serialization/list.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2015, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,71 +30,29 @@
#pragma once
-#include "serialization.h"
+#include <list>
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::list<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::list<T> &v);
namespace serialization
{
namespace detail
{
- template <typename Archive, class T>
- bool serialize_list_element(Archive& ar, T& e)
- {
- return ::do_serialize(ar, e);
- }
-
- template <typename Archive>
- bool serialize_list_element(Archive& ar, uint64_t& e)
+ template <typename T>
+ void do_add(std::list<T> &c, T &&e)
{
- ar.serialize_varint(e);
- return true;
+ c.emplace_back(std::move(e));
}
}
}
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::list<T> &l)
-{
- size_t cnt;
- ar.begin_array(cnt);
- if (!ar.stream().good())
- return false;
- l.clear();
-
- // very basic sanity check
- if (ar.remaining_bytes() < cnt) {
- ar.stream().setstate(std::ios::failbit);
- return false;
- }
-
- for (size_t i = 0; i < cnt; i++) {
- if (i > 0)
- ar.delimit_array();
- l.push_back(T());
- T &t = l.back();
- if (!::serialization::detail::serialize_list_element(ar, t))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
+#include "serialization.h"
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::list<T> &l)
-{
- size_t cnt = l.size();
- ar.begin_array(cnt);
- for (typename std::list<T>::iterator i = l.begin(); i != l.end(); ++i) {
- if (!ar.stream().good())
- return false;
- if (i != l.begin())
- ar.delimit_array();
- if(!::serialization::detail::serialize_list_element(ar, *i))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
+bool do_serialize(Archive<false> &ar, std::list<T> &v) { return do_serialize_container(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::list<T> &v) { return do_serialize_container(ar, v); }
+
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 9e23f0791..56496c790 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -63,15 +63,17 @@ struct is_blob_type { typedef boost::false_type type; };
template <class T>
struct has_free_serializer { typedef boost::true_type type; };
-/*! \struct is_pair_type
+/*! \struct is_basic_type
*
* \brief a descriptor for dispatching serialize
*/
template <class T>
-struct is_pair_type { typedef boost::false_type type; };
+struct is_basic_type { typedef boost::false_type type; };
template<typename F, typename S>
-struct is_pair_type<std::pair<F,S>> { typedef boost::true_type type; };
+struct is_basic_type<std::pair<F,S>> { typedef boost::true_type type; };
+template<>
+struct is_basic_type<std::string> { typedef boost::true_type type; };
/*! \struct serializer
*
@@ -89,7 +91,7 @@ struct is_pair_type<std::pair<F,S>> { typedef boost::true_type type; };
template <class Archive, class T>
struct serializer{
static bool serialize(Archive &ar, T &v) {
- return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type(), typename is_pair_type<T>::type());
+ return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type(), typename is_basic_type<T>::type());
}
template<typename A>
static bool serialize(Archive &ar, T &v, boost::false_type, boost::true_type, A a) {
@@ -361,9 +363,3 @@ namespace serialization {
return r && check_stream_state(ar);
}
}
-
-#include "string.h"
-#include "vector.h"
-#include "list.h"
-#include "pair.h"
-#include "set.h"
diff --git a/src/serialization/set.h b/src/serialization/set.h
index 54b4eb3ab..e6eff62a9 100644
--- a/src/serialization/set.h
+++ b/src/serialization/set.h
@@ -30,98 +30,29 @@
#pragma once
-#include "serialization.h"
+#include <set>
template <template <bool> class Archive, class T>
bool do_serialize(Archive<false> &ar, std::set<T> &v);
template <template <bool> class Archive, class T>
bool do_serialize(Archive<true> &ar, std::set<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v);
namespace serialization
{
namespace detail
{
- template <typename Archive, class T>
- bool serialize_set_element(Archive& ar, T& e)
+ template <typename T>
+ void do_add(std::set<T> &c, T &&e)
{
- return ::do_serialize(ar, e);
- }
-
- template <typename Archive>
- bool serialize_set_element(Archive& ar, uint32_t& e)
- {
- ar.serialize_varint(e);
- return true;
- }
-
- template <typename Archive>
- bool serialize_set_element(Archive& ar, uint64_t& e)
- {
- ar.serialize_varint(e);
- return true;
+ c.insert(std::move(e));
}
}
}
-template <template <bool> class Archive, class T>
-bool do_serialize_set(Archive<false> &ar, T &v)
-{
- size_t cnt;
- ar.begin_array(cnt);
- if (!ar.stream().good())
- return false;
- v.clear();
-
- // very basic sanity check
- if (ar.remaining_bytes() < cnt) {
- ar.stream().setstate(std::ios::failbit);
- return false;
- }
-
- for (size_t i = 0; i < cnt; i++) {
- if (i > 0)
- ar.delimit_array();
- typename T::key_type k;
- if (!::serialization::detail::serialize_set_element(ar, k))
- return false;
- v.insert(std::move(k));
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
-
-template <template <bool> class Archive, class T>
-bool do_serialize_set(Archive<true> &ar, T &v)
-{
- size_t cnt = v.size();
- ar.begin_array(cnt);
- bool first = true;
- for (const typename T::key_type &k: v) {
- if (!ar.stream().good())
- return false;
- if (!first)
- ar.delimit_array();
- if(!::serialization::detail::serialize_set_element(ar, const_cast<typename T::key_type&>(k)))
- return false;
- if (!ar.stream().good())
- return false;
- first = false;
- }
- ar.end_array();
- return true;
-}
+#include "serialization.h"
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::set<T> &v) { return do_serialize_set(ar, v); }
+bool do_serialize(Archive<false> &ar, std::set<T> &v) { return do_serialize_container(ar, v); }
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::set<T> &v) { return do_serialize_set(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v) { return do_serialize_set(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v) { return do_serialize_set(ar, v); }
+bool do_serialize(Archive<true> &ar, std::set<T> &v) { return do_serialize_container(ar, v); }
+
diff --git a/src/serialization/unordered_set.h b/src/serialization/unordered_set.h
new file mode 100644
index 000000000..b277f0c4a
--- /dev/null
+++ b/src/serialization/unordered_set.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include <set>
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v);
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v);
+
+namespace serialization
+{
+ namespace detail
+ {
+ template <typename T>
+ void do_add(std::unordered_set<T> &c, T &&e)
+ {
+ c.insert(std::move(e));
+ }
+ }
+}
+
+#include "serialization.h"
+
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<false> &ar, std::unordered_set<T> &v) { return do_serialize_container(ar, v); }
+template <template <bool> class Archive, class T>
+bool do_serialize(Archive<true> &ar, std::unordered_set<T> &v) { return do_serialize_container(ar, v); }
+
diff --git a/src/serialization/vector.h b/src/serialization/vector.h
index 12fd59558..9cf3d8272 100644
--- a/src/serialization/vector.h
+++ b/src/serialization/vector.h
@@ -30,6 +30,7 @@
#pragma once
+#include <vector>
#include "serialization.h"
template <template <bool> class Archive, class T>
@@ -37,91 +38,28 @@ bool do_serialize(Archive<false> &ar, std::vector<T> &v);
template <template <bool> class Archive, class T>
bool do_serialize(Archive<true> &ar, std::vector<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::deque<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::deque<T> &v);
-
namespace serialization
{
namespace detail
{
- template <typename Archive, class T>
- bool serialize_vector_element(Archive& ar, T& e)
- {
- return ::do_serialize(ar, e);
- }
-
- template <typename Archive>
- bool serialize_vector_element(Archive& ar, uint32_t& e)
+ template <typename T>
+ void do_reserve(std::vector<T> &c, size_t N)
{
- ar.serialize_varint(e);
- return true;
+ c.reserve(N);
}
- template <typename Archive>
- bool serialize_vector_element(Archive& ar, uint64_t& e)
+ template <typename T>
+ void do_add(std::vector<T> &c, T &&e)
{
- ar.serialize_varint(e);
- return true;
+ c.emplace_back(std::move(e));
}
}
}
-template <template <bool> class Archive, class T>
-bool do_serialize_vd(Archive<false> &ar, T &v)
-{
- size_t cnt;
- ar.begin_array(cnt);
- if (!ar.stream().good())
- return false;
- v.clear();
-
- // very basic sanity check
- if (ar.remaining_bytes() < cnt) {
- ar.stream().setstate(std::ios::failbit);
- return false;
- }
-
- v.reserve(cnt);
- for (size_t i = 0; i < cnt; i++) {
- if (i > 0)
- ar.delimit_array();
- v.resize(i+1);
- if (!::serialization::detail::serialize_vector_element(ar, v[i]))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
-
-template <template <bool> class Archive, class T>
-bool do_serialize_vd(Archive<true> &ar, T &v)
-{
- size_t cnt = v.size();
- ar.begin_array(cnt);
- for (size_t i = 0; i < cnt; i++) {
- if (!ar.stream().good())
- return false;
- if (i > 0)
- ar.delimit_array();
- if(!::serialization::detail::serialize_vector_element(ar, v[i]))
- return false;
- if (!ar.stream().good())
- return false;
- }
- ar.end_array();
- return true;
-}
+#include "container.h"
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); }
+bool do_serialize(Archive<false> &ar, std::vector<T> &v) { return do_serialize_container(ar, v); }
template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::vector<T> &v) { return do_serialize_vd(ar, v); }
+bool do_serialize(Archive<true> &ar, std::vector<T> &v) { return do_serialize_container(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::deque<T> &v) { return do_serialize_vd(ar, v); }
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index bc3b3e926..b9a3e45b4 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -121,6 +121,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_mnemonic_language = {"mnemonic-language", sw::tr("Language for mnemonic"), ""};
const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
+ const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Create non-deterministic view and spend keys"), false};
const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false};
const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
@@ -516,48 +517,55 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
bool simple_wallet::print_seed(bool encrypted)
{
bool success = false;
- std::string electrum_words;
+ std::string seed;
+ bool ready, multisig;
- if (m_wallet->multisig())
- {
- fail_msg_writer() << tr("wallet is multisig and has no seed");
- return true;
- }
if (m_wallet->watch_only())
{
fail_msg_writer() << tr("wallet is watch-only and has no seed");
return true;
}
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
- if (m_wallet->is_deterministic())
- {
- 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);
- }
- epee::wipeable_string seed_pass;
- if (encrypted)
+ multisig = m_wallet->multisig(&ready);
+ if (multisig)
+ {
+ if (!ready)
{
- auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
- if (std::cin.eof() || !pwd_container)
- return true;
- seed_pass = pwd_container->password();
+ fail_msg_writer() << tr("wallet is multisig but not yet finalized");
+ return true;
}
+ }
+ else if (!m_wallet->is_deterministic())
+ {
+ fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
+ return true;
+ }
- success = m_wallet->get_seed(electrum_words, seed_pass);
+ epee::wipeable_string seed_pass;
+ if (encrypted)
+ {
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
+ auto pwd_container = tools::password_container::prompt(true, tr("Enter optional seed encryption passphrase, empty to see raw seed"));
+ if (std::cin.eof() || !pwd_container)
+ return true;
+ seed_pass = pwd_container->password();
}
+ if (multisig)
+ success = m_wallet->get_multisig_seed(seed, seed_pass);
+ else if (m_wallet->is_deterministic())
+ success = m_wallet->get_seed(seed, seed_pass);
+
if (success)
{
- print_seed(electrum_words);
+ print_seed(seed);
}
else
{
- fail_msg_writer() << tr("wallet is non-deterministic and has no seed");
+ fail_msg_writer() << tr("Failed to retrieve seed");
}
return true;
}
@@ -1984,6 +1992,8 @@ static bool might_be_partial_seed(std::string words)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
+ std::string multisig_keys;
+
if (!handle_command_line(vm))
return false;
@@ -2001,49 +2011,91 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
std::string old_language;
// check for recover flag. if present, require electrum word list (only recovery option for now).
- if (m_restore_deterministic_wallet)
+ if (m_restore_deterministic_wallet || m_restore_multisig_wallet)
{
if (m_non_deterministic)
{
- fail_msg_writer() << tr("can't specify both --restore-deterministic-wallet and --non-deterministic");
+ fail_msg_writer() << tr("can't specify both --restore-deterministic-wallet or --restore-multisig-wallet and --non-deterministic");
return false;
}
if (!m_wallet_file.empty())
{
- fail_msg_writer() << tr("--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
+ if (m_restore_multisig_wallet)
+ fail_msg_writer() << tr("--restore-multisig-wallet uses --generate-new-wallet, not --wallet-file");
+ else
+ fail_msg_writer() << tr("--restore-deterministic-wallet uses --generate-new-wallet, not --wallet-file");
return false;
}
if (m_electrum_seed.empty())
{
- m_electrum_seed = "";
- do
+ if (m_restore_multisig_wallet)
{
- const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
- std::string electrum_seed = input_line(prompt);
- if (std::cin.eof())
- return false;
- if (electrum_seed.empty())
+ const char *prompt = "Specify multisig seed: ";
+ m_electrum_seed = input_line(prompt);
+ if (std::cin.eof())
+ return false;
+ if (m_electrum_seed.empty())
+ {
+ fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"multisig seed here\"");
+ return false;
+ }
+ }
+ else
+ {
+ m_electrum_seed = "";
+ do
{
- fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
- return false;
- }
- m_electrum_seed += electrum_seed + " ";
- } while (might_be_partial_seed(m_electrum_seed));
+ const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
+ std::string electrum_seed = input_line(prompt);
+ if (std::cin.eof())
+ return false;
+ if (electrum_seed.empty())
+ {
+ fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
+ return false;
+ }
+ m_electrum_seed += electrum_seed + " ";
+ } while (might_be_partial_seed(m_electrum_seed));
+ }
}
- if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language))
+ if (m_restore_multisig_wallet)
{
- fail_msg_writer() << tr("Electrum-style word list failed verification");
- return false;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(m_electrum_seed, multisig_keys))
+ {
+ fail_msg_writer() << tr("Multisig seed failed verification");
+ return false;
+ }
+ }
+ else
+ {
+ if (!crypto::ElectrumWords::words_to_bytes(m_electrum_seed, m_recovery_key, old_language))
+ {
+ fail_msg_writer() << tr("Electrum-style word list failed verification");
+ return false;
+ }
}
+#ifdef HAVE_READLINE
+ rdln::suspend_readline pause_readline;
+#endif
auto pwd_container = tools::password_container::prompt(false, tr("Enter seed encryption passphrase, empty if none"));
if (std::cin.eof() || !pwd_container)
return false;
epee::wipeable_string seed_pass = pwd_container->password();
if (!seed_pass.empty())
- m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
+ {
+ if (m_restore_multisig_wallet)
+ {
+ crypto::secret_key key;
+ crypto::cn_slow_hash(seed_pass.data(), seed_pass.size(), (crypto::hash&)key);
+ sc_reduce32((unsigned char*)key.data);
+ multisig_keys = m_wallet->decrypt(multisig_keys, key, true);
+ }
+ else
+ m_recovery_key = cryptonote::decrypt_key(m_recovery_key, seed_pass);
+ }
}
if (!m_generate_from_view_key.empty())
{
@@ -2354,7 +2406,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
m_wallet_file = m_generate_new;
- bool r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
+ bool r;
+ if (m_restore_multisig_wallet)
+ r = new_wallet(vm, multisig_keys, old_language);
+ else
+ r = new_wallet(vm, m_recovery_key, m_restore_deterministic_wallet, m_non_deterministic, old_language);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
}
if (!m_restore_height && m_restoring)
@@ -2485,6 +2541,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_mnemonic_language = command_line::get_arg(vm, arg_mnemonic_language);
m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
+ m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet);
m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic);
m_trusted_daemon = command_line::get_arg(vm, arg_trusted_daemon);
m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
@@ -2495,7 +2552,8 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
!m_generate_from_keys.empty() ||
!m_generate_from_multisig_keys.empty() ||
!m_generate_from_json.empty() ||
- m_restore_deterministic_wallet;
+ m_restore_deterministic_wallet ||
+ m_restore_multisig_wallet;
return true;
}
@@ -2695,6 +2753,49 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
+ const std::string &multisig_keys, const std::string &old_language)
+{
+ auto rc = tools::wallet2::make_new(vm, password_prompter);
+ m_wallet = std::move(rc.first);
+ if (!m_wallet)
+ {
+ return false;
+ }
+
+ std::string mnemonic_language = old_language;
+
+ std::vector<std::string> language_list;
+ crypto::ElectrumWords::get_language_list(language_list);
+ if (mnemonic_language.empty() && std::find(language_list.begin(), language_list.end(), m_mnemonic_language) != language_list.end())
+ {
+ mnemonic_language = m_mnemonic_language;
+ }
+
+ m_wallet->set_seed_language(mnemonic_language);
+
+ try
+ {
+ m_wallet->generate(m_wallet_file, std::move(rc.second).password(), multisig_keys);
+ bool ready;
+ uint32_t threshold, total;
+ if (!m_wallet->multisig(&ready, &threshold, &total) || !ready)
+ {
+ fail_msg_writer() << tr("failed to generate new mutlisig wallet");
+ return false;
+ }
+ message_writer(console_color_white, true) << boost::format(tr("Generated new %u/%u multisig wallet: ")) % threshold % total
+ << m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+ }
+ catch (const std::exception& e)
+ {
+ fail_msg_writer() << tr("failed to generate new wallet: ") << e.what();
+ return false;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
{
if (!tools::wallet2::wallet_valid_path_format(m_wallet_file))
@@ -3206,6 +3307,13 @@ bool simple_wallet::show_incoming_transfers(const std::vector<std::string>& args
{
if (!parse_subaddress_indices(local_args[0], subaddr_indices))
return true;
+ local_args.erase(local_args.begin());
+ }
+
+ if (local_args.size() > 0)
+ {
+ fail_msg_writer() << tr("usage: incoming_transfers [available|unavailable] [verbose] [index=<N>]");
+ return true;
}
tools::wallet2::transfer_container transfers;
@@ -4427,6 +4535,12 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::donate(const std::vector<std::string> &args_)
{
+ if(m_wallet->testnet())
+ {
+ fail_msg_writer() << tr("donations are not enabled on the testnet");
+ return true;
+ }
+
std::vector<std::string> local_args = args_;
if(local_args.empty() || local_args.size() > 5)
{
@@ -6094,6 +6208,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
try
{
+ LOCK_IDLE_SCOPE();
if (!m_wallet->export_key_images(filename))
{
fail_msg_writer() << tr("failed to save file ") << filename;
@@ -6126,6 +6241,7 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
}
std::string filename = args[0];
+ LOCK_IDLE_SCOPE();
try
{
uint64_t spent = 0, unspent = 0;
@@ -6157,6 +6273,7 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
std::string filename = args[0];
+ LOCK_IDLE_SCOPE();
try
{
std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs();
@@ -6255,6 +6372,7 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args)
boost::archive::binary_iarchive ar(iss);
ar >> outputs;
}
+ LOCK_IDLE_SCOPE();
size_t n_outputs = m_wallet->import_outputs(outputs);
success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported";
}
@@ -6473,6 +6591,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_command);
command_line::add_arg(desc_params, arg_restore_deterministic_wallet );
+ command_line::add_arg(desc_params, arg_restore_multisig_wallet );
command_line::add_arg(desc_params, arg_non_deterministic );
command_line::add_arg(desc_params, arg_electrum_seed );
command_line::add_arg(desc_params, arg_trusted_daemon);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index e5c00e542..024077ca1 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -92,6 +92,8 @@ namespace cryptonote
bool recover, bool two_random, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
+ bool new_wallet(const boost::program_options::variables_map& vm,
+ const std::string &multisig_keys, const std::string &old_language);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
@@ -306,6 +308,7 @@ namespace cryptonote
crypto::secret_key m_recovery_key; // recovery key (used as random for wallet gen)
bool m_restore_deterministic_wallet; // recover flag
+ bool m_restore_multisig_wallet; // recover flag
bool m_non_deterministic; // old 2-random generation
bool m_trusted_daemon;
bool m_allow_mismatched_daemon_version;
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index d6f4b9b98..20e8ba53d 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -53,6 +53,7 @@ using namespace epee;
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
+#include "serialization/string.h"
#include "cryptonote_basic/blobdatatype.h"
#include "mnemonics/electrum-words.h"
#include "common/i18n.h"
@@ -62,7 +63,7 @@ using namespace epee;
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include "common/json_util.h"
-#include "common/memwipe.h"
+#include "memwipe.h"
#include "common/base58.h"
#include "ringct/rctSigs.h"
@@ -610,6 +611,7 @@ wallet2::wallet2(bool testnet, bool restricted):
m_min_output_value(0),
m_merge_destinations(false),
m_confirm_backlog(true),
+ m_confirm_backlog_threshold(0),
m_is_initialized(false),
m_restricted(restricted),
is_old_file_format(false),
@@ -733,6 +735,70 @@ bool wallet2::get_seed(std::string& electrum_words, const epee::wipeable_string
return true;
}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase, bool raw) const
+{
+ bool ready;
+ uint32_t threshold, total;
+ if (!multisig(&ready, &threshold, &total))
+ {
+ std::cout << "This is not a multisig wallet" << std::endl;
+ return false;
+ }
+ if (!ready)
+ {
+ std::cout << "This multisig wallet is not yet finalized" << std::endl;
+ return false;
+ }
+ if (!raw && seed_language.empty())
+ {
+ std::cout << "seed_language not set" << std::endl;
+ return false;
+ }
+
+ crypto::secret_key skey;
+ crypto::public_key pkey;
+ const account_keys &keys = get_account().get_keys();
+ std::string data;
+ data.append((const char*)&threshold, sizeof(uint32_t));
+ data.append((const char*)&total, sizeof(uint32_t));
+ skey = keys.m_spend_secret_key;
+ data.append((const char*)&skey, sizeof(skey));
+ pkey = keys.m_account_address.m_spend_public_key;
+ data.append((const char*)&pkey, sizeof(pkey));
+ skey = keys.m_view_secret_key;
+ data.append((const char*)&skey, sizeof(skey));
+ pkey = keys.m_account_address.m_view_public_key;
+ data.append((const char*)&pkey, sizeof(pkey));
+ for (const auto &skey: keys.m_multisig_keys)
+ data.append((const char*)&skey, sizeof(skey));
+ for (const auto &signer: m_multisig_signers)
+ data.append((const char*)&signer, sizeof(signer));
+
+ if (!passphrase.empty())
+ {
+ crypto::secret_key key;
+ crypto::cn_slow_hash(passphrase.data(), passphrase.size(), (crypto::hash&)key);
+ sc_reduce32((unsigned char*)key.data);
+ data = encrypt(data, key, true);
+ }
+
+ if (raw)
+ {
+ seed = epee::string_tools::buff_to_hex_nodelimer(data);
+ }
+ else
+ {
+ if (!crypto::ElectrumWords::bytes_to_words(data.data(), data.size(), seed, seed_language))
+ {
+ std::cout << "Failed to encode seed";
+ return false;
+ }
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
@@ -2646,6 +2712,97 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
* \param password Password of wallet file
+ * \param multisig_data The multisig restore info and keys
+ */
+void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const std::string& multisig_data)
+{
+ clear();
+ prepare_file_names(wallet_);
+
+ if (!wallet_.empty())
+ {
+ boost::system::error_code ignored_ec;
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_wallet_file, ignored_ec), error::file_exists, m_wallet_file);
+ THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
+ }
+
+ m_account.generate(rct::rct2sk(rct::zero()), true, false);
+
+ THROW_WALLET_EXCEPTION_IF(multisig_data.size() < 32, error::invalid_multisig_seed);
+ size_t offset = 0;
+ uint32_t threshold = *(uint32_t*)(multisig_data.data() + offset);
+ offset += sizeof(uint32_t);
+ uint32_t total = *(uint32_t*)(multisig_data.data() + offset);
+ offset += sizeof(uint32_t);
+ THROW_WALLET_EXCEPTION_IF(threshold < 2, error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(total != threshold && total != threshold + 1, error::invalid_multisig_seed);
+ const size_t n_multisig_keys = total == threshold ? 1 : threshold;
+ THROW_WALLET_EXCEPTION_IF(multisig_data.size() != 8 + 32 * (4 + n_multisig_keys + total), error::invalid_multisig_seed);
+
+ std::vector<crypto::secret_key> multisig_keys;
+ std::vector<crypto::public_key> multisig_signers;
+ crypto::secret_key spend_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::secret_key);
+ crypto::public_key spend_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::public_key);
+ crypto::secret_key view_secret_key = *(crypto::secret_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::secret_key);
+ crypto::public_key view_public_key = *(crypto::public_key*)(multisig_data.data() + offset);
+ offset += sizeof(crypto::public_key);
+ for (size_t n = 0; n < n_multisig_keys; ++n)
+ {
+ multisig_keys.push_back(*(crypto::secret_key*)(multisig_data.data() + offset));
+ offset += sizeof(crypto::secret_key);
+ }
+ for (size_t n = 0; n < total; ++n)
+ {
+ multisig_signers.push_back(*(crypto::public_key*)(multisig_data.data() + offset));
+ offset += sizeof(crypto::public_key);
+ }
+
+ crypto::public_key calculated_view_public_key;
+ THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(view_secret_key, calculated_view_public_key), error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(view_public_key != calculated_view_public_key, error::invalid_multisig_seed);
+ crypto::public_key local_signer;
+ THROW_WALLET_EXCEPTION_IF(!crypto::secret_key_to_public_key(spend_secret_key, local_signer), error::invalid_multisig_seed);
+ THROW_WALLET_EXCEPTION_IF(std::find(multisig_signers.begin(), multisig_signers.end(), local_signer) == multisig_signers.end(), error::invalid_multisig_seed);
+ rct::key skey = rct::zero();
+ for (const auto &msk: multisig_keys)
+ sc_add(skey.bytes, skey.bytes, rct::sk2rct(msk).bytes);
+ THROW_WALLET_EXCEPTION_IF(!(rct::rct2sk(skey) == spend_secret_key), error::invalid_multisig_seed);
+
+ m_account.make_multisig(view_secret_key, spend_secret_key, spend_public_key, multisig_keys);
+ m_account.finalize_multisig(spend_public_key);
+
+ m_account_public_address = m_account.get_keys().m_account_address;
+ m_watch_only = false;
+ m_multisig = true;
+ m_multisig_threshold = threshold;
+ m_multisig_signers = multisig_signers;
+
+ if (!wallet_.empty())
+ {
+ 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));
+ if(!r) MERROR("String with address text not saved");
+ }
+
+ cryptonote::block b;
+ generate_genesis(b);
+ m_blockchain.push_back(get_block_hash(b));
+ add_subaddress_account(tr("Primary account"));
+
+ if (!wallet_.empty())
+ store();
+}
+
+/*!
+ * \brief Generates a wallet or restores one.
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
* \param recovery_param If it is a restore, the recovery key
* \param recover Whether it is a restore
* \param two_random Whether it is a non-deterministic wallet
@@ -6003,7 +6160,8 @@ void wallet2::light_wallet_get_unspent_outs()
add_tx_pub_key_to_extra(td.m_tx, tx_pub_key);
td.m_key_image = unspent_key_image;
- td.m_key_image_known = !m_watch_only;
+ td.m_key_image_known = !m_watch_only && !m_multisig;
+ td.m_key_image_partial = m_multisig;
td.m_amount = o.amount;
td.m_pk_index = 0;
td.m_internal_output_index = o.index;
@@ -6677,6 +6835,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
+ uint64_t inputs = 0, outputs = needed_fee;
+ for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
+ for (const auto &o: tx.dsts) outputs += o.amount;
+
+ if (inputs < outputs)
+ {
+ LOG_PRINT_L2("We don't have enough for the basic fee, switching to adding_fee");
+ adding_fee = true;
+ goto skip_tx;
+ }
+
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
tx.selected_transfers.size() << " inputs");
if (use_rct)
@@ -6752,6 +6921,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
}
+skip_tx:
// if unused_*_indices is empty while unused_*_indices_per_subaddr has multiple elements, and if we still have something to pay,
// pop front of unused_*_indices_per_subaddr and have unused_*_indices point to the front of unused_*_indices_per_subaddr
if ((!dsts.empty() && dsts[0].amount > 0) || adding_fee)
@@ -6804,37 +6974,48 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
- std::map<uint32_t, uint64_t> balance_per_subaddr = unlocked_balance_per_subaddress(subaddr_account);
-
- if (subaddr_indices.empty())
- {
- // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
- if (balance_per_subaddr.count(0) == 1 && balance_per_subaddr.size() > 1)
- balance_per_subaddr.erase(0);
- auto i = balance_per_subaddr.begin();
- std::advance(i, crypto::rand<size_t>() % balance_per_subaddr.size());
- subaddr_indices.insert(i->first);
- }
- for (uint32_t i : subaddr_indices)
- LOG_PRINT_L2("Spending from subaddress index " << i);
+ std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
- // gather all dust and non-dust outputs of specified subaddress
+ // gather all dust and non-dust outputs of specified subaddress (if any) and below specified threshold (if any)
+ bool fund_found = false;
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
- if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
+ if (!td.m_spent && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
{
+ fund_found = true;
if (below == 0 || td.amount() < below)
{
if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
- unused_transfers_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].first.push_back(i);
else
- unused_dust_indices.push_back(i);
+ unused_transfer_dust_indices_per_subaddr[td.m_subaddr_index.minor].second.push_back(i);
}
}
}
+ THROW_WALLET_EXCEPTION_IF(!fund_found, error::wallet_internal_error, "No unlocked balance in the specified subaddress(es)");
+ THROW_WALLET_EXCEPTION_IF(unused_transfer_dust_indices_per_subaddr.empty(), error::wallet_internal_error, "The smallest amount found is not below the specified threshold");
- THROW_WALLET_EXCEPTION_IF(unused_transfers_indices.empty() && unused_dust_indices.empty(), error::not_enough_money, 0, 0, 0); // not sure if a new error class (something like 'cant_sweep_empty'?) should be introduced
+ if (subaddr_indices.empty())
+ {
+ // in case subaddress index wasn't specified, choose non-empty subaddress randomly (with index=0 being chosen last)
+ if (unused_transfer_dust_indices_per_subaddr.count(0) == 1 && unused_transfer_dust_indices_per_subaddr.size() > 1)
+ unused_transfer_dust_indices_per_subaddr.erase(0);
+ auto i = unused_transfer_dust_indices_per_subaddr.begin();
+ std::advance(i, crypto::rand<size_t>() % unused_transfer_dust_indices_per_subaddr.size());
+ unused_transfers_indices = i->second.first;
+ unused_dust_indices = i->second.second;
+ LOG_PRINT_L2("Spending from subaddress index " << i->first);
+ }
+ else
+ {
+ for (const auto& p : unused_transfer_dust_indices_per_subaddr)
+ {
+ unused_transfers_indices.insert(unused_transfers_indices.end(), p.second.first.begin(), p.second.first.end());
+ unused_dust_indices.insert(unused_dust_indices.end(), p.second.second.begin(), p.second.second.end());
+ LOG_PRINT_L2("Spending from subaddress index " << p.first);
+ }
+ }
return create_transactions_from(address, is_subaddress, unused_transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra, trusted_daemon);
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index b1115f67b..2fbe05f89 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -436,6 +436,15 @@ namespace tools
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
/*!
+ * \brief Generates a wallet or restores one.
+ * \param wallet_ Name of wallet file
+ * \param password Password of wallet file
+ * \param multisig_data The multisig restore info and keys
+ */
+ void generate(const std::string& wallet_, const epee::wipeable_string& password,
+ const std::string& multisig_data);
+
+ /*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
* \param password Password of wallet file
@@ -610,6 +619,7 @@ namespace tools
bool watch_only() const { return m_watch_only; }
bool multisig(bool *ready = NULL, uint32_t *threshold = NULL, uint32_t *total = NULL) const;
bool has_multisig_partial_key_images() const;
+ bool get_multisig_seed(std::string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 234c22d85..023b53f28 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -60,6 +60,7 @@ namespace tools
// file_save_error
// invalid_password
// invalid_priority
+ // invalid_multisig_seed
// refresh_error *
// acc_outs_lookup_error
// block_parse_error
@@ -266,6 +267,16 @@ namespace tools
std::string to_string() const { return wallet_logic_error::to_string(); }
};
+ struct invalid_multisig_seed : public wallet_logic_error
+ {
+ explicit invalid_multisig_seed(std::string&& loc)
+ : wallet_logic_error(std::move(loc), "invalid multisig seed")
+ {
+ }
+
+ std::string to_string() const { return wallet_logic_error::to_string(); }
+ };
+
//----------------------------------------------------------------------------------------------------
struct invalid_pregenerated_random : public wallet_logic_error
{
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index f031b765d..4c1788f0b 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -229,8 +229,9 @@ namespace tools
m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
m_net_server.set_threads_prefix("RPC");
+ auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -662,7 +663,98 @@ namespace tools
}
return true;
}
+ //------------------------------------------------------------------------------------------------------------------------------
+ static std::string ptx_to_string(const tools::wallet2::pending_tx &ptx)
+ {
+ std::ostringstream oss;
+ boost::archive::portable_binary_oarchive ar(oss);
+ try
+ {
+ ar << ptx;
+ }
+ catch (...)
+ {
+ return "";
+ }
+ return epee::string_tools::buff_to_hex_nodelimer(oss.str());
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T> static bool is_error_value(const T &val) { return false; }
+ static bool is_error_value(const std::string &s) { return s.empty(); }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T, typename V>
+ static bool fill(T &where, V s)
+ {
+ if (is_error_value(s)) return false;
+ where = std::move(s);
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename T, typename V>
+ static bool fill(std::list<T> &where, V s)
+ {
+ if (is_error_value(s)) return false;
+ where.emplace_back(std::move(s));
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ static uint64_t total_amount(const tools::wallet2::pending_tx &ptx)
+ {
+ uint64_t amount = 0;
+ for (const auto &dest: ptx.dests) amount += dest.amount;
+ return amount;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ template<typename Ts, typename Tu>
+ bool wallet_rpc_server::fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay,
+ Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er)
+ {
+ for (const auto & ptx : ptx_vector)
+ {
+ if (get_tx_key)
+ {
+ std::string s = epee::string_tools::pod_to_hex(ptx.tx_key);
+ for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
+ s += epee::string_tools::pod_to_hex(additional_tx_key);
+ fill(tx_key, s);
+ }
+ // Compute amount leaving wallet in tx. By convention dests does not include change outputs
+ fill(amount, total_amount(ptx));
+ fill(fee, ptx.fee);
+ }
+ if (m_wallet->multisig())
+ {
+ multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
+ if (multisig_txset.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to save multisig tx set after creation";
+ return false;
+ }
+ }
+ else
+ {
+ if (!do_not_relay)
+ m_wallet->commit_tx(ptx_vector);
+
+ // populate response with tx hashes
+ for (auto & ptx : ptx_vector)
+ {
+ bool r = fill(tx_hash, epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
+ r = r && (!get_tx_hex || fill(tx_blob, epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx))));
+ r = r && (!get_tx_metadata || fill(tx_metadata, ptx_to_string(ptx)));
+ if (!r)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to save tx info";
+ return false;
+ }
+ }
+ }
+ return true;
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er)
{
@@ -705,55 +797,8 @@ namespace tools
return false;
}
- if (req.get_tx_key)
- {
- res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key);
- for (const crypto::secret_key& additional_tx_key : ptx_vector.back().additional_tx_keys)
- res.tx_key += epee::string_tools::pod_to_hex(additional_tx_key);
- }
- res.fee = ptx_vector.back().fee;
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hash
- res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx_vector.back().tx, blob);
- res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx_vector.back();
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
- }
- }
- return true;
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay,
+ res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
{
@@ -786,82 +831,12 @@ namespace tools
try
{
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
- uint64_t ptx_amount;
- std::vector<wallet2::pending_tx> ptx_vector;
LOG_PRINT_L2("on_transfer_split calling create_transactions_2");
- ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
- // populate response with tx hashes
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
- res.tx_key_list.back() += epee::string_tools::pod_to_hex(additional_tx_key);
- }
- // Compute amount leaving wallet in tx. By convention dests does not include change outputs
- ptx_amount = 0;
- for(auto & dt: ptx.dests)
- ptx_amount += dt.amount;
- res.amount_list.push_back(ptx_amount);
-
- res.fee_list.push_back(ptx.fee);
- }
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
-
- // populate response with tx hashes
- for (const auto & ptx : ptx_vector)
- {
- if (!req.do_not_relay)
- {
- LOG_PRINT_L2("on_transfer_split calling commit_tx");
- m_wallet->commit_tx(ptx_vector);
- LOG_PRINT_L2("on_transfer_split called commit_tx");
- }
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
-
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
{
@@ -885,69 +860,8 @@ namespace tools
{
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- }
- res.fee_list.push_back(ptx.fee);
- }
-
- if (m_wallet->multisig())
- {
- for (tools::wallet2::pending_tx &ptx: ptx_vector)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
{
@@ -985,68 +899,8 @@ namespace tools
uint64_t mixin = m_wallet->adjust_mixin(req.mixin);
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, dsts[0].is_subaddress, mixin, req.unlock_time, req.priority, extra, req.account_index, req.subaddr_indices, m_trusted_daemon);
- for (const auto & ptx : ptx_vector)
- {
- if (req.get_tx_keys)
- {
- res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key));
- }
- }
-
- if (m_wallet->multisig())
- {
- for (tools::wallet2::pending_tx &ptx: ptx_vector)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.multisig_txset.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- for (auto & ptx : ptx_vector)
- {
- res.tx_hash_list.push_back(epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx)));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob));
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata_list.push_back(epee::string_tools::buff_to_hex_nodelimer(oss.str()));
- }
- }
- }
-
- return true;
+ return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, req.do_not_relay,
+ res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
catch (const std::exception& e)
{
@@ -1112,63 +966,12 @@ namespace tools
return false;
}
- if (req.get_tx_key)
- {
- res.tx_key = epee::string_tools::pod_to_hex(ptx.tx_key);
- }
-
- if (m_wallet->multisig())
- {
- res.multisig_txset = epee::string_tools::buff_to_hex_nodelimer(m_wallet->save_multisig_tx(ptx_vector));
- if (res.multisig_txset.empty())
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- }
- else
- {
- if (!req.do_not_relay)
- m_wallet->commit_tx(ptx_vector);
-
- // populate response with tx hashes
- res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx.tx));
- if (req.get_tx_hex)
- {
- cryptonote::blobdata blob;
- tx_to_blob(ptx.tx, blob);
- res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob);
- }
- if (req.get_tx_metadata)
- {
- std::ostringstream oss;
- boost::archive::portable_binary_oarchive ar(oss);
- try
- {
- ar << ptx;
- }
- catch (...)
- {
- er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
- er.message = "Failed to save multisig tx set after creation";
- return false;
- }
- res.tx_metadata = epee::string_tools::buff_to_hex_nodelimer(oss.str());
- }
- }
- return true;
- }
- catch (const tools::error::daemon_busy& e)
- {
- er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
- er.message = e.what();
- return false;
+ return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.fee, res.multisig_txset, req.do_not_relay,
+ res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, er);
}
catch (const std::exception& e)
{
- er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
- er.message = e.what();
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
return false;
}
catch (...)
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index b20198b78..88f2a85a4 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -207,6 +207,11 @@ namespace tools
bool not_open(epee::json_rpc::error& er);
void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code);
+ template<typename Ts, typename Tu>
+ bool fill_response(std::vector<tools::wallet2::pending_tx> &ptx_vector,
+ bool get_tx_key, Ts& tx_key, Tu &amount, Tu &fee, std::string &multisig_txset, bool do_not_relay,
+ Ts &tx_hash, bool get_tx_hex, Ts &tx_blob, bool get_tx_metadata, Ts &tx_metadata, epee::json_rpc::error &er);
+
wallet2 *m_wallet;
std::string m_wallet_dir;
tools::private_file rpc_login_file;
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 76c02039b..d797af5c1 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -411,6 +411,7 @@ namespace wallet_rpc
std::string tx_hash;
std::string tx_key;
std::list<std::string> amount_keys;
+ uint64_t amount;
uint64_t fee;
std::string tx_blob;
std::string tx_metadata;
@@ -420,6 +421,7 @@ namespace wallet_rpc
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
KV_SERIALIZE(amount_keys)
+ KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
@@ -520,14 +522,16 @@ namespace wallet_rpc
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
+ std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
- std::list<std::string> multisig_txset;
+ std::string multisig_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
+ KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
@@ -582,14 +586,16 @@ namespace wallet_rpc
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
+ std::list<uint64_t> amount_list;
std::list<uint64_t> fee_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
- std::list<std::string> multisig_txset;
+ std::string multisig_txset;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
+ KV_SERIALIZE(amount_list)
KV_SERIALIZE(fee_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
@@ -631,6 +637,7 @@ namespace wallet_rpc
{
std::string tx_hash;
std::string tx_key;
+ uint64_t amount;
uint64_t fee;
std::string tx_blob;
std::string tx_metadata;
@@ -639,6 +646,7 @@ namespace wallet_rpc
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_key)
+ KV_SERIALIZE(amount)
KV_SERIALIZE(fee)
KV_SERIALIZE(tx_blob)
KV_SERIALIZE(tx_metadata)
diff --git a/tests/fuzz/cold-outputs.cpp b/tests/fuzz/cold-outputs.cpp
index 2785db05d..6e813d823 100644
--- a/tests/fuzz/cold-outputs.cpp
+++ b/tests/fuzz/cold-outputs.cpp
@@ -53,16 +53,8 @@ int ColdOutputsFuzzer::init()
try
{
- boost::filesystem::remove("/tmp/cold-outputs-test.keys");
- boost::filesystem::remove("/tmp/cold-outputs-test.address.txt");
- boost::filesystem::remove("/tmp/cold-outputs-test");
-
wallet.init("");
- wallet.generate("/tmp/cold-outputs-test", "", spendkey, true, false);
-
- boost::filesystem::remove("/tmp/cold-outputs-test.keys");
- boost::filesystem::remove("/tmp/cold-outputs-test.address.txt");
- boost::filesystem::remove("/tmp/cold-outputs-test");
+ wallet.generate("", "", spendkey, true, false);
}
catch (const std::exception &e)
{
diff --git a/tests/fuzz/cold-transaction.cpp b/tests/fuzz/cold-transaction.cpp
index f0b4b26d6..20715c9ed 100644
--- a/tests/fuzz/cold-transaction.cpp
+++ b/tests/fuzz/cold-transaction.cpp
@@ -54,16 +54,8 @@ int ColdTransactionFuzzer::init()
try
{
- boost::filesystem::remove("/tmp/cold-transaction-test.keys");
- boost::filesystem::remove("/tmp/cold-transaction-test.address.txt");
- boost::filesystem::remove("/tmp/cold-transaction-test");
-
wallet.init("");
- wallet.generate("/tmp/cold-transaction-test", "", spendkey, true, false);
-
- boost::filesystem::remove("/tmp/cold-transaction-test.keys");
- boost::filesystem::remove("/tmp/cold-transaction-test.address.txt");
- boost::filesystem::remove("/tmp/cold-transaction-test");
+ wallet.generate("", "", spendkey, true, false);
}
catch (const std::exception &e)
{
diff --git a/tests/fuzz/signature.cpp b/tests/fuzz/signature.cpp
index 7ec4434e6..e7a0a53df 100644
--- a/tests/fuzz/signature.cpp
+++ b/tests/fuzz/signature.cpp
@@ -54,16 +54,8 @@ int SignatureFuzzer::init()
try
{
- boost::filesystem::remove("/tmp/signature-test.keys");
- boost::filesystem::remove("/tmp/signature-test.address.txt");
- boost::filesystem::remove("/tmp/signature-test");
-
wallet.init("");
- wallet.generate("/tmp/signature-test", "", spendkey, true, false);
-
- boost::filesystem::remove("/tmp/signature-test.keys");
- boost::filesystem::remove("/tmp/signature-test.address.txt");
- boost::filesystem::remove("/tmp/signature-test");
+ wallet.generate("", "", spendkey, true, false);
cryptonote::address_parse_info info;
if (!cryptonote::get_account_address_from_str_or_url(info, true, "9uVsvEryzpN8WH2t1WWhFFCG5tS8cBNdmJYNRuckLENFimfauV5pZKeS1P2CbxGkSDTUPHXWwiYE5ZGSXDAGbaZgDxobqDN"))
diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt
index 2b3a0d6f8..1a0677925 100644
--- a/tests/performance_tests/CMakeLists.txt
+++ b/tests/performance_tests/CMakeLists.txt
@@ -41,6 +41,7 @@ set(performance_tests_headers
generate_key_image_helper.h
generate_keypair.h
is_out_to_acc.h
+ subaddress_expand.h
multi_tx_test_base.h
performance_tests.h
performance_utils.h
@@ -51,6 +52,7 @@ add_executable(performance_tests
${performance_tests_headers})
target_link_libraries(performance_tests
PRIVATE
+ wallet
cryptonote_core
common
cncrypto
diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp
index 459eecba4..2f3ce289d 100644
--- a/tests/performance_tests/main.cpp
+++ b/tests/performance_tests/main.cpp
@@ -44,6 +44,7 @@
#include "generate_key_image_helper.h"
#include "generate_keypair.h"
#include "is_out_to_acc.h"
+#include "subaddress_expand.h"
#include "sc_reduce32.h"
#include "cn_fast_hash.h"
@@ -112,6 +113,8 @@ int main(int argc, char** argv)
TEST_PERFORMANCE0(test_generate_keypair);
TEST_PERFORMANCE0(test_sc_reduce32);
+ TEST_PERFORMANCE2(test_wallet2_expand_subaddresses, 50, 200);
+
TEST_PERFORMANCE0(test_cn_slow_hash);
TEST_PERFORMANCE1(test_cn_fast_hash, 32);
TEST_PERFORMANCE1(test_cn_fast_hash, 16384);
diff --git a/tests/performance_tests/subaddress_expand.h b/tests/performance_tests/subaddress_expand.h
new file mode 100644
index 000000000..fea92d54b
--- /dev/null
+++ b/tests/performance_tests/subaddress_expand.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#pragma once
+
+#include "wallet/wallet2.h"
+#include "ringct/rctOps.h"
+
+#include "single_tx_test_base.h"
+
+template<size_t Major, size_t Minor>
+class test_wallet2_expand_subaddresses : public single_tx_test_base
+{
+public:
+ static const size_t loop_count = 1;
+ static const size_t major = Major;
+ static const size_t minor = Minor;
+
+ bool init()
+ {
+ if (!single_tx_test_base::init())
+ return false;
+ wallet.set_subaddress_lookahead(1, 1);
+ crypto::secret_key spendkey = rct::rct2sk(rct::skGen());
+ wallet.generate("", "", spendkey, true, false);
+ wallet.set_subaddress_lookahead(major, minor);
+ return true;
+ }
+
+ bool test()
+ {
+ wallet.expand_subaddresses({0, 0});
+ return true;
+ }
+
+private:
+ tools::wallet2 wallet;
+};
diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp
index d9a7b8ad5..3a8e787ec 100644
--- a/tests/unit_tests/crypto.cpp
+++ b/tests/unit_tests/crypto.cpp
@@ -47,14 +47,6 @@ namespace
"8b655970153799af2aeadc9ff1add0ea6c7251d54154cfa92c173a0dd39c1f94"
"6c7251d54154cfa92c173a0dd39c1f948b655970153799af2aeadc9ff1add0ea";
- static std::uint8_t md[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00
- };
-
template<typename T>
bool is_formatted()
{
@@ -69,22 +61,6 @@ namespace
out << "BEGIN" << value << "END";
return out.str() == "BEGIN<" + std::string{expected, sizeof(T) * 2} + ">END";
}
-
- bool keccak_harness()
- {
- size_t inlen = sizeof(source);
- int mdlen = (int)sizeof(md);
- keccak(source, inlen, md, mdlen);
-
- if (md[0] != 0x00)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
}
TEST(Crypto, Ostream)
diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp
index c235f49fd..0a472a421 100644
--- a/tests/unit_tests/hardfork.cpp
+++ b/tests/unit_tests/hardfork.cpp
@@ -118,7 +118,7 @@ public:
virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; }
virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; }
virtual void remove_txpool_tx(const crypto::hash& txid) {}
- virtual txpool_tx_meta_t get_txpool_tx_meta(const crypto::hash& txid) const { return txpool_tx_meta_t(); }
+ virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const { return false; }
virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; }
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; }
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; }
diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp
index 5e427f064..0e8f9f747 100644
--- a/tests/unit_tests/http.cpp
+++ b/tests/unit_tests/http.cpp
@@ -60,12 +60,18 @@
#include "md5_l.h"
#include "string_tools.h"
+#include "crypto/crypto.h"
namespace {
namespace http = epee::net_utils::http;
using fields = std::unordered_map<std::string, std::string>;
using auth_responses = std::vector<fields>;
+void rng(size_t len, uint8_t *ptr)
+{
+ crypto::rand(len, ptr);
+}
+
std::string quoted(std::string str)
{
str.insert(str.begin(), '"');
@@ -250,13 +256,13 @@ std::string get_nc(std::uint32_t count)
TEST(HTTP_Server_Auth, NotRequired)
{
- http::http_server_auth auth{};
+ http::http_server_auth auth{}; // no rng here
EXPECT_FALSE(auth.get_response(http::http_request_info{}));
}
TEST(HTTP_Server_Auth, MissingAuth)
{
- http::http_server_auth auth{{"foo", "bar"}};
+ http::http_server_auth auth{{"foo", "bar"}, rng};
EXPECT_TRUE(bool(auth.get_response(http::http_request_info{})));
{
http::http_request_info request{};
@@ -267,7 +273,7 @@ TEST(HTTP_Server_Auth, MissingAuth)
TEST(HTTP_Server_Auth, BadSyntax)
{
- http::http_server_auth auth{{"foo", "bar"}};
+ http::http_server_auth auth{{"foo", "bar"}, rng};
EXPECT_TRUE(bool(auth.get_response(make_request({{u8"algorithm", "fo\xFF"}}))));
EXPECT_TRUE(bool(auth.get_response(make_request({{u8"cnonce", "\"000\xFF\""}}))));
EXPECT_TRUE(bool(auth.get_response(make_request({{u8"cnonce \xFF =", "\"000\xFF\""}}))));
@@ -277,7 +283,7 @@ TEST(HTTP_Server_Auth, BadSyntax)
TEST(HTTP_Server_Auth, MD5)
{
http::login user{"foo", "bar"};
- http::http_server_auth auth{user};
+ http::http_server_auth auth{user, rng};
const auto response = auth.get_response(make_request(fields{}));
ASSERT_TRUE(bool(response));
@@ -326,7 +332,7 @@ TEST(HTTP_Server_Auth, MD5_sess)
constexpr const char cnonce[] = "not a good cnonce";
http::login user{"foo", "bar"};
- http::http_server_auth auth{user};
+ http::http_server_auth auth{user, rng};
const auto response = auth.get_response(make_request(fields{}));
ASSERT_TRUE(bool(response));
@@ -378,7 +384,7 @@ TEST(HTTP_Server_Auth, MD5_auth)
constexpr const char qop[] = "auth";
http::login user{"foo", "bar"};
- http::http_server_auth auth{user};
+ http::http_server_auth auth{user, rng};
const auto response = auth.get_response(make_request(fields{}));
ASSERT_TRUE(bool(response));
@@ -446,7 +452,7 @@ TEST(HTTP_Server_Auth, MD5_sess_auth)
constexpr const char qop[] = "auth";
http::login user{"foo", "bar"};
- http::http_server_auth auth{user};
+ http::http_server_auth auth{user, rng};
const auto response = auth.get_response(make_request(fields{}));
ASSERT_TRUE(bool(response));
@@ -523,7 +529,7 @@ TEST(HTTP_Auth, DogFood)
const http::login user{"some_user", "ultimate password"};
- http::http_server_auth server{user};
+ http::http_server_auth server{user, rng};
http::http_client_auth client{user};
http::http_request_info request{};
diff --git a/tests/unit_tests/memwipe.cpp b/tests/unit_tests/memwipe.cpp
index 2d8980ef7..59f50cef8 100644
--- a/tests/unit_tests/memwipe.cpp
+++ b/tests/unit_tests/memwipe.cpp
@@ -30,7 +30,7 @@
#include <stdint.h>
#include "misc_log_ex.h"
-#include "common/memwipe.h"
+#include "memwipe.h"
// Probably won't catch the optimized out case, but at least we test
// it works in the normal case
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp
index 2ef1097da..8a75ac435 100644
--- a/tests/unit_tests/serialization.cpp
+++ b/tests/unit_tests/serialization.cpp
@@ -38,7 +38,6 @@
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "ringct/rctSigs.h"
-#include "serialization/serialization.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
#include "serialization/debug_archive.h"
diff --git a/tests/unit_tests/subaddress.cpp b/tests/unit_tests/subaddress.cpp
index c304b7347..ca950b25d 100644
--- a/tests/unit_tests/subaddress.cpp
+++ b/tests/unit_tests/subaddress.cpp
@@ -44,7 +44,7 @@ class WalletSubaddress : public ::testing::Test
{
try
{
- w1.generate(wallet_name, password, recovery_key, true, false);
+ w1.generate("", password, recovery_key, true, false);
}
catch (const std::exception& e)
{
@@ -58,24 +58,9 @@ class WalletSubaddress : public ::testing::Test
virtual void TearDown()
{
- boost::filesystem::wpath wallet_file(wallet_name);
- boost::filesystem::wpath wallet_address_file(wallet_name + ".address.txt");
- boost::filesystem::wpath wallet_keys_file(wallet_name + ".keys");
-
- if ( boost::filesystem::exists(wallet_file) )
- boost::filesystem::remove(wallet_file);
-
- if ( boost::filesystem::exists(wallet_address_file) )
- boost::filesystem::remove(wallet_address_file);
-
- if ( boost::filesystem::exists(wallet_keys_file) )
- boost::filesystem::remove(wallet_keys_file);
}
tools::wallet2 w1;
- std::string path_working_dir = ".";
- std::string path_test_wallet = "test_wallet";
- const std::string wallet_name = path_working_dir + "/" + path_test_wallet;
const std::string password = "testpass";
crypto::secret_key recovery_key = crypto::secret_key();
const std::string test_label = "subaddress test label";
diff --git a/tests/unit_tests/varint.cpp b/tests/unit_tests/varint.cpp
index 2b31cdfdf..577ad4d26 100644
--- a/tests/unit_tests/varint.cpp
+++ b/tests/unit_tests/varint.cpp
@@ -36,7 +36,6 @@
#include <boost/foreach.hpp>
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
-#include "serialization/serialization.h"
#include "serialization/binary_archive.h"
#include "serialization/json_archive.h"
#include "serialization/debug_archive.h"
diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt
new file mode 100644
index 000000000..36b72d68a
--- /dev/null
+++ b/translations/CMakeLists.txt
@@ -0,0 +1,54 @@
+# Copyright (c) 2017, The Monero Project
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are
+# permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of
+# conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list
+# of conditions and the following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be
+# used to endorse or promote products derived from this software without specific
+# prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+cmake_minimum_required(VERSION 2.8.7)
+
+add_executable(generate_translations_header generate_translations_header.c)
+
+find_program(LRELEASE lrelease)
+if(LRELEASE STREQUAL "LRELEASE-NOTFOUND")
+ set(ts_files "")
+ message(WARNING "lrelease program not found, translation files not built")
+else()
+ file(GLOB ts_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" *.ts)
+ foreach(ts_file ${ts_files})
+ string(REPLACE ".ts" ".qm" qm_file "${ts_file}")
+ add_custom_command(TARGET generate_translations_header
+ PRE_BUILD
+ COMMAND ${LRELEASE} "${CMAKE_CURRENT_SOURCE_DIR}/${ts_file}" -qm "${qm_file}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}")
+ endforeach()
+endif()
+
+string(REPLACE ".ts" ".qm" qm_files "${ts_files}")
+
+add_custom_command(TARGET generate_translations_header
+ POST_BUILD
+ COMMAND generate_translations_header ${qm_files}
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BIN_DIR}"
+ COMMENT "Generating embedded translations header")
diff --git a/translations/generate_translations_header.c b/translations/generate_translations_header.c
new file mode 100644
index 000000000..69671913e
--- /dev/null
+++ b/translations/generate_translations_header.c
@@ -0,0 +1,86 @@
+// Copyright (c) 2013, Sergey Lyubka
+// Copyright (c) 2017, The Monero Project
+// All rights reserved.
+// Released under the MIT license.
+
+// This program takes a list of files as an input, and produces C++ code that
+// contains the contents of all these files as a collection of strings.
+//
+// Usage:
+// 1. Compile this file:
+// cc -o generate-translations-header generate-translations-header.c
+//
+// 2. Convert list of files into single header:
+// ./generate-translations-header monero_fr.qm monero_it.qm > translations_files.h
+//
+// 3. In your application code, include translations_files.h, then you can
+// access the files using this function:
+// static bool find_embedded_file(const std::string &file_name, std::string &data);
+// std::string data;
+// find_embedded_file("monero_fr.qm", data);
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static const char *code =
+ "static bool find_embedded_file(const std::string &name, std::string &data) {\n"
+ " const struct embedded_file *p;\n"
+ " for (p = embedded_files; p->name != NULL; p++) {\n"
+ " if (*p->name == name) {\n"
+ " data = *p->data;\n"
+ " return true;\n"
+ " }\n"
+ " }\n"
+ " return false;\n"
+ "}\n";
+
+int main(int argc, char *argv[]) {
+ FILE *fp, *foutput;
+ int i, j, ch;
+
+ if((foutput = fopen("translation_files.h", "w")) == NULL) {
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(foutput, "#ifndef TRANSLATION_FILES_H\n");
+ fprintf(foutput, "#define TRANSLATION_FILES_H\n\n");
+ fprintf(foutput, "#include <string>\n\n");
+
+ for (i = 1; i < argc; i++) {
+ if ((fp = fopen(argv[i], "rb")) == NULL) {
+ exit(EXIT_FAILURE);
+ } else {
+ fprintf(foutput, "static const std::string translation_file_name_%d = \"%s\";\n", i, argv[i]);
+ fprintf(foutput, "static const std::string translation_file_data_%d = std::string(", i);
+ for (j = 0; (ch = fgetc(fp)) != EOF; j++) {
+ if ((j % 16) == 0) {
+ if (j > 0) {
+ fprintf(foutput, "%s", "\"");
+ }
+ fprintf(foutput, "%s", "\n \"");
+ }
+ fprintf(foutput, "\\x%02x", ch);
+ }
+ fprintf(foutput, "\",\n %d);\n\n", j);
+ fclose(fp);
+ }
+ }
+
+ fprintf(foutput, "%s", "static const struct embedded_file {\n");
+ fprintf(foutput, "%s", " const std::string *name;\n");
+ fprintf(foutput, "%s", " const std::string *data;\n");
+ fprintf(foutput, "%s", "} embedded_files[] = {\n");
+
+ for (i = 1; i < argc; i++) {
+ fprintf(foutput, " {&translation_file_name_%d, &translation_file_data_%d},\n", i, i);
+ }
+ fprintf(foutput, "%s", " {NULL, NULL}\n");
+ fprintf(foutput, "%s", "};\n\n");
+ fprintf(foutput, "%s\n", code);
+
+ fprintf(foutput, "#endif /* TRANSLATION_FILES_H */\n");
+
+ fclose(foutput);
+
+ return EXIT_SUCCESS;
+}
diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile
index 37d012202..20b846aa1 100644
--- a/utils/build_scripts/android32.Dockerfile
+++ b/utils/build_scripts/android32.Dockerfile
@@ -80,7 +80,7 @@ RUN git clone https://github.com/zeromq/zeromq4-1.git \
&& CC=clang CXX=clang++ ./configure --host=arm-none-linux-gnueabi \
&& make
-RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a /opt/android/toolchain-arm/arm-linux-androideabi/lib/armv7-a
+RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a ${TOOLCHAIN_DIR}/arm-linux-androideabi/lib/armv7-a
RUN git clone https://github.com/monero-project/monero.git \
&& cd monero \
diff --git a/utils/build_scripts/android64.Dockerfile b/utils/build_scripts/android64.Dockerfile
index 70c3c2b41..83bcbad89 100644
--- a/utils/build_scripts/android64.Dockerfile
+++ b/utils/build_scripts/android64.Dockerfile
@@ -79,7 +79,7 @@ RUN git clone https://github.com/zeromq/zeromq4-1.git \
&& CC=clang CXX=clang++ ./configure --host=aarch64-linux-android \
&& make
-RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a /opt/android/toolchain-arm/aarch64-linux-android/lib
+RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a ${TOOLCHAIN_DIR}/aarch64-linux-android/lib
RUN git clone https://github.com/monero-project/monero.git \
&& cd monero \