aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt27
-rw-r--r--src/blockchain_db/CMakeLists.txt3
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp4
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h2
-rw-r--r--src/blockchain_db/blockchain_db.cpp55
-rw-r--r--src/blockchain_db/blockchain_db.h60
-rw-r--r--src/blockchain_db/db_types.h2
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp167
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h20
-rw-r--r--src/blockchain_utilities/CMakeLists.txt3
-rw-r--r--src/blockchain_utilities/README.md2
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp18
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp40
-rw-r--r--src/blockchain_utilities/blockchain_utilities.h2
-rw-r--r--src/blockchain_utilities/blocksdat_file.cpp2
-rw-r--r--src/blockchain_utilities/blocksdat_file.h6
-rw-r--r--src/blockchain_utilities/bootstrap_file.cpp4
-rw-r--r--src/blockchain_utilities/bootstrap_file.h4
-rw-r--r--src/blockchain_utilities/bootstrap_serialization.h6
-rw-r--r--src/blockchain_utilities/cn_deserialize.cpp6
-rw-r--r--src/blockchain_utilities/fake_core.h2
-rw-r--r--src/blocks/CMakeLists.txt6
-rw-r--r--src/blocks/blockexports.c9
-rw-r--r--src/common/CMakeLists.txt17
-rw-r--r--src/common/base58.cpp2
-rw-r--r--src/common/base58.h2
-rw-r--r--src/common/boost_serialization_helper.h2
-rw-r--r--src/common/command_line.cpp8
-rw-r--r--src/common/command_line.h6
-rw-r--r--src/common/common_fwd.h41
-rw-r--r--src/common/dns_utils.cpp117
-rw-r--r--src/common/dns_utils.h8
-rw-r--r--src/common/download.cpp270
-rw-r--r--src/common/download.h44
-rw-r--r--src/common/http_connection.h2
-rw-r--r--src/common/i18n.cpp2
-rw-r--r--src/common/i18n.h2
-rw-r--r--src/common/int-util.h2
-rw-r--r--src/common/password.cpp (renamed from src/wallet/password_container.cpp)27
-rw-r--r--src/common/password.h (renamed from src/wallet/password_container.h)31
-rw-r--r--src/common/pod-class.h2
-rw-r--r--src/common/rpc_client.h8
-rw-r--r--src/common/scoped_message_writer.h2
-rw-r--r--src/common/stack_trace.cpp2
-rw-r--r--src/common/task_region.cpp2
-rw-r--r--src/common/task_region.h2
-rw-r--r--src/common/thread_group.cpp2
-rw-r--r--src/common/thread_group.h2
-rw-r--r--src/common/unordered_containers_boost_serialization.h2
-rw-r--r--src/common/updates.cpp115
-rw-r--r--src/common/updates.h37
-rw-r--r--src/common/util.cpp66
-rw-r--r--src/common/util.h6
-rw-r--r--src/common/varint.h2
-rw-r--r--src/crypto/CMakeLists.txt4
-rw-r--r--src/crypto/blake256.c2
-rw-r--r--src/crypto/blake256.h2
-rw-r--r--src/crypto/chacha8.h2
-rw-r--r--src/crypto/crypto-ops-data.c2
-rw-r--r--src/crypto/crypto-ops.c2
-rw-r--r--src/crypto/crypto-ops.h2
-rw-r--r--src/crypto/crypto.cpp2
-rw-r--r--src/crypto/crypto.h2
-rw-r--r--src/crypto/crypto_ops_builder/README.md2
-rw-r--r--src/crypto/crypto_ops_builder/crypto-ops-data.c2
-rw-r--r--src/crypto/crypto_ops_builder/crypto-ops-old.c2
-rw-r--r--src/crypto/crypto_ops_builder/crypto-ops.h2
-rw-r--r--src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py2
-rw-r--r--src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h2
-rw-r--r--src/crypto/generic-ops.h2
-rw-r--r--src/crypto/groestl.h2
-rw-r--r--src/crypto/groestl_tables.h2
-rw-r--r--src/crypto/hash-extra-blake.c2
-rw-r--r--src/crypto/hash-extra-groestl.c2
-rw-r--r--src/crypto/hash-extra-jh.c2
-rw-r--r--src/crypto/hash-extra-skein.c2
-rw-r--r--src/crypto/hash-ops.h2
-rw-r--r--src/crypto/hash.c2
-rw-r--r--src/crypto/hash.h2
-rw-r--r--src/crypto/initializer.h2
-rw-r--r--src/crypto/random.c2
-rw-r--r--src/crypto/random.h2
-rw-r--r--src/crypto/skein_port.h2
-rw-r--r--src/crypto/slow-hash.c60
-rw-r--r--src/crypto/tree-hash.c2
-rw-r--r--src/cryptonote_basic/CMakeLists.txt73
-rw-r--r--src/cryptonote_basic/account.cpp (renamed from src/cryptonote_core/account.cpp)6
-rw-r--r--src/cryptonote_basic/account.h (renamed from src/cryptonote_core/account.h)4
-rw-r--r--src/cryptonote_basic/account_boost_serialization.h (renamed from src/cryptonote_core/account_boost_serialization.h)4
-rw-r--r--src/cryptonote_basic/checkpoints.cpp (renamed from src/cryptonote_core/checkpoints.cpp)111
-rw-r--r--src/cryptonote_basic/checkpoints.h (renamed from src/cryptonote_core/checkpoints.h)2
-rw-r--r--src/cryptonote_basic/connection_context.h (renamed from src/cryptonote_core/connection_context.h)2
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h (renamed from src/cryptonote_core/cryptonote_basic.h)75
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.cpp (renamed from src/cryptonote_core/cryptonote_basic_impl.cpp)19
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.h (renamed from src/cryptonote_core/cryptonote_basic_impl.h)44
-rw-r--r--src/cryptonote_basic/cryptonote_boost_serialization.h (renamed from src/cryptonote_core/cryptonote_boost_serialization.h)2
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp (renamed from src/cryptonote_core/cryptonote_format_utils.cpp)603
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h (renamed from src/cryptonote_core/cryptonote_format_utils.h)74
-rw-r--r--src/cryptonote_basic/cryptonote_stat_info.h (renamed from src/cryptonote_core/cryptonote_stat_info.h)2
-rw-r--r--src/cryptonote_basic/difficulty.cpp (renamed from src/cryptonote_core/difficulty.cpp)2
-rw-r--r--src/cryptonote_basic/difficulty.h (renamed from src/cryptonote_core/difficulty.h)2
-rw-r--r--src/cryptonote_basic/hardfork.cpp (renamed from src/cryptonote_core/hardfork.cpp)15
-rw-r--r--src/cryptonote_basic/hardfork.h (renamed from src/cryptonote_core/hardfork.h)4
-rw-r--r--src/cryptonote_basic/miner.cpp853
-rw-r--r--src/cryptonote_basic/miner.h (renamed from src/cryptonote_core/miner.h)61
-rw-r--r--src/cryptonote_basic/tx_extra.h (renamed from src/cryptonote_core/tx_extra.h)2
-rw-r--r--src/cryptonote_basic/verification_context.h (renamed from src/cryptonote_core/verification_context.h)2
-rw-r--r--src/cryptonote_config.h8
-rw-r--r--src/cryptonote_core/CMakeLists.txt25
-rw-r--r--src/cryptonote_core/blockchain.cpp206
-rw-r--r--src/cryptonote_core/blockchain.h30
-rw-r--r--src/cryptonote_core/blockchain_storage_boost_serialization.h2
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp275
-rw-r--r--src/cryptonote_core/cryptonote_core.h94
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp499
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h101
-rw-r--r--src/cryptonote_core/miner.cpp414
-rw-r--r--src/cryptonote_core/tx_pool.cpp97
-rw-r--r--src/cryptonote_core/tx_pool.h16
-rw-r--r--src/cryptonote_protocol/CMakeLists.txt4
-rw-r--r--src/cryptonote_protocol/blobdatatype.h2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h14
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp4
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h9
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl182
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler_common.h4
-rw-r--r--src/daemon/CMakeLists.txt6
-rw-r--r--src/daemon/command_line_args.h2
-rw-r--r--src/daemon/command_parser_executor.cpp58
-rw-r--r--src/daemon/command_parser_executor.h13
-rw-r--r--src/daemon/command_server.cpp23
-rw-r--r--src/daemon/command_server.h6
-rw-r--r--src/daemon/core.h3
-rw-r--r--src/daemon/daemon.cpp12
-rw-r--r--src/daemon/daemon.h2
-rw-r--r--src/daemon/executor.cpp9
-rw-r--r--src/daemon/executor.h6
-rw-r--r--src/daemon/main.cpp29
-rw-r--r--src/daemon/p2p.h2
-rw-r--r--src/daemon/protocol.h3
-rw-r--r--src/daemon/rpc.h2
-rw-r--r--src/daemon/rpc_command_executor.cpp234
-rw-r--r--src/daemon/rpc_command_executor.h15
-rw-r--r--src/daemonizer/CMakeLists.txt2
-rw-r--r--src/daemonizer/daemonizer.h2
-rw-r--r--src/daemonizer/posix_daemonizer.inl11
-rw-r--r--src/daemonizer/posix_fork.h2
-rw-r--r--src/daemonizer/windows_daemonizer.inl2
-rw-r--r--src/daemonizer/windows_service.cpp2
-rw-r--r--src/daemonizer/windows_service.h2
-rw-r--r--src/daemonizer/windows_service_runner.h2
-rw-r--r--src/mnemonics/CMakeLists.txt7
-rw-r--r--src/mnemonics/chinese_simplified.h1709
-rw-r--r--src/mnemonics/dutch.h1686
-rw-r--r--src/mnemonics/electrum-words.cpp25
-rw-r--r--src/mnemonics/electrum-words.h2
-rw-r--r--src/mnemonics/english.h13
-rw-r--r--src/mnemonics/french.h1686
-rw-r--r--src/mnemonics/german.h13
-rw-r--r--src/mnemonics/italian.h13
-rw-r--r--src/mnemonics/japanese.h49
-rw-r--r--src/mnemonics/language_base.h63
-rw-r--r--src/mnemonics/old_english.h15
-rw-r--r--src/mnemonics/portuguese.h48
-rw-r--r--src/mnemonics/russian.h13
-rw-r--r--src/mnemonics/singleton.h2
-rw-r--r--src/mnemonics/spanish.h54
-rw-r--r--src/p2p/CMakeLists.txt3
-rw-r--r--src/p2p/connection_basic.cpp2
-rw-r--r--src/p2p/connection_basic.hpp2
-rw-r--r--src/p2p/net_node.h8
-rw-r--r--src/p2p/net_node.inl102
-rw-r--r--src/p2p/net_node_common.h2
-rw-r--r--src/p2p/net_peerlist.h9
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h2
-rw-r--r--src/p2p/network_throttle-detail.cpp6
-rw-r--r--src/p2p/network_throttle-detail.hpp2
-rw-r--r--src/p2p/network_throttle.cpp2
-rw-r--r--src/p2p/network_throttle.hpp2
-rw-r--r--src/p2p/p2p_protocol_defs.h2
-rw-r--r--src/p2p/stdafx.h2
-rw-r--r--src/platform/mingw/alloca.h2
-rw-r--r--src/platform/msc/alloca.h2
-rw-r--r--src/platform/msc/inline_c.h2
-rw-r--r--src/platform/msc/stdbool.h2
-rw-r--r--src/platform/msc/sys/param.h2
-rw-r--r--src/ringct/CMakeLists.txt1
-rw-r--r--src/ringct/rctCryptoOps.c2
-rw-r--r--src/ringct/rctCryptoOps.h2
-rw-r--r--src/ringct/rctOps.cpp24
-rw-r--r--src/ringct/rctOps.h4
-rw-r--r--src/ringct/rctSigs.cpp8
-rw-r--r--src/ringct/rctTypes.h7
-rw-r--r--src/rpc/CMakeLists.txt10
-rw-r--r--src/rpc/core_rpc_server.cpp266
-rw-r--r--src/rpc/core_rpc_server.h16
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h94
-rw-r--r--src/rpc/core_rpc_server_error_codes.h2
-rw-r--r--src/rpc/rpc_args.cpp96
-rw-r--r--src/rpc/rpc_args.h67
-rw-r--r--src/serialization/binary_archive.h2
-rw-r--r--src/serialization/binary_utils.h2
-rw-r--r--src/serialization/crypto.h2
-rw-r--r--src/serialization/debug_archive.h2
-rw-r--r--src/serialization/json_archive.h2
-rw-r--r--src/serialization/json_utils.h2
-rw-r--r--src/serialization/serialization.h2
-rw-r--r--src/serialization/string.h2
-rw-r--r--src/serialization/variant.h2
-rw-r--r--src/serialization/vector.h2
-rw-r--r--src/simplewallet/CMakeLists.txt2
-rw-r--r--src/simplewallet/simplewallet.cpp458
-rw-r--r--src/simplewallet/simplewallet.h22
-rw-r--r--src/version.cmake2
-rw-r--r--src/version.h.in2
-rw-r--r--src/wallet/CMakeLists.txt14
-rw-r--r--src/wallet/api/address_book.cpp4
-rw-r--r--src/wallet/api/address_book.h2
-rw-r--r--src/wallet/api/pending_transaction.cpp9
-rw-r--r--src/wallet/api/pending_transaction.h2
-rw-r--r--src/wallet/api/transaction_history.cpp2
-rw-r--r--src/wallet/api/transaction_history.h2
-rw-r--r--src/wallet/api/transaction_info.cpp2
-rw-r--r--src/wallet/api/transaction_info.h2
-rw-r--r--src/wallet/api/unsigned_transaction.cpp19
-rw-r--r--src/wallet/api/unsigned_transaction.h2
-rw-r--r--src/wallet/api/utils.cpp2
-rw-r--r--src/wallet/api/wallet.cpp55
-rw-r--r--src/wallet/api/wallet.h8
-rw-r--r--src/wallet/api/wallet_manager.cpp63
-rw-r--r--src/wallet/api/wallet_manager.h5
-rw-r--r--src/wallet/node_rpc_proxy.cpp43
-rw-r--r--src/wallet/node_rpc_proxy.h4
-rw-r--r--src/wallet/wallet2.cpp605
-rw-r--r--src/wallet/wallet2.h83
-rw-r--r--src/wallet/wallet2_api.h20
-rw-r--r--src/wallet/wallet_args.cpp54
-rw-r--r--src/wallet/wallet_args.h4
-rw-r--r--src/wallet/wallet_errors.h4
-rw-r--r--src/wallet/wallet_rpc_server.cpp618
-rw-r--r--src/wallet/wallet_rpc_server.h28
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h122
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h3
243 files changed, 11499 insertions, 2744 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fc7d60e4c..d83242a3c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -99,19 +99,26 @@ endfunction ()
add_subdirectory(common)
add_subdirectory(crypto)
add_subdirectory(ringct)
+add_subdirectory(cryptonote_basic)
add_subdirectory(cryptonote_core)
-add_subdirectory(blockchain_db)
+if(NOT IOS)
+ add_subdirectory(blockchain_db)
+endif()
add_subdirectory(mnemonics)
-add_subdirectory(rpc)
+if(NOT IOS)
+ add_subdirectory(rpc)
+endif()
add_subdirectory(wallet)
-add_subdirectory(p2p)
+if(NOT IOS)
+ add_subdirectory(p2p)
+endif()
add_subdirectory(cryptonote_protocol)
-
-add_subdirectory(simplewallet)
-add_subdirectory(daemonizer)
-add_subdirectory(daemon)
-
-add_subdirectory(blockchain_utilities)
+if(NOT IOS)
+ add_subdirectory(simplewallet)
+ add_subdirectory(daemonizer)
+ add_subdirectory(daemon)
+ add_subdirectory(blockchain_utilities)
+endif()
if(PER_BLOCK_CHECKPOINT)
add_subdirectory(blocks)
diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt
index cefe93ebe..c27aa73b0 100644
--- a/src/blockchain_db/CMakeLists.txt
+++ b/src/blockchain_db/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -63,6 +63,7 @@ target_link_libraries(blockchain_db
PUBLIC
common
crypto
+ ringct
${LMDB_LIBRARY}
${BDB_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index 57d8371bd..c954a7751 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
@@ -31,7 +31,7 @@
#include <memory> // std::unique_ptr
#include <cstring> // memcpy
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "crypto/crypto.h"
#include "profile_tools.h"
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index 266e780c6..a040a70ef 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index bd1a38ec3..136d4fa80 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -29,7 +29,7 @@
#include <boost/range/adaptor/reversed.hpp>
#include "blockchain_db.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "profile_tools.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -130,12 +130,6 @@ uint64_t BlockchainDB::add_block( const block& blk
uint64_t prev_height = height();
- // call out to subclass implementation to add the block & metadata
- time1 = epee::misc_utils::get_tick_count();
- add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash);
- TIME_MEASURE_FINISH(time1);
- time_add_block1 += time1;
-
// call out to add the transactions
time1 = epee::misc_utils::get_tick_count();
@@ -151,6 +145,12 @@ uint64_t BlockchainDB::add_block( const block& blk
TIME_MEASURE_FINISH(time1);
time_add_transaction += time1;
+ // call out to subclass implementation to add the block & metadata
+ time1 = epee::misc_utils::get_tick_count();
+ add_block(blk, block_size, cumulative_difficulty, coins_generated, blk_hash);
+ TIME_MEASURE_FINISH(time1);
+ time_add_block1 += time1;
+
m_hardfork->add(blk, prev_height);
block_txn_stop();
@@ -200,6 +200,45 @@ void BlockchainDB::remove_transaction(const crypto::hash& tx_hash)
remove_transaction_data(tx_hash, tx);
}
+block BlockchainDB::get_block_from_height(const uint64_t& height) const
+{
+ blobdata bd = get_block_blob_from_height(height);
+ block b;
+ if (!parse_and_validate_block_from_blob(bd, b))
+ throw new DB_ERROR("Failed to parse block from blob retrieved from the db");
+
+ return b;
+}
+
+block BlockchainDB::get_block(const crypto::hash& h) const
+{
+ blobdata bd = get_block_blob(h);
+ block b;
+ if (!parse_and_validate_block_from_blob(bd, b))
+ throw new DB_ERROR("Failed to parse block from blob retrieved from the db");
+
+ return b;
+}
+
+bool BlockchainDB::get_tx(const crypto::hash& h, cryptonote::transaction &tx) const
+{
+ blobdata bd;
+ if (!get_tx_blob(h, bd))
+ return false;
+ if (!parse_and_validate_tx_from_blob(bd, tx))
+ throw new DB_ERROR("Failed to parse transaction from blob retrieved from the db");
+
+ return true;
+}
+
+transaction BlockchainDB::get_tx(const crypto::hash& h) const
+{
+ transaction tx;
+ if (!get_tx(h, tx))
+ throw new TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str());
+ return tx;
+}
+
void BlockchainDB::reset_stats()
{
num_calls = 0;
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 455e0c811..f5710550b 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -34,9 +34,10 @@
#include <string>
#include <exception>
#include "crypto/hash.h"
-#include "cryptonote_core/cryptonote_basic.h"
-#include "cryptonote_core/difficulty.h"
-#include "cryptonote_core/hardfork.h"
+#include "cryptonote_protocol/blobdatatype.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "cryptonote_basic/difficulty.h"
+#include "cryptonote_basic/hardfork.h"
/** \file
* Cryptonote Blockchain Database Interface
@@ -754,7 +755,20 @@ public:
*
* @return the block requested
*/
- virtual block get_block(const crypto::hash& h) const = 0;
+ virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const = 0;
+
+ /**
+ * @brief fetches the block with the given hash
+ *
+ * Returns the requested block.
+ *
+ * If the block does not exist, the subclass should throw BLOCK_DNE
+ *
+ * @param h the hash to look for
+ *
+ * @return the block requested
+ */
+ virtual block get_block(const crypto::hash& h) const;
/**
* @brief gets the height of the block with a given hash
@@ -784,7 +798,7 @@ public:
virtual block_header get_block_header(const crypto::hash& h) const = 0;
/**
- * @brief fetch a block by height
+ * @brief fetch a block blob by height
*
* The subclass should return the block at the given height.
*
@@ -793,9 +807,21 @@ public:
*
* @param height the height to look for
*
+ * @return the block blob
+ */
+ virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const = 0;
+
+ /**
+ * @brief fetch a block by height
+ *
+ * If the block does not exist, that is to say if the blockchain is not
+ * that high, then the subclass should throw BLOCK_DNE
+ *
+ * @param height the height to look for
+ *
* @return the block
*/
- virtual block get_block_from_height(const uint64_t& height) const = 0;
+ virtual block get_block_from_height(const uint64_t& height) const;
/**
* @brief fetch a block's timestamp
@@ -1009,20 +1035,28 @@ public:
/**
* @brief fetches the transaction with the given hash
*
- * The subclass should return the transaction stored which has the given
- * hash.
- *
* If the transaction does not exist, the subclass should throw TX_DNE.
*
* @param h the hash to look for
*
* @return the transaction with the given hash
*/
- virtual transaction get_tx(const crypto::hash& h) const = 0;
+ virtual transaction get_tx(const crypto::hash& h) const;
/**
* @brief fetches the transaction with the given hash
*
+ * If the transaction does not exist, the subclass should return false.
+ *
+ * @param h the hash to look for
+ *
+ * @return true iff the transaction was found
+ */
+ virtual bool get_tx(const crypto::hash& h, transaction &tx) const;
+
+ /**
+ * @brief fetches the transaction blob with the given hash
+ *
* The subclass should return the transaction stored which has the given
* hash.
*
@@ -1032,7 +1066,7 @@ public:
*
* @return true iff the transaction was found
*/
- virtual bool get_tx(const crypto::hash& h, transaction &tx) const = 0;
+ virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const = 0;
/**
* @brief fetches the total number of transactions ever
@@ -1184,7 +1218,7 @@ public:
* @param offsets a list of amount-specific output indices
* @param outputs return-by-reference a list of outputs' metadata
*/
- virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs) = 0;
+ virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false) = 0;
/*
* FIXME: Need to check with git blame and ask what this does to
diff --git a/src/blockchain_db/db_types.h b/src/blockchain_db/db_types.h
index 67afe0405..6c21b029e 100644
--- a/src/blockchain_db/db_types.h
+++ b/src/blockchain_db/db_types.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 1cfcf3a9e..dab09e8f4 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
@@ -34,7 +34,7 @@
#include <cstring> // memcpy
#include <random>
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "crypto/crypto.h"
#include "profile_tools.h"
@@ -377,7 +377,50 @@ void mdb_txn_safe::allow_new_txns()
creation_gate.clear();
}
+void lmdb_resized(MDB_env *env)
+{
+ mdb_txn_safe::prevent_new_txns();
+
+ MGINFO("LMDB map resize detected.");
+
+ MDB_envinfo mei;
+
+ mdb_env_info(env, &mei);
+ uint64_t old = mei.me_mapsize;
+
+ mdb_txn_safe::wait_no_active_txns();
+
+ int result = mdb_env_set_mapsize(env, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to set new mapsize: ", result).c_str()));
+
+ mdb_env_info(env, &mei);
+ uint64_t new_mapsize = mei.me_mapsize;
+
+ MGINFO("LMDB Mapsize increased." << " Old: " << old / (1024 * 1024) << "MiB" << ", New: " << new_mapsize / (1024 * 1024) << "MiB");
+
+ mdb_txn_safe::allow_new_txns();
+}
+inline int lmdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
+{
+ int res = mdb_txn_begin(env, parent, flags, txn);
+ if (res == MDB_MAP_RESIZED) {
+ lmdb_resized(env);
+ res = mdb_txn_begin(env, parent, flags, txn);
+ }
+ return res;
+}
+
+inline int lmdb_txn_renew(MDB_txn *txn)
+{
+ int res = mdb_txn_renew(txn);
+ if (res == MDB_MAP_RESIZED) {
+ lmdb_resized(mdb_txn_env(txn));
+ res = mdb_txn_renew(txn);
+ }
+ return res;
+}
void BlockchainLMDB::do_resize(uint64_t increase_size)
{
@@ -561,7 +604,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con
{
LOG_PRINT_L1("No existing blocks to check for average block size");
}
- else if (m_cum_count)
+ else if (m_cum_count >= num_prev_blocks)
{
avg_block_size = m_cum_size / m_cum_count;
LOG_PRINT_L1("average block size across recent " << m_cum_count << " blocks: " << avg_block_size);
@@ -570,6 +613,9 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con
}
else
{
+ MDB_txn *rtxn;
+ mdb_txn_cursors *rcurs;
+ block_rtxn_start(&rtxn, &rcurs);
for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num)
{
uint32_t block_size = get_block_size(block_num);
@@ -578,6 +624,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks) con
// some blocks were to be skipped for being outliers.
++num_blocks_used;
}
+ block_rtxn_stop();
avg_block_size = total_block_size / num_blocks_used;
LOG_PRINT_L1("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size);
}
@@ -628,6 +675,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
CURSOR(blocks)
CURSOR(block_info)
+ // this call to mdb_cursor_put will change height()
MDB_val_copy<blobdata> blob(block_to_blob(blk));
result = mdb_cursor_put(m_cur_blocks, &key, &blob, MDB_APPEND);
if (result)
@@ -701,7 +749,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
uint64_t m_height = height();
int result;
- uint64_t tx_id = m_num_txs;
+ uint64_t tx_id = get_tx_count();
CURSOR(txs)
CURSOR(tx_indices)
@@ -734,7 +782,6 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add tx blob to db transaction: ", result).c_str()));
- m_num_txs++;
return tx_id;
}
@@ -782,8 +829,6 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
// Don't delete the tx_indices entry until the end, after we're done with val_tx_id
if (mdb_cursor_del(m_cur_tx_indices, 0))
throw1(DB_ERROR("Failed to add removal of tx index to db transaction"));
-
- m_num_txs--;
}
uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
@@ -796,6 +841,7 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
check_open();
mdb_txn_cursors *m_cursors = &m_wcursors;
uint64_t m_height = height();
+ uint64_t m_num_outputs = num_outputs();
int result = 0;
@@ -848,7 +894,6 @@ uint64_t BlockchainLMDB::add_output(const crypto::hash& tx_hash,
if ((result = mdb_cursor_put(m_cur_output_amounts, &val_amount, &data, MDB_APPENDDUP)))
throw0(DB_ERROR(lmdb_error("Failed to add output pubkey to db transaction: ", result).c_str()));
- m_num_outputs++;
return ok.amount_index;
}
@@ -933,8 +978,6 @@ void BlockchainLMDB::remove_output(const uint64_t amount, const uint64_t& out_in
result = mdb_cursor_del(m_cur_output_amounts, 0);
if (result)
throw0(DB_ERROR(lmdb_error(std::string("Error deleting amount for output index ").append(boost::lexical_cast<std::string>(out_index).append(": ")).c_str(), result).c_str()));
-
- m_num_outputs--;
}
void BlockchainLMDB::add_spent_key(const crypto::key_image& k_image)
@@ -999,7 +1042,7 @@ tx_out BlockchainLMDB::output_from_blob(const blobdata& blob) const
void BlockchainLMDB::check_open() const
{
- LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+// LOG_PRINT_L3("BlockchainLMDB::" << __func__);
if (!m_open)
throw0(DB_ERROR("DB operation attempted on a not-open DB instance"));
}
@@ -1154,16 +1197,6 @@ void BlockchainLMDB::open(const std::string& filename, const int mdb_flags)
LOG_PRINT_L2("Setting m_height to: " << db_stats.ms_entries);
uint64_t m_height = db_stats.ms_entries;
- // get and keep current number of txs
- if ((result = mdb_stat(txn, m_txs, &db_stats)))
- throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str()));
- m_num_txs = db_stats.ms_entries;
-
- // get and keep current number of outputs
- if ((result = mdb_stat(txn, m_output_txs, &db_stats)))
- throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str()));
- m_num_outputs = db_stats.ms_entries;
-
bool compatible = true;
MDB_val_copy<const char*> k("version");
@@ -1269,7 +1302,7 @@ void BlockchainLMDB::reset()
check_open();
mdb_txn_safe txn;
- if (auto result = mdb_txn_begin(m_env, NULL, 0, txn))
+ if (auto result = lmdb_txn_begin(m_env, NULL, 0, txn))
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
if (auto result = mdb_drop(txn, m_blocks, 0))
@@ -1303,7 +1336,6 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to write version to database: ", result).c_str()));
txn.commit();
- m_num_outputs = 0;
m_cum_size = 0;
m_cum_count = 0;
}
@@ -1353,7 +1385,7 @@ void BlockchainLMDB::unlock()
txn_ptr = m_write_txn; \
else \
{ \
- if (auto mdb_res = mdb_txn_begin(m_env, NULL, flags, auto_txn)) \
+ if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \
throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \
} \
@@ -1387,7 +1419,7 @@ void BlockchainLMDB::unlock()
txn_ptr = m_write_txn; \
else \
{ \
- if (auto mdb_res = mdb_txn_begin(m_env, NULL, flags, auto_txn)) \
+ if (auto mdb_res = lmdb_txn_begin(m_env, NULL, flags, auto_txn)) \
throw0(DB_ERROR(lmdb_error(std::string("Failed to create a transaction for the db in ")+__FUNCTION__+": ", mdb_res).c_str())); \
} \
@@ -1428,12 +1460,12 @@ bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const
return ret;
}
-block BlockchainLMDB::get_block(const crypto::hash& h) const
+cryptonote::blobdata BlockchainLMDB::get_block_blob(const crypto::hash& h) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
- return get_block_from_height(get_block_height(h));
+ return get_block_blob_from_height(get_block_height(h));
}
uint64_t BlockchainLMDB::get_block_height(const crypto::hash& h) const
@@ -1466,7 +1498,7 @@ block_header BlockchainLMDB::get_block_header(const crypto::hash& h) const
return get_block(h);
}
-block BlockchainLMDB::get_block_from_height(const uint64_t& height) const
+cryptonote::blobdata BlockchainLMDB::get_block_blob_from_height(const uint64_t& height) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1487,13 +1519,9 @@ block BlockchainLMDB::get_block_from_height(const uint64_t& height) const
blobdata bd;
bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
- block b;
- if (!parse_and_validate_block_from_blob(bd, b))
- throw0(DB_ERROR("Failed to parse block from blob retrieved from the db"));
-
TXN_POSTFIX_RDONLY();
- return b;
+ return bd;
}
uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const
@@ -1713,6 +1741,20 @@ uint64_t BlockchainLMDB::height() const
return db_stats.ms_entries;
}
+uint64_t BlockchainLMDB::num_outputs() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ TXN_PREFIX_RDONLY();
+ int result;
+
+ // get current height
+ MDB_stat db_stats;
+ if ((result = mdb_stat(m_txn, m_output_txs, &db_stats)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_output_txs: ", result).c_str()));
+ return db_stats.ms_entries;
+}
+
bool BlockchainLMDB::tx_exists(const crypto::hash& h) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -1808,7 +1850,7 @@ uint64_t BlockchainLMDB::get_tx_unlock_time(const crypto::hash& h) const
return ret;
}
-bool BlockchainLMDB::get_tx(const crypto::hash& h, transaction &tx) const
+bool BlockchainLMDB::get_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1831,36 +1873,24 @@ bool BlockchainLMDB::get_tx(const crypto::hash& h, transaction &tx) const
else if (get_result)
throw0(DB_ERROR(lmdb_error("DB error attempting to fetch tx from hash", get_result).c_str()));
- blobdata bd;
bd.assign(reinterpret_cast<char*>(result.mv_data), result.mv_size);
- if (!parse_and_validate_tx_from_blob(bd, tx))
- throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
-
TXN_POSTFIX_RDONLY();
return true;
}
-transaction BlockchainLMDB::get_tx(const crypto::hash& h) const
-{
- transaction tx;
-
- if (!get_tx(h, tx))
- throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()));
- return tx;
-}
-
uint64_t BlockchainLMDB::get_tx_count() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
TXN_PREFIX_RDONLY();
+ int result;
MDB_stat db_stats;
- if (mdb_stat(m_txn, m_tx_indices, &db_stats))
- throw0(DB_ERROR("Failed to query m_tx_indices"));
+ if ((result = mdb_stat(m_txn, m_txs, &db_stats)))
+ throw0(DB_ERROR(lmdb_error("Failed to query m_txs: ", result).c_str()));
TXN_POSTFIX_RDONLY();
@@ -2278,7 +2308,7 @@ bool BlockchainLMDB::batch_start(uint64_t batch_num_blocks)
m_write_batch_txn = new mdb_txn_safe();
// NOTE: need to make sure it's destroyed properly when done
- if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, *m_write_batch_txn))
+ if (auto mdb_res = lmdb_txn_begin(m_env, NULL, 0, *m_write_batch_txn))
{
delete m_write_batch_txn;
m_write_batch_txn = nullptr;
@@ -2397,12 +2427,12 @@ bool BlockchainLMDB::block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) co
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 = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
+ 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()));
ret = true;
} else if (!m_tinfo->m_ti_rflags.m_rf_txn)
{
- if (auto mdb_res = mdb_txn_renew(m_tinfo->m_ti_rtxn))
+ 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()));
ret = true;
}
@@ -2435,12 +2465,12 @@ void BlockchainLMDB::block_txn_start(bool readonly)
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 = mdb_txn_begin(m_env, NULL, MDB_RDONLY, &m_tinfo->m_ti_rtxn))
+ 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 = mdb_txn_renew(m_tinfo->m_ti_rtxn))
+ 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;
}
@@ -2466,14 +2496,15 @@ void BlockchainLMDB::block_txn_start(bool readonly)
{
m_writer = boost::this_thread::get_id();
m_write_txn = new mdb_txn_safe();
- if (auto mdb_res = mdb_txn_begin(m_env, NULL, 0, *m_write_txn))
+ if (auto mdb_res = lmdb_txn_begin(m_env, NULL, 0, *m_write_txn))
{
delete m_write_txn;
m_write_txn = nullptr;
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));
- }
+ } 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()));
}
void BlockchainLMDB::block_txn_stop()
@@ -2544,8 +2575,6 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c
}
}
- uint64_t num_txs = m_num_txs;
- uint64_t num_outputs = m_num_outputs;
try
{
BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs);
@@ -2556,8 +2585,6 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c
}
catch (...)
{
- m_num_txs = num_txs;
- m_num_outputs = num_outputs;
block_txn_abort();
throw;
}
@@ -2572,8 +2599,6 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs)
block_txn_start(false);
- uint64_t num_txs = m_num_txs;
- uint64_t num_outputs = m_num_outputs;
try
{
BlockchainDB::pop_block(blk, txs);
@@ -2581,8 +2606,6 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs)
}
catch (...)
{
- m_num_txs = num_txs;
- m_num_outputs = num_outputs;
block_txn_abort();
throw;
}
@@ -2616,7 +2639,7 @@ void BlockchainLMDB::get_output_tx_and_index_from_global(const std::vector<uint6
TXN_POSTFIX_RDONLY();
}
-void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs)
+void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
TIME_MEASURE_START(db3);
@@ -2634,7 +2657,14 @@ void BlockchainLMDB::get_output_key(const uint64_t &amount, const std::vector<ui
auto get_result = mdb_cursor_get(m_cur_output_amounts, &k, &v, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
+ {
+ if (allow_partial)
+ {
+ MDEBUG("Partial result: " << outputs.size() << "/" << offsets.size());
+ break;
+ }
throw1(OUTPUT_DNE((std::string("Attempting to get output pubkey by global index (amount ") + boost::lexical_cast<std::string>(amount) + ", index " + boost::lexical_cast<std::string>(index) + ", count " + boost::lexical_cast<std::string>(get_num_outputs(amount)) + "), but key does not exist").c_str()));
+ }
else if (get_result)
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve an output pubkey from the db", get_result).c_str()));
@@ -3256,8 +3286,6 @@ void BlockchainLMDB::migrate_0_1()
lmdb_db_open(txn, LMDB_OUTPUT_AMOUNTS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_output_amounts, "Failed to open db handle for m_output_amounts");
mdb_set_dupsort(txn, m_output_amounts, compare_uint64);
txn.commit();
- m_num_txs = 0;
- m_num_outputs = 0;
} while(0);
do {
@@ -3328,12 +3356,9 @@ void BlockchainLMDB::migrate_0_1()
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs: ", result).c_str()));
if (!i) {
MDB_stat ms;
- mdb_stat(txn, m_output_txs, &ms);
- m_num_outputs = ms.ms_entries;
mdb_stat(txn, m_txs, &ms);
- m_num_txs = i = ms.ms_entries;
+ i = ms.ms_entries;
if (i) {
- m_num_txs = i;
MDB_val_set(pk, "txblk");
result = mdb_cursor_get(c_props, &pk, &k, MDB_SET);
if (result)
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index e7faf8cdc..8a5677566 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
@@ -170,13 +170,13 @@ public:
virtual bool block_exists(const crypto::hash& h, uint64_t *height = NULL) const;
- virtual block get_block(const crypto::hash& h) const;
-
virtual uint64_t get_block_height(const crypto::hash& h) const;
virtual block_header get_block_header(const crypto::hash& h) const;
- virtual block get_block_from_height(const uint64_t& height) const;
+ virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const;
+
+ virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const;
virtual uint64_t get_block_timestamp(const uint64_t& height) const;
@@ -207,9 +207,7 @@ public:
virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const;
- virtual transaction get_tx(const crypto::hash& h) const;
-
- virtual bool get_tx(const crypto::hash& h, transaction &tx) const;
+ virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual uint64_t get_tx_count() const;
@@ -221,7 +219,7 @@ public:
virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index);
virtual output_data_t get_output_key(const uint64_t& global_index) const;
- virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs);
+ virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs, bool allow_partial = false);
virtual tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const;
virtual void get_output_tx_and_index_from_global(const std::vector<uint64_t> &global_indices,
@@ -312,6 +310,8 @@ private:
virtual void remove_spent_key(const crypto::key_image& k_image);
+ uint64_t num_outputs() const;
+
// Hard fork
virtual void set_hard_fork_version(uint64_t height, uint8_t version);
virtual uint8_t get_hard_fork_version(uint64_t height) const;
@@ -369,10 +369,8 @@ private:
MDB_dbi m_properties;
- uint64_t m_num_txs;
- uint64_t m_num_outputs;
mutable uint64_t m_cum_size; // used in batch size estimation
- mutable int m_cum_count;
+ mutable unsigned int m_cum_count;
std::string m_folder;
mdb_txn_safe* m_write_txn; // may point to either a short-lived txn or a batch txn
mdb_txn_safe* m_write_batch_txn; // persist batch txn outside of BlockchainLMDB
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index b26fe04cc..bb23cdc8b 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -119,6 +119,7 @@ monero_add_executable(cn_deserialize
target_link_libraries(cn_deserialize
LINK_PRIVATE
cryptonote_core
+ blockchain_db
p2p
epee
${CMAKE_THREAD_LIBS_INIT})
diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md
index 809df3bfa..d18dcba8c 100644
--- a/src/blockchain_utilities/README.md
+++ b/src/blockchain_utilities/README.md
@@ -1,6 +1,6 @@
# Monero Blockchain Utilities
-Copyright (c) 2014-2016, The Monero Project
+Copyright (c) 2014-2017, The Monero Project
## Introduction
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index b4411fbba..f145bc107 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -57,6 +57,10 @@ std::string join_set_strings(const std::unordered_set<std::string>& db_types_all
int main(int argc, char* argv[])
{
+ TRY_ENTRY();
+
+ epee::string_tools::set_module_name_and_folder(argv[0]);
+
std::string default_db_type = "lmdb";
std::unordered_set<std::string> db_types_all = cryptonote::blockchain_db_types;
@@ -78,7 +82,7 @@ int main(int argc, char* argv[])
po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options");
const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true};
- const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level};
+ const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
const command_line::arg_descriptor<bool> arg_testnet_on = {
"testnet"
@@ -122,10 +126,13 @@ int main(int argc, char* argv[])
return 1;
}
- log_level = command_line::get_arg(vm, arg_log_level);
+ mlog_configure(mlog_get_default_log_path("monero-blockchain-export.log"), true);
+ if (!vm["log-level"].defaulted())
+ mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
+ else
+ mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
block_stop = command_line::get_arg(vm, arg_block_stop);
- mlog_configure("monero-blockchain-export", true);
LOG_PRINT_L0("Starting...");
bool opt_testnet = command_line::get_arg(vm, arg_testnet_on);
@@ -223,4 +230,7 @@ int main(int argc, char* argv[])
}
CHECK_AND_ASSERT_MES(r, false, "Failed to export blockchain raw data");
LOG_PRINT_L0("Blockchain raw data exported OK");
+ return 0;
+
+ CATCH_ENTRY("Export error", 1);
}
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index f21673b89..b2c217ca1 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -34,7 +34,7 @@
#include <boost/filesystem.hpp>
#include "bootstrap_file.h"
#include "bootstrap_serialization.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "serialization/binary_utils.h" // dump_binary(), parse_binary()
#include "serialization/json_utils.h" // dump_json()
#include "include_base_utils.h"
@@ -374,7 +374,7 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path,
return 2;
}
bytes_read += chunk_size;
- MINFO("Total bytes read: " << bytes_read);
+ MDEBUG("Total bytes read: " << bytes_read);
if (h + NUM_BLOCKS_PER_CHUNK < start_height + 1)
{
@@ -596,6 +596,10 @@ int import_from_file(FakeCore& simple_core, const std::string& import_file_path,
int main(int argc, char* argv[])
{
+ TRY_ENTRY();
+
+ epee::string_tools::set_module_name_and_folder(argv[0]);
+
std::string default_db_type = "lmdb";
std::string default_db_engine_compiled = "blockchain_db";
@@ -620,7 +624,7 @@ int main(int argc, char* argv[])
po::options_description desc_cmd_only("Command line options");
po::options_description desc_cmd_sett("Command line options and settings options");
const command_line::arg_descriptor<std::string> arg_input_file = {"input-file", "Specify input file", "", true};
- const command_line::arg_descriptor<uint32_t> arg_log_level = {"log-level", "", log_level};
+ const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
const command_line::arg_descriptor<uint64_t> arg_block_stop = {"block-stop", "Stop at block number", block_stop};
const command_line::arg_descriptor<uint64_t> arg_batch_size = {"batch-size", "", db_batch_size};
const command_line::arg_descriptor<uint64_t> arg_pop_blocks = {"pop-blocks", "Remove blocks from end of blockchain", num_blocks};
@@ -680,7 +684,6 @@ int main(int argc, char* argv[])
if (! r)
return 1;
- log_level = command_line::get_arg(vm, arg_log_level);
opt_verify = command_line::get_arg(vm, arg_verify);
opt_batch = command_line::get_arg(vm, arg_batch);
opt_resume = command_line::get_arg(vm, arg_resume);
@@ -722,7 +725,12 @@ int main(int argc, char* argv[])
m_config_folder = command_line::get_arg(vm, data_dir_arg);
db_arg_str = command_line::get_arg(vm, arg_database);
- mlog_configure("monero-blockchain-import", true);
+ mlog_configure(mlog_get_default_log_path("monero-blockchain-import.log"), true);
+ if (!vm["log-level"].defaulted())
+ mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
+ else
+ mlog_set_log(std::string(std::to_string(log_level) + ",bcutil:INFO").c_str());
+
MINFO("Starting...");
boost::filesystem::path fs_import_file_path;
@@ -759,7 +767,11 @@ int main(int argc, char* argv[])
return 1;
}
- if ((db_type == "lmdb") || (db_type == "berkeley"))
+ if ((db_type == "lmdb")
+#if defined(BERKELEY_DB)
+ || (db_type == "berkeley")
+#endif
+ )
{
db_engine_compiled = "blockchain_db";
}
@@ -796,13 +808,11 @@ int main(int argc, char* argv[])
// properties to do so. Both ways work, but fake core isn't necessary in that
// circumstance.
- // for multi_db_runtime:
- if (db_type == "lmdb" || db_type == "berkeley")
- {
- fake_core_db simple_core(m_config_folder, opt_testnet, opt_batch, db_type, db_flags);
- import_from_file(simple_core, import_file_path, block_stop);
- }
- else
+ if (db_type != "lmdb"
+#if defined(BERKELEY_DB)
+ && db_type != "berkeley"
+#endif
+ )
{
std::cerr << "database type unrecognized" << ENDL;
return 1;
@@ -847,4 +857,6 @@ int main(int argc, char* argv[])
// calls delete on its BlockchainDB derived class' object, which closes its
// files.
return 0;
+
+ CATCH_ENTRY("Import error", 1);
}
diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h
index 6ddda65ec..af934bf29 100644
--- a/src/blockchain_utilities/blockchain_utilities.h
+++ b/src/blockchain_utilities/blockchain_utilities.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp
index 7ccb9a145..63224225f 100644
--- a/src/blockchain_utilities/blocksdat_file.cpp
+++ b/src/blockchain_utilities/blocksdat_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h
index ed821cd6d..4e9d8173b 100644
--- a/src/blockchain_utilities/blocksdat_file.h
+++ b/src/blockchain_utilities/blocksdat_file.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -34,8 +34,8 @@
#include <boost/iostreams/filtering_streambuf.hpp>
-#include "cryptonote_core/cryptonote_basic.h"
-#include "cryptonote_core/cryptonote_boost_serialization.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
diff --git a/src/blockchain_utilities/bootstrap_file.cpp b/src/blockchain_utilities/bootstrap_file.cpp
index add2c65e6..d5bb37d93 100644
--- a/src/blockchain_utilities/bootstrap_file.cpp
+++ b/src/blockchain_utilities/bootstrap_file.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -458,7 +458,7 @@ uint64_t BootstrapFile::count_blocks(const std::string& import_file_path)
bytes_read += chunk_size;
// std::cout << refresh_string;
- MINFO("Number bytes scanned: " << bytes_read);
+ MDEBUG("Number bytes scanned: " << bytes_read);
}
import_file.close();
diff --git a/src/blockchain_utilities/bootstrap_file.h b/src/blockchain_utilities/bootstrap_file.h
index 0bbe28f17..1a646b54b 100644
--- a/src/blockchain_utilities/bootstrap_file.h
+++ b/src/blockchain_utilities/bootstrap_file.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -34,7 +34,7 @@
#include <boost/iostreams/filtering_streambuf.hpp>
-#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_core/blockchain.h"
#include <algorithm>
diff --git a/src/blockchain_utilities/bootstrap_serialization.h b/src/blockchain_utilities/bootstrap_serialization.h
index a7d88907f..f9f50f00b 100644
--- a/src/blockchain_utilities/bootstrap_serialization.h
+++ b/src/blockchain_utilities/bootstrap_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -28,8 +28,8 @@
#pragma once
-#include "cryptonote_core/cryptonote_boost_serialization.h"
-#include "cryptonote_core/difficulty.h"
+#include "cryptonote_basic/cryptonote_boost_serialization.h"
+#include "cryptonote_basic/difficulty.h"
namespace cryptonote
diff --git a/src/blockchain_utilities/cn_deserialize.cpp b/src/blockchain_utilities/cn_deserialize.cpp
index ae8e38435..b178e4e03 100644
--- a/src/blockchain_utilities/cn_deserialize.cpp
+++ b/src/blockchain_utilities/cn_deserialize.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -26,8 +26,8 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "cryptonote_core/cryptonote_basic.h"
-#include "cryptonote_core/tx_extra.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_utilities.h"
#include "common/command_line.h"
diff --git a/src/blockchain_utilities/fake_core.h b/src/blockchain_utilities/fake_core.h
index 814139602..70144184b 100644
--- a/src/blockchain_utilities/fake_core.h
+++ b/src/blockchain_utilities/fake_core.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/blocks/CMakeLists.txt b/src/blocks/CMakeLists.txt
index f65503fbc..3a866af5b 100644
--- a/src/blocks/CMakeLists.txt
+++ b/src/blocks/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -30,8 +30,8 @@ if(APPLE)
add_library(blocks STATIC blockexports.c)
set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
else()
- add_custom_command(OUTPUT blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
- add_custom_command(OUTPUT testnet_blocks.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
+ add_custom_command(OUTPUT blocks.o MAIN_DEPENDENCY blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocks.o blocks.dat)
+ add_custom_command(OUTPUT testnet_blocks.o MAIN_DEPENDENCY testnet_blocks.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/testnet_blocks.o testnet_blocks.dat)
add_library(blocks STATIC blocks.o testnet_blocks.o blockexports.c)
set_target_properties(blocks PROPERTIES LINKER_LANGUAGE C)
endif()
diff --git a/src/blocks/blockexports.c b/src/blocks/blockexports.c
index 26adaad62..477167a12 100644
--- a/src/blocks/blockexports.c
+++ b/src/blocks/blockexports.c
@@ -2,12 +2,19 @@
#if defined(__APPLE__)
#include <mach-o/getsect.h>
-
+#ifdef BUILD_SHARED_LIBS
+#if !defined(__LP64__)
+const struct mach_header _mh_execute_header;
+#else
+const struct mach_header_64 _mh_execute_header;
+#endif
+#else
#if !defined(__LP64__)
extern const struct mach_header _mh_execute_header;
#else
extern const struct mach_header_64 _mh_execute_header;
#endif
+#endif
const unsigned char *get_blocks_dat_start(int testnet)
{
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 1864c7835..0a3e8cd19 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -26,15 +26,20 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
+
set(common_sources
base58.cpp
command_line.cpp
dns_utils.cpp
+ download.cpp
util.cpp
i18n.cpp
+ password.cpp
perf_timer.cpp
task_region.cpp
- thread_group.cpp)
+ thread_group.cpp
+ updates.cpp)
if (STACK_TRACE)
list(APPEND common_sources stack_trace.cpp)
@@ -46,7 +51,9 @@ set(common_private_headers
base58.h
boost_serialization_helper.h
command_line.h
+ common_fwd.h
dns_utils.h
+ download.h
http_connection.h
int-util.h
pod-class.h
@@ -56,10 +63,12 @@ set(common_private_headers
util.h
varint.h
i18n.h
+ password.h
perf_timer.h
stack_trace.h
task_region.h
- thread_group.h)
+ thread_group.h
+ updates.h)
monero_private_headers(common
${common_private_headers})
@@ -69,6 +78,7 @@ monero_add_library(common
${common_private_headers})
target_link_libraries(common
PUBLIC
+ epee
crypto
${UNBOUND_LIBRARY}
${LIBUNWIND_LIBRARIES}
@@ -78,6 +88,7 @@ target_link_libraries(common
${Boost_THREAD_LIBRARY}
${Boost_REGEX_LIBRARY}
PRIVATE
+ ${OPENSSL_LIBRARIES}
${EXTRA_LIBRARIES})
#monero_install_headers(common
diff --git a/src/common/base58.cpp b/src/common/base58.cpp
index 355d1e209..64cb7c0de 100644
--- a/src/common/base58.cpp
+++ b/src/common/base58.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/base58.h b/src/common/base58.h
index df2ac2e9b..6dd850c03 100644
--- a/src/common/base58.h
+++ b/src/common/base58.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h
index 88dccbde7..4a503d830 100644
--- a/src/common/boost_serialization_helper.h
+++ b/src/common/boost_serialization_helper.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp
index d95859256..f71b3e576 100644
--- a/src/common/command_line.cpp
+++ b/src/common/command_line.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -76,7 +76,6 @@ namespace command_line
const arg_descriptor<bool> arg_version = {"version", "Output version information"};
const arg_descriptor<std::string> arg_data_dir = {"data-dir", "Specify data directory"};
const arg_descriptor<std::string> arg_testnet_data_dir = {"testnet-data-dir", "Specify testnet data directory"};
- const arg_descriptor<std::string> arg_user_agent = {"user-agent", "Restrict RPC use to clients using this user agent"};
const arg_descriptor<bool> arg_test_drop_download = {"test-drop-download", "For net tests: in download, discard ALL blocks instead checking/saving them (very fast)"};
const arg_descriptor<uint64_t> arg_test_drop_download_height = {"test-drop-download-height", "Like test-drop-download but disards only after around certain height", 0};
const arg_descriptor<int> arg_test_dbg_lock_sleep = {"test-dbg-lock-sleep", "Sleep time in ms, defaults to 0 (off), used to debug before/after locking mutex. Values 100 to 1000 are good for tests."};
@@ -121,4 +120,9 @@ namespace command_line
, "How many blocks to sync at once during chain synchronization."
, BLOCKS_SYNCHRONIZING_DEFAULT_COUNT
};
+ const command_line::arg_descriptor<std::string> arg_check_updates = {
+ "check-updates"
+ , "Check for new versions of monero: [disabled|notify|download|update]"
+ , "notify"
+ };
}
diff --git a/src/common/command_line.h b/src/common/command_line.h
index 3f0919e99..2110b8849 100644
--- a/src/common/command_line.h
+++ b/src/common/command_line.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -168,7 +168,7 @@ namespace command_line
{
return parser();
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
std::cerr << "Failed to parse arguments: " << e.what() << std::endl;
std::cerr << desc << std::endl;
@@ -207,7 +207,6 @@ namespace command_line
extern const arg_descriptor<bool> arg_version;
extern const arg_descriptor<std::string> arg_data_dir;
extern const arg_descriptor<std::string> arg_testnet_data_dir;
- extern const arg_descriptor<std::string> arg_user_agent;
extern const arg_descriptor<bool> arg_test_drop_download;
extern const arg_descriptor<uint64_t> arg_test_drop_download_height;
extern const arg_descriptor<int> arg_test_dbg_lock_sleep;
@@ -219,4 +218,5 @@ namespace command_line
extern const arg_descriptor<uint64_t> arg_prep_blocks_threads;
extern const arg_descriptor<uint64_t> arg_show_time_stats;
extern const arg_descriptor<size_t> arg_block_sync_size;
+ extern const arg_descriptor<std::string> arg_check_updates;
}
diff --git a/src/common/common_fwd.h b/src/common/common_fwd.h
new file mode 100644
index 000000000..5d67251b1
--- /dev/null
+++ b/src/common/common_fwd.h
@@ -0,0 +1,41 @@
+// 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
+
+namespace tools
+{
+ class DNSResolver;
+ struct login;
+ class password_container;
+ class t_http_connection;
+ class task_region;
+ class thread_group;
+}
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index 83259bc70..ab38cbbae 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -29,7 +29,7 @@
#include "common/command_line.h"
#include "common/i18n.h"
#include "common/dns_utils.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include <cstring>
#include <sstream>
// check local first (in the event of static or in-source compilation of libunbound)
@@ -37,6 +37,7 @@
#include <stdlib.h>
#include "include_base_utils.h"
+#include <random>
#include <boost/filesystem/fstream.hpp>
using namespace epee;
namespace bf = boost::filesystem;
@@ -404,21 +405,23 @@ std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec
return addresses;
}
-std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid)
+std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm)
{
// attempt to get address from dns query
auto addresses = addresses_from_url(url, dnssec_valid);
if (addresses.empty())
{
- std::cout << tr("wrong address: ") << url;
+ LOG_ERROR("wrong address: " << url);
return {};
}
// for now, move on only if one address found
if (addresses.size() > 1)
{
- std::cout << tr("not yet supported: Multiple Monero addresses found for given URL: ") << url;
+ LOG_ERROR("not yet supported: Multiple Monero addresses found for given URL: " << url);
return {};
}
+ if (!cli_confirm)
+ return addresses[0];
// prompt user for confirmation.
// inform user of DNSSEC validation status as well.
std::string dnssec_str;
@@ -451,6 +454,110 @@ std::string get_account_address_as_str_from_url(const std::string& url, bool& dn
return addresses[0];
}
+namespace
+{
+ bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b)
+ {
+ if (a.size() != b.size()) return false;
+
+ for (const auto& record_in_a : a)
+ {
+ bool ok = false;
+ for (const auto& record_in_b : b)
+ {
+ if (record_in_a == record_in_b)
+ {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) return false;
+ }
+
+ return true;
+ }
+}
+
+bool load_txt_records_from_dns(std::vector<std::string> &good_records, const std::vector<std::string> &dns_urls)
+{
+ // Prevent infinite recursion when distributing
+ if (dns_urls.empty()) return false;
+
+ std::vector<std::vector<std::string> > records;
+ records.resize(dns_urls.size());
+
+ std::random_device rd;
+ std::mt19937 gen(rd());
+ std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1);
+ size_t first_index = dis(gen);
+
+ bool avail, valid;
+ size_t cur_index = first_index;
+ do
+ {
+ std::string url = dns_urls[cur_index];
+
+ records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid);
+ if (!avail)
+ {
+ records[cur_index].clear();
+ LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping.");
+ }
+ if (!valid)
+ {
+ records[cur_index].clear();
+ LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping.");
+ }
+
+ cur_index++;
+ if (cur_index == dns_urls.size())
+ {
+ cur_index = 0;
+ }
+ } while (cur_index != first_index);
+
+ size_t num_valid_records = 0;
+
+ for( const auto& record_set : records)
+ {
+ if (record_set.size() != 0)
+ {
+ num_valid_records++;
+ }
+ }
+
+ if (num_valid_records < 2)
+ {
+ LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received");
+ return false;
+ }
+
+ int good_records_index = -1;
+ for (size_t i = 0; i < records.size() - 1; ++i)
+ {
+ if (records[i].size() == 0) continue;
+
+ for (size_t j = i + 1; j < records.size(); ++j)
+ {
+ if (dns_records_match(records[i], records[j]))
+ {
+ good_records_index = i;
+ break;
+ }
+ }
+ if (good_records_index >= 0) break;
+ }
+
+ if (good_records_index < 0)
+ {
+ LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched");
+ return false;
+ }
+
+ good_records = records[good_records_index];
+ return true;
+}
+
} // namespace tools::dns_utils
} // namespace tools
diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h
index 8a63a8129..53c0c1c7b 100644
--- a/src/common/dns_utils.h
+++ b/src/common/dns_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -29,7 +29,7 @@
#include <vector>
#include <string>
-#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_basic.h"
namespace tools
{
@@ -163,7 +163,9 @@ namespace dns_utils
std::string address_from_txt_record(const std::string& s);
std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid);
-std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid);
+std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm = true);
+
+bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls);
} // namespace tools::dns_utils
diff --git a/src/common/download.cpp b/src/common/download.cpp
new file mode 100644
index 000000000..28aac5a59
--- /dev/null
+++ b/src/common/download.cpp
@@ -0,0 +1,270 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+#include <atomic>
+#include <boost/filesystem.hpp>
+#include <boost/asio.hpp>
+#include <boost/thread/thread.hpp>
+#include "cryptonote_config.h"
+#include "include_base_utils.h"
+#include "net/http_client.h"
+#include "download.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "net.dl"
+
+namespace tools
+{
+ struct download_thread_control
+ {
+ const std::string path;
+ const std::string uri;
+ std::function<void(const std::string&, const std::string&, bool)> result_cb;
+ std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress_cb;
+ bool stop;
+ bool stopped;
+ bool success;
+ boost::thread thread;
+ boost::mutex mutex;
+
+ download_thread_control(const std::string &path, const std::string &uri, std::function<void(const std::string&, const std::string&, bool)> result_cb, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress_cb):
+ path(path), uri(uri), result_cb(result_cb), progress_cb(progress_cb), stop(false), stopped(false), success(false) {}
+ ~download_thread_control() { if (thread.joinable()) thread.detach(); }
+ };
+
+ static void download_thread(download_async_handle control)
+ {
+ static std::atomic<unsigned int> thread_id(0);
+
+ MLOG_SET_THREAD_NAME("DL" + std::to_string(thread_id++));
+
+ struct stopped_setter
+ {
+ stopped_setter(const download_async_handle &control): control(control) {}
+ ~stopped_setter() { control->stopped = true; }
+ download_async_handle control;
+ } stopped_setter(control);
+
+ try
+ {
+ boost::unique_lock<boost::mutex> lock(control->mutex);
+ MINFO("Downloading " << control->uri << " to " << control->path);
+ std::ofstream f;
+ f.open(control->path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);
+ if (!f.good()) {
+ MERROR("Failed to open file " << control->path);
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ class download_client: public epee::net_utils::http::http_simple_client
+ {
+ public:
+ download_client(download_async_handle control, std::ofstream &f):
+ control(control), f(f), content_length(-1), total(0) {}
+ virtual ~download_client() { f.close(); }
+ virtual bool on_header(const epee::net_utils::http::http_response_info &headers)
+ {
+ ssize_t length;
+ if (epee::string_tools::get_xtype_from_string(length, headers.m_header_info.m_content_length) && length >= 0)
+ {
+ MINFO("Content-Length: " << length);
+ content_length = length;
+ boost::filesystem::path path(control->path);
+ boost::filesystem::space_info si = boost::filesystem::space(path);
+ if (si.available < (size_t)content_length)
+ {
+ const uint64_t avail = (si.available + 1023) / 1024, needed = (content_length + 1023) / 1024;
+ MERROR("Not enough space to download " << needed << " kB to " << path << " (" << avail << " kB available)");
+ return false;
+ }
+ }
+ return true;
+ }
+ virtual bool handle_target_data(std::string &piece_of_transfer)
+ {
+ try
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ if (control->stop)
+ return false;
+ f << piece_of_transfer;
+ total += piece_of_transfer.size();
+ if (control->progress_cb && !control->progress_cb(control->path, control->uri, total, content_length))
+ return false;
+ return f.good();
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Error writing data: " << e.what());
+ return false;
+ }
+ }
+ private:
+ download_async_handle control;
+ std::ofstream &f;
+ ssize_t content_length;
+ size_t total;
+ } client(control, f);
+ epee::net_utils::http::url_content u_c;
+ if (!epee::net_utils::parse_url(control->uri, u_c))
+ {
+ MERROR("Failed to parse URL " << control->uri);
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ if (u_c.host.empty())
+ {
+ MERROR("Failed to determine address from URL " << control->uri);
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+
+ lock.unlock();
+
+ uint16_t port = u_c.port ? u_c.port : 80;
+ MDEBUG("Connecting to " << u_c.host << ":" << port);
+ client.set_server(u_c.host, std::to_string(port), boost::none);
+ if (!client.connect(std::chrono::seconds(30)))
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ MERROR("Failed to connect to " << control->uri);
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ MDEBUG("GETting " << u_c.uri);
+ const epee::net_utils::http::http_response_info *info = NULL;
+ if (!client.invoke_get(u_c.uri, std::chrono::seconds(30), "", &info))
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ MERROR("Failed to connect to " << control->uri);
+ client.disconnect();
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ if (control->stop)
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ MDEBUG("Download cancelled");
+ client.disconnect();
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ if (!info)
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ MERROR("Failed invoking GET command to " << control->uri << ", no status info returned");
+ client.disconnect();
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ MDEBUG("response code: " << info->m_response_code);
+ MDEBUG("response length: " << info->m_header_info.m_content_length);
+ MDEBUG("response comment: " << info->m_response_comment);
+ MDEBUG("response body: " << info->m_body);
+ for (const auto &f: info->m_additional_fields)
+ MDEBUG("additional field: " << f.first << ": " << f.second);
+ if (info->m_response_code != 200)
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ MERROR("Status code " << info->m_response_code);
+ client.disconnect();
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ client.disconnect();
+ f.close();
+ MDEBUG("Download complete");
+ lock.lock();
+ control->success = true;
+ control->result_cb(control->path, control->uri, control->success);
+ return;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Exception in download thread: " << e.what());
+ // fall through and call result_cb not from the catch block to avoid another exception
+ }
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ control->result_cb(control->path, control->uri, control->success);
+ }
+
+ bool download(const std::string &path, const std::string &url, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> cb)
+ {
+ bool success = false;
+ download_async_handle handle = download_async(path, url, [&success](const std::string&, const std::string&, bool result) {success = result;}, cb);
+ download_wait(handle);
+ return success;
+ }
+
+ download_async_handle download_async(const std::string &path, const std::string &url, std::function<void(const std::string&, const std::string&, bool)> result, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress)
+ {
+ download_async_handle control = std::make_shared<download_thread_control>(path, url, result, progress);
+ control->thread = boost::thread([control](){ download_thread(control); });
+ return control;
+ }
+
+ bool download_finished(const download_async_handle &control)
+ {
+ CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle");
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ return control->stopped;
+ }
+
+ bool download_error(const download_async_handle &control)
+ {
+ CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle");
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ return !control->success;
+ }
+
+ bool download_wait(const download_async_handle &control)
+ {
+ CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle");
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ if (control->stopped)
+ return true;
+ }
+ control->thread.join();
+ return true;
+ }
+
+ bool download_cancel(const download_async_handle &control)
+ {
+ CHECK_AND_ASSERT_MES(control != 0, false, "NULL async download handle");
+ {
+ boost::lock_guard<boost::mutex> lock(control->mutex);
+ if (control->stopped)
+ return true;
+ control->stop = true;
+ }
+ control->thread.join();
+ return true;
+ }
+}
diff --git a/src/common/download.h b/src/common/download.h
new file mode 100644
index 000000000..917cb2278
--- /dev/null
+++ b/src/common/download.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <string>
+
+namespace tools
+{
+ struct download_thread_control;
+ typedef std::shared_ptr<download_thread_control> download_async_handle;
+
+ bool download(const std::string &path, const std::string &url, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress = NULL);
+ download_async_handle download_async(const std::string &path, const std::string &url, std::function<void(const std::string&, const std::string&, bool)> result, std::function<bool(const std::string&, const std::string&, size_t, ssize_t)> progress = NULL);
+ bool download_error(const download_async_handle &h);
+ bool download_finished(const download_async_handle &h);
+ bool download_wait(const download_async_handle &h);
+ bool download_cancel(const download_async_handle &h);
+}
diff --git a/src/common/http_connection.h b/src/common/http_connection.h
index 8a786361a..0357a90a0 100644
--- a/src/common/http_connection.h
+++ b/src/common/http_connection.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/i18n.cpp b/src/common/i18n.cpp
index 05eea3b66..4a76e76fc 100644
--- a/src/common/i18n.cpp
+++ b/src/common/i18n.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/i18n.h b/src/common/i18n.h
index 332602185..5169cf9f7 100644
--- a/src/common/i18n.h
+++ b/src/common/i18n.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/int-util.h b/src/common/int-util.h
index 9ac20e582..34288805a 100644
--- a/src/common/int-util.h
+++ b/src/common/int-util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/password_container.cpp b/src/common/password.cpp
index 832b93a1a..bdc9c69c0 100644
--- a/src/wallet/password_container.cpp
+++ b/src/common/password.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -28,7 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-#include "password_container.h"
+#include "password.h"
#include <iostream>
#include <memory.h>
@@ -245,4 +245,27 @@ namespace tools
return boost::none;
}
+
+ boost::optional<login> login::parse(std::string&& userpass, bool verify, const char* message)
+ {
+ login out{};
+ password_container wipe{std::move(userpass)};
+
+ const auto loc = wipe.password().find(':');
+ if (loc == std::string::npos)
+ {
+ auto result = tools::password_container::prompt(verify, message);
+ if (!result)
+ return boost::none;
+
+ out.password = std::move(*result);
+ }
+ else
+ {
+ out.password = password_container{wipe.password().substr(loc + 1)};
+ }
+
+ out.username = wipe.password().substr(0, loc);
+ return {std::move(out)};
+ }
}
diff --git a/src/wallet/password_container.h b/src/common/password.h
index 9c6faf9c8..12f715df4 100644
--- a/src/wallet/password_container.h
+++ b/src/common/password.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -64,4 +64,33 @@ namespace tools
//! TODO Custom allocator that locks to RAM?
std::string m_password;
};
+
+ struct login
+ {
+ login() = default;
+
+ /*!
+ Extracts username and password from the format `username:password`. A
+ blank username or password is allowed. If the `:` character is not
+ present, `password_container::prompt` will be called by forwarding the
+ `verify` and `message` arguments.
+
+ \param userpass Is "consumed", and the memory contents are wiped.
+ \param verify is passed to `password_container::prompt` if necessary.
+ \param message is passed to `password_container::prompt` if necessary.
+
+ \return The username and password, or boost::none if
+ `password_container::prompt` fails.
+ */
+ static boost::optional<login> parse(std::string&& userpass, bool verify, const char* message = "Password");
+
+ login(const login&) = delete;
+ login(login&&) = default;
+ ~login() = default;
+ login& operator=(const login&) = delete;
+ login& operator=(login&&) = default;
+
+ std::string username;
+ password_container password;
+ };
}
diff --git a/src/common/pod-class.h b/src/common/pod-class.h
index f3f241552..3896d5c29 100644
--- a/src/common/pod-class.h
+++ b/src/common/pod-class.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h
index f5ecc8b50..8494b4a60 100644
--- a/src/common/rpc_client.h
+++ b/src/common/rpc_client.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -28,10 +28,13 @@
#pragma once
+#include <boost/optional/optional.hpp>
+
#include "common/http_connection.h"
#include "common/scoped_message_writer.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "storages/http_abstract_invoke.h"
+#include "net/http_auth.h"
#include "net/http_client.h"
#include "string_tools.h"
@@ -45,11 +48,12 @@ namespace tools
t_rpc_client(
uint32_t ip
, uint16_t port
+ , boost::optional<epee::net_utils::http::login> user
)
: m_http_client{}
{
m_http_client.set_server(
- epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port)
+ epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port), std::move(user)
);
}
diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h
index f82181926..7ee4f1379 100644
--- a/src/common/scoped_message_writer.h
+++ b/src/common/scoped_message_writer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp
index 67065aae7..ef64c20c5 100644
--- a/src/common/stack_trace.cpp
+++ b/src/common/stack_trace.cpp
@@ -26,7 +26,7 @@
// 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.
-#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__
+#if !defined __GNUC__ || defined __MINGW32__ || defined __MINGW64__ || defined __ANDROID__
#define USE_UNWIND
#endif
diff --git a/src/common/task_region.cpp b/src/common/task_region.cpp
index b53a8376a..9b4620c6e 100644
--- a/src/common/task_region.cpp
+++ b/src/common/task_region.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/task_region.h b/src/common/task_region.h
index e4d210661..30972cce3 100644
--- a/src/common/task_region.h
+++ b/src/common/task_region.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/thread_group.cpp b/src/common/thread_group.cpp
index 4e1cc8964..860d0b732 100644
--- a/src/common/thread_group.cpp
+++ b/src/common/thread_group.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/thread_group.h b/src/common/thread_group.h
index 10add89e0..48fd4cd56 100644
--- a/src/common/thread_group.h
+++ b/src/common/thread_group.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h
index b2d5b27a6..4d82a1364 100644
--- a/src/common/unordered_containers_boost_serialization.h
+++ b/src/common/unordered_containers_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/common/updates.cpp b/src/common/updates.cpp
new file mode 100644
index 000000000..5b1acf5fa
--- /dev/null
+++ b/src/common/updates.cpp
@@ -0,0 +1,115 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "util.h"
+#include "dns_utils.h"
+#include "updates.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "updates"
+
+namespace tools
+{
+ bool check_updates(const std::string &software, const std::string &buildtag, std::string &version, std::string &hash)
+ {
+ std::vector<std::string> records;
+ bool found = false;
+
+ MDEBUG("Checking updates for " << buildtag << " " << software);
+
+ // All four MoneroPulse domains have DNSSEC on and valid
+ static const std::vector<std::string> dns_urls = {
+ "updates.moneropulse.org",
+ "updates.moneropulse.net",
+ "updates.moneropulse.co",
+ "updates.moneropulse.se"
+ };
+
+ if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
+ return false;
+
+ for (const auto& record : records)
+ {
+ std::vector<std::string> fields;
+ boost::split(fields, record, boost::is_any_of(":"));
+ if (fields.size() != 4)
+ {
+ MWARNING("Updates record does not have 4 fields: " << record);
+ continue;
+ }
+
+ if (software != fields[0] || buildtag != fields[1])
+ continue;
+
+ bool alnum = true;
+ for (auto c: hash)
+ if (!isalnum(c))
+ alnum = false;
+ if (hash.size() != 64 && !alnum)
+ {
+ MWARNING("Invalid hash: " << hash);
+ continue;
+ }
+
+ // use highest version
+ if (found)
+ {
+ int cmp = vercmp(version.c_str(), fields[2].c_str());
+ if (cmp > 0)
+ continue;
+ if (cmp == 0 && hash != fields[3])
+ MWARNING("Two matches found for " << software << " version " << version << " on " << buildtag);
+ }
+
+ version = fields[2];
+ hash = fields[3];
+
+ MINFO("Found new version " << version << " with hash " << hash);
+ found = true;
+ }
+ return found;
+ }
+
+ std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user)
+ {
+ const char *base = user ? "https://downloads.getmonero.org/" : "http://updates.getmonero.org/";
+#ifdef _WIN32
+ static const char extension[] = ".zip";
+#else
+ static const char extension[] = ".tar.bz2";
+#endif
+
+ std::string url;
+
+ url = base;
+ if (!subdir.empty())
+ url += subdir + "/";
+ url = url + software + "-" + buildtag + "-v" + version + extension;
+ return url;
+ }
+}
diff --git a/src/common/updates.h b/src/common/updates.h
new file mode 100644
index 000000000..e494ed7ac
--- /dev/null
+++ b/src/common/updates.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#include <string>
+
+namespace tools
+{
+ bool check_updates(const std::string &software, const std::string &buildtag, std::string &version, std::string &hash);
+ std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user);
+}
diff --git a/src/common/util.cpp b/src/common/util.cpp
index bfcf86bc6..046961b06 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -31,6 +31,7 @@
#include <cstdio>
#include "include_base_utils.h"
+#include "file_io_utils.h"
using namespace epee;
#include "util.h"
@@ -46,7 +47,7 @@ using namespace epee;
#endif
#include <boost/filesystem.hpp>
#include <boost/asio.hpp>
-
+#include <openssl/sha.h>
namespace tools
{
@@ -568,4 +569,65 @@ std::string get_nix_version_display_string()
MDEBUG("Address '" << address << "' is not local");
return false;
}
+ int vercmp(const char *v0, const char *v1)
+ {
+ std::vector<std::string> f0, f1;
+ boost::split(f0, v0, boost::is_any_of("."));
+ boost::split(f1, v1, boost::is_any_of("."));
+ while (f0.size() < f1.size())
+ f0.push_back("0");
+ while (f1.size() < f0.size())
+ f1.push_back("0");
+ for (size_t i = 0; i < f0.size(); ++i) {
+ int f0i = atoi(f0[i].c_str()), f1i = atoi(f1[i].c_str());
+ int n = f0i - f1i;
+ if (n)
+ return n;
+ }
+ return 0;
+ }
+
+ bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash)
+ {
+ SHA256_CTX ctx;
+ if (!SHA256_Init(&ctx))
+ return false;
+ if (!SHA256_Update(&ctx, data, len))
+ return false;
+ if (!SHA256_Final((unsigned char*)hash.data, &ctx))
+ return false;
+ return true;
+ }
+
+ bool sha256sum(const std::string &filename, crypto::hash &hash)
+ {
+ if (!epee::file_io_utils::is_file_exist(filename))
+ return false;
+ std::ifstream f;
+ f.exceptions(std::ifstream::failbit | std::ifstream::badbit);
+ f.open(filename, std::ios_base::binary | std::ios_base::in | std::ios::ate);
+ if (!f)
+ return false;
+ std::ifstream::pos_type file_size = f.tellg();
+ SHA256_CTX ctx;
+ if (!SHA256_Init(&ctx))
+ return false;
+ size_t size_left = file_size;
+ f.seekg(0, std::ios::beg);
+ while (size_left)
+ {
+ char buf[4096];
+ std::ifstream::pos_type read_size = size_left > sizeof(buf) ? sizeof(buf) : size_left;
+ f.read(buf, read_size);
+ if (!f || !f.good())
+ return false;
+ if (!SHA256_Update(&ctx, buf, read_size))
+ return false;
+ size_left -= read_size;
+ }
+ f.close();
+ if (!SHA256_Final((unsigned char*)hash.data, &ctx))
+ return false;
+ return true;
+ }
}
diff --git a/src/common/util.h b/src/common/util.h
index c2ffc44ca..4291d7e18 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -183,4 +183,8 @@ namespace tools
unsigned get_max_concurrency();
bool is_local_address(const std::string &address);
+ int vercmp(const char *v0, const char *v1); // returns < 0, 0, > 0, similar to strcmp, but more human friendly than lexical - does not attempt to validate
+
+ bool sha256sum(const uint8_t *data, size_t len, crypto::hash &hash);
+ bool sha256sum(const std::string &filename, crypto::hash &hash);
}
diff --git a/src/common/varint.h b/src/common/varint.h
index ffaa682c5..cb785e61a 100644
--- a/src/common/varint.h
+++ b/src/common/varint.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 1e037a07d..277ee64c2 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -93,7 +93,7 @@ endif()
# Because of the way Qt works on android with JNI, the code does not live in the main android thread
# So this code runs with a 1 MB default stack size.
# This will force the use of the heap for the allocation of the scratchpad
-if (ANDROID)
+if (ANDROID OR IOS)
if( BUILD_GUI_DEPS )
add_definitions(-DFORCE_USE_HEAP=1)
endif()
diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c
index 1cb1cf344..1e43f9c4d 100644
--- a/src/crypto/blake256.c
+++ b/src/crypto/blake256.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h
index e262d1b4b..921fcd2fd 100644
--- a/src/crypto/blake256.h
+++ b/src/crypto/blake256.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h
index 94c0ba721..80557e9f5 100644
--- a/src/crypto/chacha8.h
+++ b/src/crypto/chacha8.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c
index 60ee38096..4bd75b77c 100644
--- a/src/crypto/crypto-ops-data.c
+++ b/src/crypto/crypto-ops-data.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c
index 1b390e402..4edfee0ce 100644
--- a/src/crypto/crypto-ops.c
+++ b/src/crypto/crypto-ops.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h
index 4986499f4..37edf5b6d 100644
--- a/src/crypto/crypto-ops.h
+++ b/src/crypto/crypto-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp
index 6ceb944cd..98da466cc 100644
--- a/src/crypto/crypto.cpp
+++ b/src/crypto/crypto.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 9ca835d9e..3b8c7996b 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto_ops_builder/README.md b/src/crypto/crypto_ops_builder/README.md
index eec3e21e7..3b87966f5 100644
--- a/src/crypto/crypto_ops_builder/README.md
+++ b/src/crypto/crypto_ops_builder/README.md
@@ -1,6 +1,6 @@
# Monero
-Copyright (c) 2014-2016, The Monero Project
+Copyright (c) 2014-2017, The Monero Project
## Crypto Ops Builder
diff --git a/src/crypto/crypto_ops_builder/crypto-ops-data.c b/src/crypto/crypto_ops_builder/crypto-ops-data.c
index 60ee38096..4bd75b77c 100644
--- a/src/crypto/crypto_ops_builder/crypto-ops-data.c
+++ b/src/crypto/crypto_ops_builder/crypto-ops-data.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto_ops_builder/crypto-ops-old.c b/src/crypto/crypto_ops_builder/crypto-ops-old.c
index 910801c57..b7a290b4a 100644
--- a/src/crypto/crypto_ops_builder/crypto-ops-old.c
+++ b/src/crypto/crypto_ops_builder/crypto-ops-old.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto_ops_builder/crypto-ops.h b/src/crypto/crypto_ops_builder/crypto-ops.h
index 84ef12ae2..47d5b46ae 100644
--- a/src/crypto/crypto_ops_builder/crypto-ops.h
+++ b/src/crypto/crypto_ops_builder/crypto-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py
index 9d996f9d1..5f8776a49 100644
--- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py
+++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/MakeCryptoOps.py
@@ -15,7 +15,7 @@ print("maybe someone smart can replace the sed with perl..")
a = ""
license = textwrap.dedent("""\
- // Copyright (c) 2014-2016, The Monero Project
+ // Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h
index cdc5ac1ee..b432efade 100644
--- a/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h
+++ b/src/crypto/crypto_ops_builder/ref10CommentedCombined/crypto-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h
index a8d8fdb19..1a135ffcf 100644
--- a/src/crypto/generic-ops.h
+++ b/src/crypto/generic-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h
index ac749d1d8..89a073a4c 100644
--- a/src/crypto/groestl.h
+++ b/src/crypto/groestl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h
index c271fd367..8fa6d7a83 100644
--- a/src/crypto/groestl_tables.h
+++ b/src/crypto/groestl_tables.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c
index 15357dc7a..236479880 100644
--- a/src/crypto/hash-extra-blake.c
+++ b/src/crypto/hash-extra-blake.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c
index 69546ee44..b15075306 100644
--- a/src/crypto/hash-extra-groestl.c
+++ b/src/crypto/hash-extra-groestl.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c
index 5da0894f6..8950687d3 100644
--- a/src/crypto/hash-extra-jh.c
+++ b/src/crypto/hash-extra-jh.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c
index babf5006f..e63e7da20 100644
--- a/src/crypto/hash-extra-skein.c
+++ b/src/crypto/hash-extra-skein.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h
index 612d94efe..6e3a5c6c9 100644
--- a/src/crypto/hash-ops.h
+++ b/src/crypto/hash-ops.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/hash.c b/src/crypto/hash.c
index 93f7353e4..ed95391d8 100644
--- a/src/crypto/hash.c
+++ b/src/crypto/hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/hash.h b/src/crypto/hash.h
index 803992078..22991e513 100644
--- a/src/crypto/hash.h
+++ b/src/crypto/hash.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h
index fefc3b7c3..619038ae6 100644
--- a/src/crypto/initializer.h
+++ b/src/crypto/initializer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 6a9f63c12..691c31f62 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/random.h b/src/crypto/random.h
index b0d2303b6..75d23fd04 100644
--- a/src/crypto/random.h
+++ b/src/crypto/random.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h
index 0b3d071ee..a06ef30a2 100644
--- a/src/crypto/skein_port.h
+++ b/src/crypto/skein_port.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c
index 43b9619f3..6afa28934 100644
--- a/src/crypto/slow-hash.c
+++ b/src/crypto/slow-hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -44,6 +44,9 @@
#define INIT_SIZE_BLK 8
#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE)
+extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey);
+extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey);
+
#if defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))
// Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI
// Fall back to more portable code is down at the bottom
@@ -138,9 +141,6 @@
#define THREADV __thread
#endif
-extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey);
-extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey);
-
#pragma pack(push, 1)
union cn_slow_hash_state
{
@@ -494,7 +494,7 @@ void slow_hash_free_state(void)
* buffer of pseudorandom data by hashing the supplied data. It then uses this
* random data to fill a large 2MB buffer with pseudorandom data by iteratively
* encrypting it using 10 rounds of AES per entry. After this initialization,
- * it executes 500,000 rounds of mixing through the random 2MB buffer using
+ * it executes 524,288 rounds of mixing through the random 2MB buffer using
* AES (typically provided in hardware on modern CPUs) and a 64 bit multiply.
* Finally, it re-mixes this large buffer back into
* the 200 byte "text" buffer, and then hashes this buffer using one of four
@@ -530,7 +530,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
size_t i, j;
uint64_t *p = NULL;
- oaes_ctx *aes_ctx;
+ oaes_ctx *aes_ctx = NULL;
int useAes = !force_software_aes() && check_aes_hw();
static void (*const extra_hashes[4])(const void *, size_t, char *) =
@@ -578,8 +578,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0];
U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1];
- /* CryptoNight Step 3: Bounce randomly 1 million times through the mixing buffer,
- * using 500,000 iterations of the following mixing function. Each execution
+ /* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer,
+ * using 524,288 iterations of the following mixing function. Each execution
* performs two reads and writes from the mixing buffer.
*/
@@ -722,32 +722,24 @@ union cn_slow_hash_state
* key schedule. Don't try to use this for vanilla AES.
*/
static void aes_expand_key(const uint8_t *key, uint8_t *expandedKey) {
-__asm__("mov x2, %1\n\t" : : "r"(key), "r"(expandedKey));
+static const int rcon[] = {
+ 0x01,0x01,0x01,0x01,
+ 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d, // rotate-n-splat
+ 0x1b,0x1b,0x1b,0x1b };
__asm__(
-" adr x3,Lrcon\n"
-"\n"
" eor v0.16b,v0.16b,v0.16b\n"
-" ld1 {v3.16b},[x0],#16\n"
-" ld1 {v1.4s,v2.4s},[x3],#32\n"
-" b L256\n"
-".align 5\n"
-"Lrcon:\n"
-".long 0x01,0x01,0x01,0x01\n"
-".long 0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d,0x0c0f0e0d // rotate-n-splat\n"
-".long 0x1b,0x1b,0x1b,0x1b\n"
-"\n"
-".align 4\n"
-"L256:\n"
-" ld1 {v4.16b},[x0]\n"
-" mov w1,#5\n"
-" st1 {v3.4s},[x2],#16\n"
+" ld1 {v3.16b},[%0],#16\n"
+" ld1 {v1.4s,v2.4s},[%2],#32\n"
+" ld1 {v4.16b},[%0]\n"
+" mov w2,#5\n"
+" st1 {v3.4s},[%1],#16\n"
"\n"
-"Loop256:\n"
+"1:\n"
" tbl v6.16b,{v4.16b},v2.16b\n"
" ext v5.16b,v0.16b,v3.16b,#12\n"
-" st1 {v4.4s},[x2],#16\n"
+" st1 {v4.4s},[%1],#16\n"
" aese v6.16b,v0.16b\n"
-" subs w1,w1,#1\n"
+" subs w2,w2,#1\n"
"\n"
" eor v3.16b,v3.16b,v5.16b\n"
" ext v5.16b,v0.16b,v5.16b,#12\n"
@@ -757,8 +749,8 @@ __asm__(
" eor v3.16b,v3.16b,v5.16b\n"
" shl v1.16b,v1.16b,#1\n"
" eor v3.16b,v3.16b,v6.16b\n"
-" st1 {v3.4s},[x2],#16\n"
-" b.eq Ldone\n"
+" st1 {v3.4s},[%1],#16\n"
+" b.eq 2f\n"
"\n"
" dup v6.4s,v3.s[3] // just splat\n"
" ext v5.16b,v0.16b,v4.16b,#12\n"
@@ -771,9 +763,9 @@ __asm__(
" eor v4.16b,v4.16b,v5.16b\n"
"\n"
" eor v4.16b,v4.16b,v6.16b\n"
-" b Loop256\n"
+" b 1b\n"
"\n"
-"Ldone:\n");
+"2:\n" : : "r"(key), "r"(expandedKey), "r"(rcon));
}
/* An ordinary AES round is a sequence of SubBytes, ShiftRows, MixColumns, AddRoundKey. There
@@ -895,8 +887,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash)
U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0];
U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1];
- /* CryptoNight Step 3: Bounce randomly 1 million times through the mixing buffer,
- * using 500,000 iterations of the following mixing function. Each execution
+ /* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer,
+ * using 524,288 iterations of the following mixing function. Each execution
* performs two reads and writes from the mixing buffer.
*/
diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c
index 5cdaa8c94..eb98c31b7 100644
--- a/src/crypto/tree-hash.c
+++ b/src/crypto/tree-hash.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
new file mode 100644
index 000000000..2b8ad365a
--- /dev/null
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -0,0 +1,73 @@
+# 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.
+
+set(cryptonote_basic_sources
+ account.cpp
+ checkpoints.cpp
+ cryptonote_basic_impl.cpp
+ cryptonote_format_utils.cpp
+ difficulty.cpp
+ hardfork.cpp
+ miner.cpp)
+
+set(cryptonote_basic_headers)
+
+set(cryptonote_basic_private_headers
+ account.h
+ account_boost_serialization.h
+ checkpoints.h
+ connection_context.h
+ cryptonote_basic.h
+ cryptonote_basic_impl.h
+ cryptonote_boost_serialization.h
+ cryptonote_format_utils.h
+ cryptonote_stat_info.h
+ difficulty.h
+ hardfork.h
+ miner.h
+ tx_extra.h
+ verification_context.h)
+
+monero_private_headers(cryptonote_basic
+ ${crypto_private_headers})
+monero_add_library(cryptonote_basic
+ ${cryptonote_basic_sources}
+ ${cryptonote_basic_headers}
+ ${cryptonote_basic_private_headers})
+target_link_libraries(cryptonote_basic
+ PUBLIC
+ common
+ crypto
+ ${Boost_DATE_TIME_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_SERIALIZATION_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ PRIVATE
+ ${EXTRA_LIBRARIES})
diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_basic/account.cpp
index 8f2db6863..dd875402f 100644
--- a/src/cryptonote_core/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -38,8 +38,8 @@ extern "C"
{
#include "crypto/keccak.h"
}
-#include "cryptonote_core/cryptonote_basic_impl.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic_impl.h"
+#include "cryptonote_format_utils.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "account"
diff --git a/src/cryptonote_core/account.h b/src/cryptonote_basic/account.h
index 41a119b07..e0d5447a2 100644
--- a/src/cryptonote_core/account.h
+++ b/src/cryptonote_basic/account.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,7 +30,7 @@
#pragma once
-#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_basic.h"
#include "crypto/crypto.h"
#include "serialization/keyvalue_serialization.h"
diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_basic/account_boost_serialization.h
index 4151d9b50..d2f541638 100644
--- a/src/cryptonote_core/account_boost_serialization.h
+++ b/src/cryptonote_basic/account_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -31,7 +31,7 @@
#pragma once
#include "account.h"
-#include "cryptonote_core/cryptonote_boost_serialization.h"
+#include "cryptonote_boost_serialization.h"
//namespace cryptonote {
namespace boost
diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_basic/checkpoints.cpp
index 3cf804ede..103a4a33e 100644
--- a/src/cryptonote_core/checkpoints.cpp
+++ b/src/cryptonote_basic/checkpoints.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -42,30 +42,6 @@ using namespace epee;
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "checkpoints"
-namespace
-{
- bool dns_records_match(const std::vector<std::string>& a, const std::vector<std::string>& b)
- {
- if (a.size() != b.size()) return false;
-
- for (const auto& record_in_a : a)
- {
- bool ok = false;
- for (const auto& record_in_b : b)
- {
- if (record_in_a == record_in_b)
- {
- ok = true;
- break;
- }
- }
- if (!ok) return false;
- }
-
- return true;
- }
-} // anonymous namespace
-
namespace cryptonote
{
//---------------------------------------------------------------------------
@@ -230,6 +206,8 @@ namespace cryptonote
bool checkpoints::load_checkpoints_from_dns(bool testnet)
{
+ std::vector<std::string> records;
+
// All four MoneroPulse domains have DNSSEC on and valid
static const std::vector<std::string> dns_urls = { "checkpoints.moneropulse.se"
, "checkpoints.moneropulse.org"
@@ -243,87 +221,10 @@ namespace cryptonote
, "testpoints.moneropulse.co"
};
- std::vector<std::vector<std::string> > records;
- records.resize(dns_urls.size());
-
- std::random_device rd;
- std::mt19937 gen(rd());
- std::uniform_int_distribution<int> dis(0, dns_urls.size() - 1);
- size_t first_index = dis(gen);
-
- bool avail, valid;
- size_t cur_index = first_index;
- do
- {
- std::string url;
- if (testnet)
- {
- url = testnet_dns_urls[cur_index];
- }
- else
- {
- url = dns_urls[cur_index];
- }
-
- records[cur_index] = tools::DNSResolver::instance().get_txt_record(url, avail, valid);
- if (!avail)
- {
- records[cur_index].clear();
- LOG_PRINT_L2("DNSSEC not available for checkpoint update at URL: " << url << ", skipping.");
- }
- if (!valid)
- {
- records[cur_index].clear();
- LOG_PRINT_L2("DNSSEC validation failed for checkpoint update at URL: " << url << ", skipping.");
- }
-
- cur_index++;
- if (cur_index == dns_urls.size())
- {
- cur_index = 0;
- }
- records[cur_index].clear();
- } while (cur_index != first_index);
-
- size_t num_valid_records = 0;
-
- for( const auto& record_set : records)
- {
- if (record_set.size() != 0)
- {
- num_valid_records++;
- }
- }
-
- if (num_valid_records < 2)
- {
- LOG_PRINT_L0("WARNING: no two valid MoneroPulse DNS checkpoint records were received");
- return true;
- }
-
- int good_records_index = -1;
- for (size_t i = 0; i < records.size() - 1; ++i)
- {
- if (records[i].size() == 0) continue;
-
- for (size_t j = i + 1; j < records.size(); ++j)
- {
- if (dns_records_match(records[i], records[j]))
- {
- good_records_index = i;
- break;
- }
- }
- if (good_records_index >= 0) break;
- }
-
- if (good_records_index < 0)
- {
- LOG_PRINT_L0("WARNING: no two MoneroPulse DNS checkpoint records matched");
- return true;
- }
+ if (!tools::dns_utils::load_txt_records_from_dns(records, testnet ? testnet_dns_urls : dns_urls))
+ return true; // why true ?
- for (auto& record : records[good_records_index])
+ for (const auto& record : records)
{
auto pos = record.find(":");
if (pos != std::string::npos)
diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_basic/checkpoints.h
index 71727753e..3a74d8a69 100644
--- a/src/cryptonote_core/checkpoints.h
+++ b/src/cryptonote_basic/checkpoints.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_basic/connection_context.h
index 7e62e77b9..8d739900e 100644
--- a/src/cryptonote_core/connection_context.h
+++ b/src/cryptonote_basic/connection_context.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index da069a21a..c4adf1fcb 100644
--- a/src/cryptonote_core/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -35,6 +35,7 @@
#include <vector>
#include <cstring> // memcmp
#include <sstream>
+#include <atomic>
#include "serialization/serialization.h"
#include "serialization/variant.h"
#include "serialization/vector.h"
@@ -43,7 +44,6 @@
#include "serialization/debug_archive.h"
#include "serialization/crypto.h"
#include "serialization/keyvalue_serialization.h" // eepe named serialization
-#include "string_tools.h"
#include "cryptonote_config.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
@@ -186,15 +186,37 @@ namespace cryptonote
class transaction: public transaction_prefix
{
+ private:
+ // hash cash
+ mutable std::atomic<bool> hash_valid;
+ mutable std::atomic<bool> blob_size_valid;
+
public:
std::vector<std::vector<crypto::signature> > signatures; //count signatures always the same as inputs count
rct::rctSig rct_signatures;
+ // hash cash
+ mutable crypto::hash hash;
+ mutable size_t blob_size;
+
transaction();
+ transaction(const transaction &t): transaction_prefix(t), hash_valid(false), blob_size_valid(false), signatures(t.signatures), rct_signatures(t.rct_signatures) { if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } }
+ transaction &operator=(const transaction &t) { transaction_prefix::operator=(t); set_hash_valid(false); set_blob_size_valid(false); signatures = t.signatures; rct_signatures = t.rct_signatures; if (t.is_hash_valid()) { hash = t.hash; set_hash_valid(true); } if (t.is_blob_size_valid()) { blob_size = t.blob_size; set_blob_size_valid(true); } return *this; }
virtual ~transaction();
void set_null();
+ void invalidate_hashes();
+ bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); }
+ void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); }
+ bool is_blob_size_valid() const { return blob_size_valid.load(std::memory_order_acquire); }
+ void set_blob_size_valid(bool v) const { blob_size_valid.store(v,std::memory_order_release); }
BEGIN_SERIALIZE_OBJECT()
+ if (!typename Archive<W>::is_saving())
+ {
+ set_hash_valid(false);
+ set_blob_size_valid(false);
+ }
+
FIELDS(*static_cast<transaction_prefix *>(this))
if (version == 1)
@@ -250,6 +272,28 @@ namespace cryptonote
}
END_SERIALIZE()
+ template<bool W, template <bool> class Archive>
+ bool serialize_base(Archive<W> &ar)
+ {
+ FIELDS(*static_cast<transaction_prefix *>(this))
+
+ if (version == 1)
+ {
+ }
+ else
+ {
+ ar.tag("rct_signatures");
+ if (!vin.empty())
+ {
+ ar.begin_object();
+ bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size());
+ if (!r || !ar.stream().good()) return false;
+ ar.end_object();
+ }
+ }
+ return true;
+ }
+
private:
static size_t get_signature_size(const txin_v& tx_in);
};
@@ -277,6 +321,15 @@ namespace cryptonote
extra.clear();
signatures.clear();
rct_signatures.type = rct::RCTTypeNull;
+ set_hash_valid(false);
+ set_blob_size_valid(false);
+ }
+
+ inline
+ void transaction::invalidate_hashes()
+ {
+ set_hash_valid(false);
+ set_blob_size_valid(false);
}
inline
@@ -317,10 +370,28 @@ namespace cryptonote
struct block: public block_header
{
+ private:
+ // hash cash
+ mutable std::atomic<bool> hash_valid;
+
+ public:
+ block(): block_header(), hash_valid(false) {}
+ block(const block &b): block_header(b), hash_valid(false), miner_tx(b.miner_tx), tx_hashes(b.tx_hashes) { if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } }
+ block &operator=(const block &b) { block_header::operator=(b); hash_valid = false; miner_tx = b.miner_tx; tx_hashes = b.tx_hashes; if (b.is_hash_valid()) { hash = b.hash; set_hash_valid(true); } return *this; }
+ void invalidate_hashes() { set_hash_valid(false); }
+ bool is_hash_valid() const { return hash_valid.load(std::memory_order_acquire); }
+ void set_hash_valid(bool v) const { hash_valid.store(v,std::memory_order_release); }
+
transaction miner_tx;
std::vector<crypto::hash> tx_hashes;
+ // hash cash
+ mutable crypto::hash hash;
+
BEGIN_SERIALIZE_OBJECT()
+ if (!typename Archive<W>::is_saving())
+ set_hash_valid(false);
+
FIELDS(*static_cast<block_header *>(this))
FIELD(miner_tx)
FIELD(tx_hashes)
diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp
index 338210f01..edd67793f 100644
--- a/src/cryptonote_core/cryptonote_basic_impl.cpp
+++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -67,6 +67,15 @@ namespace cryptonote {
/* Cryptonote helper functions */
/************************************************************************/
//-----------------------------------------------------------------------------------------------
+ size_t get_min_block_size(uint8_t version)
+ {
+ if (version < 2)
+ return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
+ if (version < 5)
+ return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2;
+ return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5;
+ }
+ //-----------------------------------------------------------------------------------------------
size_t get_max_block_size()
{
return CRYPTONOTE_MAX_BLOCK_SIZE;
@@ -89,7 +98,7 @@ namespace cryptonote {
base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes;
}
- uint64_t full_reward_zone = version < 2 ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2;
+ uint64_t full_reward_zone = get_min_block_size(version);
//make it soft
if (median_size < full_reward_zone) {
@@ -299,12 +308,13 @@ namespace cryptonote {
, crypto::hash8& payment_id
, bool testnet
, const std::string& str_or_url
+ , bool cli_confirm
)
{
if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url))
return true;
bool dnssec_valid;
- std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid);
+ std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, cli_confirm);
return !address_str.empty() &&
get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str);
}
@@ -313,11 +323,12 @@ namespace cryptonote {
cryptonote::account_public_address& address
, bool testnet
, const std::string& str_or_url
+ , bool cli_confirm
)
{
bool has_payment_id;
crypto::hash8 payment_id;
- return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url);
+ return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, cli_confirm);
}
//--------------------------------------------------------------------------------
bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) {
diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h
index 5703a7d75..14c03ac4c 100644
--- a/src/cryptonote_core/cryptonote_basic_impl.h
+++ b/src/cryptonote_basic/cryptonote_basic_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -33,6 +33,8 @@
#include "cryptonote_basic.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
+#include "hex.h"
+#include "span.h"
namespace cryptonote {
@@ -69,6 +71,7 @@ namespace cryptonote {
/************************************************************************/
/* Cryptonote helper functions */
/************************************************************************/
+ size_t get_min_block_size(uint8_t version);
size_t get_max_block_size();
size_t get_max_tx_size();
bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version);
@@ -106,12 +109,14 @@ namespace cryptonote {
, crypto::hash8& payment_id
, bool testnet
, const std::string& str_or_url
+ , bool cli_confirm = true
);
bool get_account_address_from_str_or_url(
cryptonote::account_public_address& address
, bool testnet
, const std::string& str_or_url
+ , bool cli_confirm = true
);
bool is_coinbase(const transaction& tx);
@@ -120,23 +125,28 @@ namespace cryptonote {
bool operator ==(const cryptonote::block& a, const cryptonote::block& b);
}
-template <class T>
-std::ostream &print256(std::ostream &o, const T &v) {
- return o << "<" << epee::string_tools::pod_to_hex(v) << ">";
-}
-template <class T>
-std::ostream &print64(std::ostream &o, const T &v) {
- return o << "<" << epee::string_tools::pod_to_hex(v) << ">";
-}
-
bool parse_hash256(const std::string str_hash, crypto::hash& hash);
namespace crypto {
- inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); }
- inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); }
- inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); }
- inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); }
- inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); }
- inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); }
- inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) { return print64(o, v); }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
+ inline std::ostream &operator <<(std::ostream &o, const crypto::hash8 &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+ }
}
diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h
index 409b9798c..6e4ac9b72 100644
--- a/src/cryptonote_core/cryptonote_boost_serialization.h
+++ b/src/cryptonote_basic/cryptonote_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 2e6917878..745dfb72e 100644
--- a/src/cryptonote_core/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -31,9 +31,9 @@
#include "include_base_utils.h"
using namespace epee;
+#include <atomic>
#include "cryptonote_format_utils.h"
#include "cryptonote_config.h"
-#include "miner.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
@@ -43,6 +43,8 @@ using namespace epee;
#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
+// #define ENABLE_HASH_CASH_INTEGRITY_CHECK
+
static const uint64_t valid_decomposed_outputs[] = {
(uint64_t)1, (uint64_t)2, (uint64_t)3, (uint64_t)4, (uint64_t)5, (uint64_t)6, (uint64_t)7, (uint64_t)8, (uint64_t)9, // 1 piconero
(uint64_t)10, (uint64_t)20, (uint64_t)30, (uint64_t)40, (uint64_t)50, (uint64_t)60, (uint64_t)70, (uint64_t)80, (uint64_t)90,
@@ -66,6 +68,13 @@ static const uint64_t valid_decomposed_outputs[] = {
(uint64_t)10000000000000000000ull
};
+static std::atomic<unsigned int> default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT);
+
+static std::atomic<uint64_t> tx_hashes_calculated_count(0);
+static std::atomic<uint64_t> tx_hashes_cached_count(0);
+static std::atomic<uint64_t> block_hashes_calculated_count(0);
+static std::atomic<uint64_t> block_hashes_cached_count(0);
+
namespace cryptonote
{
//---------------------------------------------------------------
@@ -91,6 +100,17 @@ namespace cryptonote
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
+ tx.invalidate_hashes();
+ return true;
+ }
+ //---------------------------------------------------------------
+ bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx)
+ {
+ std::stringstream ss;
+ ss << tx_blob;
+ binary_archive<false> ba(ss);
+ bool r = tx.serialize_base(ba);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
return true;
}
//---------------------------------------------------------------
@@ -101,6 +121,7 @@ namespace cryptonote
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, tx);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
+ tx.invalidate_hashes();
//TODO: validate tx
get_transaction_hash(tx, tx_hash);
@@ -108,102 +129,6 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
- bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
- tx.vin.clear();
- tx.vout.clear();
- tx.extra.clear();
-
- keypair txkey = keypair::generate();
- add_tx_pub_key_to_extra(tx, txkey.pub);
- if(!extra_nonce.empty())
- if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
- return false;
-
- txin_gen in;
- in.height = height;
-
- uint64_t block_reward;
- if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version))
- {
- LOG_PRINT_L0("Block is too big");
- return false;
- }
-
-#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
- LOG_PRINT_L1("Creating block template: reward " << block_reward <<
- ", fee " << fee);
-#endif
- block_reward += fee;
-
- // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
- // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
- // emission schedule
- // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
- // and avoids the quantization. These outputs will be added as rct outputs with identity
- // masks, to they can be used as rct inputs.
- if (hard_fork_version >= 2 && hard_fork_version < 4) {
- block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
- }
-
- std::vector<uint64_t> out_amounts;
- decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
- [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
- [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
-
- CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
- if (height == 0 || hard_fork_version >= 4)
- {
- // the genesis block was not decomposed, for unknown reasons
- while (max_outs < out_amounts.size())
- {
- //out_amounts[out_amounts.size() - 2] += out_amounts.back();
- //out_amounts.resize(out_amounts.size() - 1);
- out_amounts[1] += out_amounts[0];
- for (size_t n = 1; n < out_amounts.size(); ++n)
- out_amounts[n - 1] = out_amounts[n];
- out_amounts.resize(out_amounts.size() - 1);
- }
- }
- else
- {
- CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded");
- }
-
- uint64_t summary_amounts = 0;
- for (size_t no = 0; no < out_amounts.size(); no++)
- {
- crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
- crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
- bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
- CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")");
-
- r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key);
- CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")");
-
- txout_to_key tk;
- tk.key = out_eph_public_key;
-
- tx_out out;
- summary_amounts += out.amount = out_amounts[no];
- out.target = tk;
- tx.vout.push_back(out);
- }
-
- CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
-
- if (hard_fork_version >= 4)
- tx.version = 2;
- else
- tx.version = 1;
-
- //lock
- tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
- tx.vin.push_back(in);
- //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee)
- // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2);
- return true;
- }
- //---------------------------------------------------------------
bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki)
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
@@ -239,12 +164,12 @@ namespace cryptonote
if (std::string::npos != point_index)
{
fraction_size = str_amount.size() - point_index - 1;
- while (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size && '0' == str_amount.back())
+ while (default_decimal_point < fraction_size && '0' == str_amount.back())
{
str_amount.erase(str_amount.size() - 1, 1);
--fraction_size;
}
- if (CRYPTONOTE_DISPLAY_DECIMAL_POINT < fraction_size)
+ if (default_decimal_point < fraction_size)
return false;
str_amount.erase(point_index, 1);
}
@@ -256,9 +181,9 @@ namespace cryptonote
if (str_amount.empty())
return false;
- if (fraction_size < CRYPTONOTE_DISPLAY_DECIMAL_POINT)
+ if (fraction_size < default_decimal_point)
{
- str_amount.append(CRYPTONOTE_DISPLAY_DECIMAL_POINT - fraction_size, '0');
+ str_amount.append(default_decimal_point - fraction_size, '0');
}
return string_tools::get_xtype_from_string(amount, str_amount);
@@ -435,22 +360,6 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
- crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys)
- {
- if (destinations.empty())
- return null_pkey;
- for (size_t n = 1; n < destinations.size(); ++n)
- {
- if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr)))
- continue;
- if (destinations[n].amount == 0)
- continue;
- if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr)))
- return null_pkey;
- }
- return destinations[0].addr.m_view_public_key;
- }
- //---------------------------------------------------------------
bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key)
{
crypto::key_derivation derivation;
@@ -475,313 +384,6 @@ namespace cryptonote
return encrypt_payment_id(payment_id, public_key, secret_key);
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
- {
- std::vector<rct::key> amount_keys;
- tx.set_null();
- amount_keys.clear();
-
- tx.version = rct ? 2 : 1;
- tx.unlock_time = unlock_time;
-
- tx.extra = extra;
- keypair txkey = keypair::generate();
- remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key));
- add_tx_pub_key_to_extra(tx, txkey.pub);
- tx_key = txkey.sec;
-
- // if we have a stealth payment id, find it and encrypt it with the tx key now
- std::vector<tx_extra_field> tx_extra_fields;
- if (parse_tx_extra(tx.extra, tx_extra_fields))
- {
- tx_extra_nonce extra_nonce;
- if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
- {
- crypto::hash8 payment_id = null_hash8;
- if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
- {
- LOG_PRINT_L2("Encrypting payment id " << payment_id);
- crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, sender_account_keys);
- if (view_key_pub == null_pkey)
- {
- LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids");
- return false;
- }
-
- if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec))
- {
- LOG_ERROR("Failed to encrypt payment id");
- return false;
- }
-
- std::string extra_nonce;
- set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
- remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce));
- if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
- {
- LOG_ERROR("Failed to add encrypted payment id to tx extra");
- return false;
- }
- LOG_PRINT_L1("Encrypted payment ID: " << payment_id);
- }
- }
- }
- else
- {
- LOG_ERROR("Failed to parse tx extra");
- return false;
- }
-
- struct input_generation_context_data
- {
- keypair in_ephemeral;
- };
- std::vector<input_generation_context_data> in_contexts;
-
- uint64_t summary_inputs_money = 0;
- //fill inputs
- int idx = -1;
- for(const tx_source_entry& src_entr: sources)
- {
- ++idx;
- if(src_entr.real_output >= src_entr.outputs.size())
- {
- LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size());
- return false;
- }
- summary_inputs_money += src_entr.amount;
-
- //key_derivation recv_derivation;
- in_contexts.push_back(input_generation_context_data());
- keypair& in_ephemeral = in_contexts.back().in_ephemeral;
- crypto::key_image img;
- if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img))
- return false;
-
- //check that derivated key is equal with real output key
- if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
- {
- LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:"
- << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
- << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) );
- LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct);
- LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index);
- return false;
- }
-
- //put key image into tx input
- txin_to_key input_to_key;
- input_to_key.amount = src_entr.amount;
- input_to_key.k_image = img;
-
- //fill outputs array and use relative offsets
- for(const tx_source_entry::output_entry& out_entry: src_entr.outputs)
- input_to_key.key_offsets.push_back(out_entry.first);
-
- input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
- tx.vin.push_back(input_to_key);
- }
-
- // "Shuffle" outs
- std::vector<tx_destination_entry> shuffled_dsts(destinations);
- std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } );
-
- uint64_t summary_outs_money = 0;
- //fill outputs
- size_t output_index = 0;
- for(const tx_destination_entry& dst_entr: shuffled_dsts)
- {
- CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
- crypto::key_derivation derivation;
- crypto::public_key out_eph_public_key;
- bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")");
-
- if (tx.version > 1)
- {
- crypto::secret_key scalar1;
- crypto::derivation_to_scalar(derivation, output_index, scalar1);
- amount_keys.push_back(rct::sk2rct(scalar1));
- }
- r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
- CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")");
-
- tx_out out;
- out.amount = dst_entr.amount;
- txout_to_key tk;
- tk.key = out_eph_public_key;
- out.target = tk;
- tx.vout.push_back(out);
- output_index++;
- summary_outs_money += dst_entr.amount;
- }
-
- //check money
- if(summary_outs_money > summary_inputs_money )
- {
- LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")");
- return false;
- }
-
- // check for watch only wallet
- bool zero_secret_key = true;
- for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i)
- zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0);
- if (zero_secret_key)
- {
- MDEBUG("Null secret key, skipping signatures");
- }
-
- if (tx.version == 1)
- {
- //generate ring signatures
- crypto::hash tx_prefix_hash;
- get_transaction_prefix_hash(tx, tx_prefix_hash);
-
- std::stringstream ss_ring_s;
- size_t i = 0;
- for(const tx_source_entry& src_entr: sources)
- {
- ss_ring_s << "pub_keys:" << ENDL;
- std::vector<const crypto::public_key*> keys_ptrs;
- std::vector<crypto::public_key> keys(src_entr.outputs.size());
- size_t ii = 0;
- for(const tx_source_entry::output_entry& o: src_entr.outputs)
- {
- keys[ii] = rct2pk(o.second.dest);
- keys_ptrs.push_back(&keys[ii]);
- ss_ring_s << o.second.dest << ENDL;
- ++ii;
- }
-
- tx.signatures.push_back(std::vector<crypto::signature>());
- std::vector<crypto::signature>& sigs = tx.signatures.back();
- sigs.resize(src_entr.outputs.size());
- if (!zero_secret_key)
- crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
- ss_ring_s << "signatures:" << ENDL;
- std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
- ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output;
- i++;
- }
-
- MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str());
- }
- else
- {
- size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
-
- // the non-simple version is slightly smaller, but assumes all real inputs
- // are on the same index, so can only be used if there just one ring.
- bool use_simple_rct = sources.size() > 1;
-
- if (!use_simple_rct)
- {
- // non simple ringct requires all real inputs to be at the same index for all inputs
- for(const tx_source_entry& src_entr: sources)
- {
- if(src_entr.real_output != sources.begin()->real_output)
- {
- LOG_ERROR("All inputs must have the same index for non-simple ringct");
- return false;
- }
- }
-
- // enforce same mixin for all outputs
- for (size_t i = 1; i < sources.size(); ++i) {
- if (n_total_outs != sources[i].outputs.size()) {
- LOG_ERROR("Non-simple ringct transaction has varying mixin");
- return false;
- }
- }
- }
-
- uint64_t amount_in = 0, amount_out = 0;
- rct::ctkeyV inSk;
- // mixRing indexing is done the other way round for simple
- rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs);
- rct::keyV destinations;
- std::vector<uint64_t> inamounts, outamounts;
- std::vector<unsigned int> index;
- for (size_t i = 0; i < sources.size(); ++i)
- {
- rct::ctkey ctkey;
- amount_in += sources[i].amount;
- inamounts.push_back(sources[i].amount);
- index.push_back(sources[i].real_output);
- // inSk: (secret key, mask)
- ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
- ctkey.mask = sources[i].mask;
- inSk.push_back(ctkey);
- // inPk: (public key, commitment)
- // will be done when filling in mixRing
- }
- for (size_t i = 0; i < tx.vout.size(); ++i)
- {
- destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
- outamounts.push_back(tx.vout[i].amount);
- amount_out += tx.vout[i].amount;
- }
-
- if (use_simple_rct)
- {
- // mixRing indexing is done the other way round for simple
- for (size_t i = 0; i < sources.size(); ++i)
- {
- mixRing[i].resize(sources[i].outputs.size());
- for (size_t n = 0; n < sources[i].outputs.size(); ++n)
- {
- mixRing[i][n] = sources[i].outputs[n].second;
- }
- }
- }
- else
- {
- for (size_t i = 0; i < n_total_outs; ++i) // same index assumption
- {
- mixRing[i].resize(sources.size());
- for (size_t n = 0; n < sources.size(); ++n)
- {
- mixRing[i][n] = sources[n].outputs[i].second;
- }
- }
- }
-
- // fee
- if (!use_simple_rct && amount_in > amount_out)
- outamounts.push_back(amount_in - amount_out);
-
- // zero out all amounts to mask rct outputs, real amounts are now encrypted
- for (size_t i = 0; i < tx.vin.size(); ++i)
- {
- if (sources[i].rct)
- boost::get<txin_to_key>(tx.vin[i]).amount = 0;
- }
- for (size_t i = 0; i < tx.vout.size(); ++i)
- tx.vout[i].amount = 0;
-
- crypto::hash tx_prefix_hash;
- get_transaction_prefix_hash(tx, tx_prefix_hash);
- rct::ctkeyV outSk;
- if (use_simple_rct)
- tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk);
- else
- tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk); // same index assumption
-
- CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
-
- MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL);
- }
-
- return true;
- }
- //---------------------------------------------------------------
- bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time)
- {
- crypto::secret_key tx_key;
- return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key);
- }
- //---------------------------------------------------------------
bool get_inputs_money_amount(const transaction& tx, uint64_t& money)
{
money = 0;
@@ -924,14 +526,59 @@ namespace cryptonote
cn_fast_hash(blob.data(), blob.size(), res);
}
//---------------------------------------------------------------
- std::string print_money(uint64_t amount)
+ void set_default_decimal_point(unsigned int decimal_point)
{
+ switch (decimal_point)
+ {
+ case 12:
+ case 9:
+ case 6:
+ case 3:
+ case 0:
+ default_decimal_point = decimal_point;
+ break;
+ default:
+ ASSERT_MES_AND_THROW("Invalid decimal point specification: " << decimal_point);
+ }
+ }
+ //---------------------------------------------------------------
+ unsigned int get_default_decimal_point()
+ {
+ return default_decimal_point;
+ }
+ //---------------------------------------------------------------
+ std::string get_unit(unsigned int decimal_point)
+ {
+ if (decimal_point == (unsigned int)-1)
+ decimal_point = default_decimal_point;
+ switch (std::atomic_load(&default_decimal_point))
+ {
+ case 12:
+ return "monero";
+ case 9:
+ return "millinero";
+ case 6:
+ return "micronero";
+ case 3:
+ return "nanonero";
+ case 0:
+ return "piconero";
+ default:
+ ASSERT_MES_AND_THROW("Invalid decimal point specification: " << default_decimal_point);
+ }
+ }
+ //---------------------------------------------------------------
+ std::string print_money(uint64_t amount, unsigned int decimal_point)
+ {
+ if (decimal_point == (unsigned int)-1)
+ decimal_point = default_decimal_point;
std::string s = std::to_string(amount);
- if(s.size() < CRYPTONOTE_DISPLAY_DECIMAL_POINT+1)
+ if(s.size() < decimal_point+1)
{
- s.insert(0, CRYPTONOTE_DISPLAY_DECIMAL_POINT+1 - s.size(), '0');
+ s.insert(0, decimal_point+1 - s.size(), '0');
}
- s.insert(s.size() - CRYPTONOTE_DISPLAY_DECIMAL_POINT, ".");
+ if (decimal_point > 0)
+ s.insert(s.size() - decimal_point, ".");
return s;
}
//---------------------------------------------------------------
@@ -954,7 +601,7 @@ namespace cryptonote
return get_transaction_hash(t, res, NULL);
}
//---------------------------------------------------------------
- bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
+ bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
{
// v1 transactions hash the entire blob
if (t.version == 1)
@@ -1009,6 +656,40 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
+ bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
+ {
+ if (t.is_hash_valid())
+ {
+#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK
+ CHECK_AND_ASSERT_THROW_MES(!calculate_transaction_hash(t, res, blob_size) || t.hash == res, "tx hash cash integrity failure");
+#endif
+ res = t.hash;
+ if (blob_size)
+ {
+ if (!t.is_blob_size_valid())
+ {
+ t.blob_size = get_object_blobsize(t);
+ t.set_blob_size_valid(true);
+ }
+ *blob_size = t.blob_size;
+ }
+ ++tx_hashes_cached_count;
+ return true;
+ }
+ ++tx_hashes_calculated_count;
+ bool ret = calculate_transaction_hash(t, res, blob_size);
+ if (!ret)
+ return false;
+ t.hash = res;
+ t.set_hash_valid(true);
+ if (blob_size)
+ {
+ t.blob_size = *blob_size;
+ t.set_blob_size_valid(true);
+ }
+ return true;
+ }
+ //---------------------------------------------------------------
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size)
{
return get_transaction_hash(t, res, &blob_size);
@@ -1023,7 +704,7 @@ namespace cryptonote
return blob;
}
//---------------------------------------------------------------
- bool get_block_hash(const block& b, crypto::hash& res)
+ bool calculate_block_hash(const block& b, crypto::hash& res)
{
// EXCEPTION FOR BLOCK 202612
const std::string correct_blob_hash_202612 = "3a8a2b3a29b50fc86ff73dd087ea43c6f0d6b8f936c849194d5c84c737903966";
@@ -1050,6 +731,26 @@ namespace cryptonote
return hash_result;
}
//---------------------------------------------------------------
+ bool get_block_hash(const block& b, crypto::hash& res)
+ {
+ if (b.is_hash_valid())
+ {
+#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK
+ CHECK_AND_ASSERT_THROW_MES(!calculate_block_hash(b, res) || b.hash == res, "block hash cash integrity failure");
+#endif
+ res = b.hash;
+ ++block_hashes_cached_count;
+ return true;
+ }
+ ++block_hashes_calculated_count;
+ bool ret = calculate_block_hash(b, res);
+ if (!ret)
+ return false;
+ b.hash = res;
+ b.set_hash_valid(true);
+ return true;
+ }
+ //---------------------------------------------------------------
crypto::hash get_block_hash(const block& b)
{
crypto::hash p = null_hash;
@@ -1057,36 +758,6 @@ namespace cryptonote
return p;
}
//---------------------------------------------------------------
- bool generate_genesis_block(
- block& bl
- , std::string const & genesis_tx
- , uint32_t nonce
- )
- {
- //genesis block
- bl = boost::value_initialized<block>();
-
-
- account_public_address ac = boost::value_initialized<account_public_address>();
- std::vector<size_t> sz;
- construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis
- blobdata txb = tx_to_blob(bl.miner_tx);
- std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb);
-
- std::string genesis_coinbase_tx_hex = config::GENESIS_TX;
-
- blobdata tx_bl;
- string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl);
- bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx);
- CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
- bl.major_version = CURRENT_BLOCK_MAJOR_VERSION;
- bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
- bl.timestamp = 0;
- bl.nonce = nonce;
- miner::find_nonce_for_given_block(bl, 1, 0);
- return true;
- }
- //---------------------------------------------------------------
bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height)
{
// block 202612 bug workaround
@@ -1096,7 +767,6 @@ namespace cryptonote
string_tools::hex_to_pod(longhash_202612, res);
return true;
}
- block b_local = b; //workaround to avoid const errors with do_serialize
blobdata bd = get_block_hashing_blob(b);
crypto::cn_slow_hash(bd.data(), bd.size(), res);
return true;
@@ -1136,6 +806,8 @@ namespace cryptonote
binary_archive<false> ba(ss);
bool r = ::serialization::serialize(ba, b);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob");
+ b.invalidate_hashes();
+ b.miner_tx.invalidate_hashes();
return true;
}
//---------------------------------------------------------------
@@ -1190,4 +862,11 @@ namespace cryptonote
return std::binary_search(begin, end, amount);
}
//---------------------------------------------------------------
+ void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached)
+ {
+ tx_hashes_calculated = tx_hashes_calculated_count;
+ tx_hashes_cached = tx_hashes_cached_count;
+ block_hashes_calculated = block_hashes_calculated_count;
+ block_hashes_cached = block_hashes_cached_count;
+ }
}
diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index a6610e60d..5c10907fd 100644
--- a/src/cryptonote_core/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,14 +30,12 @@
#pragma once
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "cryptonote_basic_impl.h"
#include "account.h"
#include "include_base_utils.h"
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "ringct/rctOps.h"
-#include <boost/serialization/vector.hpp>
-#include <boost/serialization/utility.hpp>
namespace cryptonote
{
@@ -46,43 +44,10 @@ namespace cryptonote
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx);
- bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
+ bool parse_and_validate_tx_base_from_blob(const blobdata& tx_blob, transaction& tx);
bool encrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key);
bool decrypt_payment_id(crypto::hash8 &payment_id, const crypto::public_key &public_key, const crypto::secret_key &secret_key);
- struct tx_source_entry
- {
- typedef std::pair<uint64_t, rct::ctkey> output_entry;
-
- std::vector<output_entry> outputs; //index + key + optional ringct commitment
- size_t real_output; //index in outputs vector of real output_entry
- crypto::public_key real_out_tx_key; //incoming real tx public key
- size_t real_output_in_tx_index; //index in transaction outputs vector
- uint64_t amount; //money
- bool rct; //true if the output is rct
- rct::key mask; //ringct amount mask
-
- void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
- };
-
- struct tx_destination_entry
- {
- uint64_t amount; //money
- account_public_address addr; //destination address
-
- tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { }
- tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { }
-
- BEGIN_SERIALIZE_OBJECT()
- VARINT_FIELD(amount)
- FIELD(addr)
- END_SERIALIZE()
- };
-
- //---------------------------------------------------------------
- bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
-
template<typename T>
bool find_tx_extra_field_by_type(const std::vector<tx_extra_field>& tx_extra_fields, T& field, size_t index = 0)
{
@@ -120,16 +85,13 @@ namespace cryptonote
bool get_transaction_hash(const transaction& t, crypto::hash& res);
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size);
bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size);
+ bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size);
blobdata get_block_hashing_blob(const block& b);
+ bool calculate_block_hash(const block& b, crypto::hash& res);
bool get_block_hash(const block& b, crypto::hash& res);
crypto::hash get_block_hash(const block& b);
bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height);
crypto::hash get_block_longhash(const block& b, uint64_t height);
- bool generate_genesis_block(
- block& bl
- , std::string const & genesis_tx
- , uint32_t nonce
- );
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b);
bool get_inputs_money_amount(const transaction& tx, uint64_t& money);
uint64_t get_outs_money_amount(const transaction& tx);
@@ -143,7 +105,10 @@ namespace cryptonote
uint64_t get_block_height(const block& b);
std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off);
std::vector<uint64_t> absolute_output_offsets_to_relative(const std::vector<uint64_t>& off);
- std::string print_money(uint64_t amount);
+ void set_default_decimal_point(unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT);
+ unsigned int get_default_decimal_point();
+ std::string get_unit(unsigned int decimal_point = -1);
+ std::string print_money(uint64_t amount, unsigned int decimal_point = -1);
//---------------------------------------------------------------
template<class t_object>
bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob)
@@ -246,29 +211,10 @@ namespace cryptonote
crypto::hash get_tx_tree_hash(const std::vector<crypto::hash>& tx_hashes);
crypto::hash get_tx_tree_hash(const block& b);
bool is_valid_decomposed_amount(uint64_t amount);
+ void get_hash_stats(uint64_t &tx_hashes_calculated, uint64_t &tx_hashes_cached, uint64_t &block_hashes_calculated, uint64_t & block_hashes_cached);
#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \
CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \
specific_type& variable_name = boost::get<specific_type>(variant_var);
}
-
-BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0)
-
-namespace boost
-{
- namespace serialization
- {
- template <class Archive>
- inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver)
- {
- a & x.outputs;
- a & x.real_output;
- a & x.real_out_tx_key;
- a & x.real_output_in_tx_index;
- a & x.amount;
- a & x.rct;
- a & x.mask;
- }
- }
-}
diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_basic/cryptonote_stat_info.h
index d44904b6d..7ebf86878 100644
--- a/src/cryptonote_core/cryptonote_stat_info.h
+++ b/src/cryptonote_basic/cryptonote_stat_info.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp
index 1c5b9cfbd..863aa4359 100644
--- a/src/cryptonote_core/difficulty.cpp
+++ b/src/cryptonote_basic/difficulty.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_basic/difficulty.h
index 910f97035..aed6cb289 100644
--- a/src/cryptonote_core/difficulty.h
+++ b/src/cryptonote_basic/difficulty.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp
index 13d7d717d..546af2076 100644
--- a/src/cryptonote_core/hardfork.cpp
+++ b/src/cryptonote_basic/hardfork.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -29,7 +29,7 @@
#include <algorithm>
#include <cstdio>
-#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_basic.h"
#include "blockchain_db/blockchain_db.h"
#include "hardfork.h"
@@ -191,10 +191,10 @@ void HardFork::init()
}
catch (...) { populate = true; }
if (populate) {
- LOG_PRINT_L0("The DB has no hard fork info, reparsing from start");
+ MINFO("The DB has no hard fork info, reparsing from start");
height = 1;
}
- LOG_PRINT_L1("reorganizing from " << height);
+ MDEBUG("reorganizing from " << height);
if (populate) {
reorganize_from_chain_height(height);
// reorg will not touch the genesis block, use this as a flag for populating done
@@ -203,7 +203,7 @@ void HardFork::init()
else {
rescan_from_chain_height(height);
}
- LOG_PRINT_L1("reorganization done");
+ MDEBUG("reorganization done");
}
uint8_t HardFork::get_block_version(uint64_t height) const
@@ -222,7 +222,7 @@ bool HardFork::reorganize_from_block_height(uint64_t height)
return false;
db.set_batch_transactions(true);
- db.batch_start();
+ bool stop_batch = db.batch_start();
versions.clear();
@@ -250,7 +250,8 @@ bool HardFork::reorganize_from_block_height(uint64_t height)
add(db.get_block_from_height(h), h);
}
- db.batch_stop();
+ if (stop_batch)
+ db.batch_stop();
return true;
}
diff --git a/src/cryptonote_core/hardfork.h b/src/cryptonote_basic/hardfork.h
index 8d2edfa2a..6c6fbcb84 100644
--- a/src/cryptonote_core/hardfork.h
+++ b/src/cryptonote_basic/hardfork.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -29,7 +29,7 @@
#pragma once
#include "syncobj.h"
-#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_basic.h"
namespace cryptonote
{
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
new file mode 100644
index 000000000..eeb7b6094
--- /dev/null
+++ b/src/cryptonote_basic/miner.cpp
@@ -0,0 +1,853 @@
+// 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
+
+#include <sstream>
+#include <numeric>
+#include <boost/utility/value_init.hpp>
+#include <boost/interprocess/detail/atomic.hpp>
+#include <boost/limits.hpp>
+#include "misc_language.h"
+#include "include_base_utils.h"
+#include "cryptonote_basic_impl.h"
+#include "cryptonote_format_utils.h"
+#include "file_io_utils.h"
+#include "common/command_line.h"
+#include "string_coding.h"
+#include "storages/portable_storage_template_helper.h"
+#include "boost/logic/tribool.hpp"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "miner"
+
+using namespace epee;
+
+#include "miner.h"
+
+
+extern "C" void slow_hash_allocate_state();
+extern "C" void slow_hash_free_state();
+namespace cryptonote
+{
+
+ namespace
+ {
+ const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true};
+ const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true};
+ const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true};
+ const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable/disable background mining", true, true};
+ const command_line::arg_descriptor<bool> arg_bg_mining_ignore_battery = {"bg-mining-ignore-battery", "if true, assumes plugged in when unable to query system power status", false, true};
+ const command_line::arg_descriptor<uint64_t> arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true};
+ const command_line::arg_descriptor<uint8_t> arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true};
+ const command_line::arg_descriptor<uint8_t> arg_bg_mining_miner_target_percentage = {"bg-mining-miner-target", "Specificy maximum percentage cpu use by miner(s)", miner::BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE, true};
+ }
+
+
+ miner::miner(i_miner_handler* phandler):m_stop(1),
+ m_template(boost::value_initialized<block>()),
+ m_template_no(0),
+ m_diffic(0),
+ m_thread_index(0),
+ m_phandler(phandler),
+ m_height(0),
+ m_pausers_count(0),
+ m_threads_total(0),
+ m_starter_nonce(0),
+ m_last_hr_merge_time(0),
+ m_hashes(0),
+ m_do_print_hashrate(false),
+ m_do_mining(false),
+ m_current_hash_rate(0),
+ m_is_background_mining_enabled(false),
+ m_min_idle_seconds(BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS),
+ m_idle_threshold(BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE),
+ m_mining_target(BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE),
+ m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS)
+ {
+
+ }
+ //-----------------------------------------------------------------------------------------------------
+ miner::~miner()
+ {
+ stop();
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height)
+ {
+ CRITICAL_REGION_LOCAL(m_template_lock);
+ m_template = bl;
+ m_diffic = di;
+ m_height = height;
+ ++m_template_no;
+ m_starter_nonce = crypto::rand<uint32_t>();
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::on_block_chain_update()
+ {
+ if(!is_mining())
+ return true;
+
+ return request_block_template();
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::request_block_template()
+ {
+ block bl = AUTO_VAL_INIT(bl);
+ difficulty_type di = AUTO_VAL_INIT(di);
+ uint64_t height = AUTO_VAL_INIT(height);
+ uint64_t expected_reward; //only used for RPC calls - could possibly be useful here too?
+
+ cryptonote::blobdata extra_nonce;
+ if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size())
+ {
+ extra_nonce = m_extra_messages[m_config.current_extra_message_index];
+ }
+
+ if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce))
+ {
+ LOG_ERROR("Failed to get_block_template(), stopping mining");
+ return false;
+ }
+ set_block_template(bl, di, height);
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::on_idle()
+ {
+ m_update_block_template_interval.do_call([&](){
+ if(is_mining())request_block_template();
+ return true;
+ });
+
+ m_update_merge_hr_interval.do_call([&](){
+ merge_hr();
+ return true;
+ });
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::do_print_hashrate(bool do_hr)
+ {
+ m_do_print_hashrate = do_hr;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::merge_hr()
+ {
+ if(m_last_hr_merge_time && is_mining())
+ {
+ m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1));
+ CRITICAL_REGION_LOCAL(m_last_hash_rates_lock);
+ m_last_hash_rates.push_back(m_current_hash_rate);
+ if(m_last_hash_rates.size() > 19)
+ m_last_hash_rates.pop_front();
+ if(m_do_print_hashrate)
+ {
+ uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0);
+ float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size());
+ std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL;
+ }
+ }
+ m_last_hr_merge_time = misc_utils::get_tick_count();
+ m_hashes = 0;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::init_options(boost::program_options::options_description& desc)
+ {
+ command_line::add_arg(desc, arg_extra_messages);
+ command_line::add_arg(desc, arg_start_mining);
+ command_line::add_arg(desc, arg_mining_threads);
+ command_line::add_arg(desc, arg_bg_mining_enable);
+ command_line::add_arg(desc, arg_bg_mining_ignore_battery);
+ command_line::add_arg(desc, arg_bg_mining_min_idle_interval_seconds);
+ command_line::add_arg(desc, arg_bg_mining_idle_threshold_percentage);
+ command_line::add_arg(desc, arg_bg_mining_miner_target_percentage);
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::init(const boost::program_options::variables_map& vm, bool testnet)
+ {
+ if(command_line::has_arg(vm, arg_extra_messages))
+ {
+ std::string buff;
+ bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages));
+ std::vector<std::string> extra_vec;
+ boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on );
+ m_extra_messages.resize(extra_vec.size());
+ for(size_t i = 0; i != extra_vec.size(); i++)
+ {
+ string_tools::trim(extra_vec[i]);
+ if(!extra_vec[i].size())
+ continue;
+ std::string buff = string_encoding::base64_decode(extra_vec[i]);
+ if(buff != "0")
+ m_extra_messages[i] = buff;
+ }
+ m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string();
+ m_config = AUTO_VAL_INIT(m_config);
+ epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME);
+ MINFO("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index);
+ }
+
+ if(command_line::has_arg(vm, arg_start_mining))
+ {
+ if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining)))
+ {
+ LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled");
+ return false;
+ }
+ m_threads_total = 1;
+ m_do_mining = true;
+ if(command_line::has_arg(vm, arg_mining_threads))
+ {
+ m_threads_total = command_line::get_arg(vm, arg_mining_threads);
+ }
+ }
+
+ // Background mining parameters
+ // Let init set all parameters even if background mining is not enabled, they can start later with params set
+ if(command_line::has_arg(vm, arg_bg_mining_enable))
+ set_is_background_mining_enabled( command_line::get_arg(vm, arg_bg_mining_enable) );
+ if(command_line::has_arg(vm, arg_bg_mining_ignore_battery))
+ set_ignore_battery( command_line::get_arg(vm, arg_bg_mining_ignore_battery) );
+ if(command_line::has_arg(vm, arg_bg_mining_min_idle_interval_seconds))
+ set_min_idle_seconds( command_line::get_arg(vm, arg_bg_mining_min_idle_interval_seconds) );
+ if(command_line::has_arg(vm, arg_bg_mining_idle_threshold_percentage))
+ set_idle_threshold( command_line::get_arg(vm, arg_bg_mining_idle_threshold_percentage) );
+ if(command_line::has_arg(vm, arg_bg_mining_miner_target_percentage))
+ set_mining_target( command_line::get_arg(vm, arg_bg_mining_miner_target_percentage) );
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::is_mining() const
+ {
+ return !m_stop;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ const account_public_address& miner::get_mining_address() const
+ {
+ return m_mine_address;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ uint32_t miner::get_threads_count() const {
+ return m_threads_total;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background, bool ignore_battery)
+ {
+ m_mine_address = adr;
+ m_threads_total = static_cast<uint32_t>(threads_count);
+ m_starter_nonce = crypto::rand<uint32_t>();
+ CRITICAL_REGION_LOCAL(m_threads_lock);
+ if(is_mining())
+ {
+ LOG_ERROR("Starting miner but it's already started");
+ return false;
+ }
+
+ if(!m_threads.empty())
+ {
+ LOG_ERROR("Unable to start miner because there are active mining threads");
+ return false;
+ }
+
+ if(!m_template_no)
+ request_block_template();//lets update block template
+
+ boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0);
+ boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0);
+ set_is_background_mining_enabled(do_background);
+ set_ignore_battery(ignore_battery);
+
+ for(size_t i = 0; i != threads_count; i++)
+ {
+ m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this)));
+ }
+
+ LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" );
+
+ if( get_is_background_mining_enabled() )
+ {
+ m_background_mining_thread = boost::thread(attrs, boost::bind(&miner::background_worker_thread, this));
+ LOG_PRINT_L0("Background mining controller thread started" );
+ }
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ uint64_t miner::get_speed() const
+ {
+ if(is_mining()) {
+ return m_current_hash_rate;
+ }
+ else {
+ return 0;
+ }
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::send_stop_signal()
+ {
+ boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1);
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::stop()
+ {
+ MTRACE("Miner has received stop signal");
+
+ if (!is_mining())
+ {
+ MDEBUG("Not mining - nothing to stop" );
+ return true;
+ }
+
+ send_stop_signal();
+ CRITICAL_REGION_LOCAL(m_threads_lock);
+
+ // In case background mining was active and the miner threads are waiting
+ // on the background miner to signal start.
+ m_is_background_mining_started_cond.notify_all();
+
+ for(boost::thread& th: m_threads)
+ th.join();
+
+ // The background mining thread could be sleeping for a long time, so we
+ // interrupt it just in case
+ m_background_mining_thread.interrupt();
+ m_background_mining_thread.join();
+
+ MINFO("Mining has been stopped, " << m_threads.size() << " finished" );
+ m_threads.clear();
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height)
+ {
+ for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++)
+ {
+ crypto::hash h;
+ get_block_longhash(bl, h, height);
+
+ if(check_hash(h, diffic))
+ {
+ bl.invalidate_hashes();
+ return true;
+ }
+ }
+ bl.invalidate_hashes();
+ return false;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::on_synchronized()
+ {
+ if(m_do_mining)
+ {
+ boost::thread::attributes attrs;
+ attrs.set_stack_size(THREAD_STACK_SIZE);
+
+ start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled());
+ }
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::pause()
+ {
+ CRITICAL_REGION_LOCAL(m_miners_count_lock);
+ MDEBUG("miner::pause: " << m_pausers_count << " -> " << (m_pausers_count + 1));
+ ++m_pausers_count;
+ if(m_pausers_count == 1 && is_mining())
+ MDEBUG("MINING PAUSED");
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::resume()
+ {
+ CRITICAL_REGION_LOCAL(m_miners_count_lock);
+ MDEBUG("miner::resume: " << m_pausers_count << " -> " << (m_pausers_count - 1));
+ --m_pausers_count;
+ if(m_pausers_count < 0)
+ {
+ m_pausers_count = 0;
+ MERROR("Unexpected miner::resume() called");
+ }
+ if(!m_pausers_count && is_mining())
+ MDEBUG("MINING RESUMED");
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::worker_thread()
+ {
+ uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index);
+ MGINFO("Miner thread was started ["<< th_local_index << "]");
+ MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]");
+ uint32_t nonce = m_starter_nonce + th_local_index;
+ uint64_t height = 0;
+ difficulty_type local_diff = 0;
+ uint32_t local_template_ver = 0;
+ block b;
+ slow_hash_allocate_state();
+ while(!m_stop)
+ {
+ if(m_pausers_count)//anti split workaround
+ {
+ misc_utils::sleep_no_w(100);
+ continue;
+ }
+ else if( m_is_background_mining_enabled )
+ {
+ misc_utils::sleep_no_w(m_miner_extra_sleep);
+ while( !m_is_background_mining_started )
+ {
+ MGINFO("background mining is enabled, but not started, waiting until start triggers");
+ boost::unique_lock<boost::mutex> started_lock( m_is_background_mining_started_mutex );
+ m_is_background_mining_started_cond.wait( started_lock );
+ if( m_stop ) break;
+ }
+
+ if( m_stop ) continue;
+ }
+
+ if(local_template_ver != m_template_no)
+ {
+ CRITICAL_REGION_BEGIN(m_template_lock);
+ b = m_template;
+ local_diff = m_diffic;
+ height = m_height;
+ CRITICAL_REGION_END();
+ local_template_ver = m_template_no;
+ nonce = m_starter_nonce + th_local_index;
+ }
+
+ if(!local_template_ver)//no any set_block_template call
+ {
+ LOG_PRINT_L2("Block template not set yet");
+ epee::misc_utils::sleep_no_w(1000);
+ continue;
+ }
+
+ b.nonce = nonce;
+ crypto::hash h;
+ get_block_longhash(b, h, height);
+
+ if(check_hash(h, local_diff))
+ {
+ //we lucky!
+ ++m_config.current_extra_message_index;
+ MGINFO_GREEN("Found block for difficulty: " << local_diff);
+ if(!m_phandler->handle_block_found(b))
+ {
+ --m_config.current_extra_message_index;
+ }else
+ {
+ //success update, lets update config
+ if (!m_config_folder_path.empty())
+ epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME);
+ }
+ }
+ nonce+=m_threads_total;
+ ++m_hashes;
+ }
+ slow_hash_free_state();
+ MGINFO("Miner thread stopped ["<< th_local_index << "]");
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::get_is_background_mining_enabled() const
+ {
+ return m_is_background_mining_enabled;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::get_ignore_battery() const
+ {
+ return m_ignore_battery;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ /**
+ * This has differing behaviour depending on if mining has been started/etc.
+ * Note: add documentation
+ */
+ bool miner::set_is_background_mining_enabled(bool is_background_mining_enabled)
+ {
+ m_is_background_mining_enabled = is_background_mining_enabled;
+ // Extra logic will be required if we make this function public in the future
+ // and allow toggling smart mining without start/stop
+ //m_is_background_mining_enabled_cond.notify_one();
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ void miner::set_ignore_battery(bool ignore_battery)
+ {
+ m_ignore_battery = ignore_battery;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ uint64_t miner::get_min_idle_seconds() const
+ {
+ return m_min_idle_seconds;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::set_min_idle_seconds(uint64_t min_idle_seconds)
+ {
+ if(min_idle_seconds > BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS) return false;
+ if(min_idle_seconds < BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS) return false;
+ m_min_idle_seconds = min_idle_seconds;
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ uint8_t miner::get_idle_threshold() const
+ {
+ return m_idle_threshold;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::set_idle_threshold(uint8_t idle_threshold)
+ {
+ if(idle_threshold > BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE) return false;
+ if(idle_threshold < BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE) return false;
+ m_idle_threshold = idle_threshold;
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ uint8_t miner::get_mining_target() const
+ {
+ return m_mining_target;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::set_mining_target(uint8_t mining_target)
+ {
+ if(mining_target > BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE) return false;
+ if(mining_target < BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE) return false;
+ m_mining_target = mining_target;
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::background_worker_thread()
+ {
+ uint64_t prev_total_time, current_total_time;
+ uint64_t prev_idle_time, current_idle_time;
+ uint64_t previous_process_time = 0, current_process_time = 0;
+ m_is_background_mining_started = false;
+
+ if(!get_system_times(prev_total_time, prev_idle_time))
+ {
+ LOG_ERROR("get_system_times call failed, background mining will NOT work!");
+ return false;
+ }
+
+ while(!m_stop)
+ {
+
+ try
+ {
+ // Commenting out the below since we're going with privatizing the bg mining enabled
+ // function, but I'll leave the code/comments here for anyone that wants to modify the
+ // patch in the future
+ // -------------------------------------------------------------------------------------
+ // All of this might be overkill if we just enforced some simple requirements
+ // about changing this variable before/after the miner starts, but I envision
+ // in the future a checkbox that you can tick on/off for background mining after
+ // you've clicked "start mining". There's still an issue here where if background
+ // mining is disabled when start is called, this thread is never created, and so
+ // enabling after does nothing, something I have to fix in the future. However,
+ // this should take care of the case where mining is started with bg-enabled,
+ // and then the user decides to un-check background mining, and just do
+ // regular full-speed mining. I might just be over-doing it and thinking up
+ // non-existant use-cases, so if the concensus is to simplify, we can remove all this fluff.
+ /*
+ while( !m_is_background_mining_enabled )
+ {
+ MGINFO("background mining is disabled, waiting until enabled!");
+ boost::unique_lock<boost::mutex> enabled_lock( m_is_background_mining_enabled_mutex );
+ m_is_background_mining_enabled_cond.wait( enabled_lock );
+ }
+ */
+
+ // If we're already mining, then sleep for the miner monitor interval.
+ // If we're NOT mining, then sleep for the idle monitor interval
+ uint64_t sleep_for_seconds = BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS;
+ if( !m_is_background_mining_started ) sleep_for_seconds = get_min_idle_seconds();
+ boost::this_thread::sleep_for(boost::chrono::seconds(sleep_for_seconds));
+ }
+ catch(const boost::thread_interrupted&)
+ {
+ MDEBUG("background miner thread interrupted ");
+ continue; // if interrupted because stop called, loop should end ..
+ }
+
+ boost::tribool battery_powered(on_battery_power());
+ bool on_ac_power = false;
+ if(indeterminate( battery_powered ))
+ {
+ // name could be better, only ignores battery requirement if we failed
+ // to get the status of the system
+ if( m_ignore_battery )
+ {
+ on_ac_power = true;
+ }
+ }
+ else
+ {
+ on_ac_power = !battery_powered;
+ }
+
+ if( m_is_background_mining_started )
+ {
+ // figure out if we need to stop, and monitor mining usage
+
+ // If we get here, then previous values are initialized.
+ // Let's get some current data for comparison.
+
+ if(!get_system_times(current_total_time, current_idle_time))
+ {
+ MERROR("get_system_times call failed");
+ continue;
+ }
+
+ if(!get_process_time(current_process_time))
+ {
+ MERROR("get_process_time call failed!");
+ continue;
+ }
+
+ uint64_t total_diff = (current_total_time - prev_total_time);
+ uint64_t idle_diff = (current_idle_time - prev_idle_time);
+ uint64_t process_diff = (current_process_time - previous_process_time);
+ uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff);
+ uint8_t process_percentage = get_percent_of_total(process_diff, total_diff);
+
+ MGINFO("idle percentage is " << unsigned(idle_percentage) << "\%, miner percentage is " << unsigned(process_percentage) << "\%, ac power : " << on_ac_power);
+ if( idle_percentage + process_percentage < get_idle_threshold() || !on_ac_power )
+ {
+ MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining stopping, thanks for your contribution!");
+ m_is_background_mining_started = false;
+
+ // reset process times
+ previous_process_time = 0;
+ current_process_time = 0;
+ }
+ else
+ {
+ previous_process_time = current_process_time;
+
+ // adjust the miner extra sleep variable
+ int64_t miner_extra_sleep_change = (-1 * (get_mining_target() - process_percentage) );
+ int64_t new_miner_extra_sleep = m_miner_extra_sleep + miner_extra_sleep_change;
+ // if you start the miner with few threads on a multicore system, this could
+ // fall below zero because all the time functions aggregate across all processors.
+ // I'm just hard limiting to 5 millis min sleep here, other options?
+ m_miner_extra_sleep = std::max( new_miner_extra_sleep , (int64_t)5 );
+ MDEBUG("m_miner_extra_sleep " << m_miner_extra_sleep);
+ }
+
+ prev_total_time = current_total_time;
+ prev_idle_time = current_idle_time;
+ }
+ else if( on_ac_power )
+ {
+ // figure out if we need to start
+
+ if(!get_system_times(current_total_time, current_idle_time))
+ {
+ MERROR("get_system_times call failed");
+ continue;
+ }
+
+ uint64_t total_diff = (current_total_time - prev_total_time);
+ uint64_t idle_diff = (current_idle_time - prev_idle_time);
+ uint8_t idle_percentage = get_percent_of_total(idle_diff, total_diff);
+
+ MGINFO("idle percentage is " << unsigned(idle_percentage));
+ if( idle_percentage >= get_idle_threshold() && on_ac_power )
+ {
+ MGINFO("cpu is " << unsigned(idle_percentage) << "% idle, idle threshold is " << unsigned(get_idle_threshold()) << "\%, ac power : " << on_ac_power << ", background mining started, good luck!");
+ m_is_background_mining_started = true;
+ m_is_background_mining_started_cond.notify_all();
+
+ // Wait for a little mining to happen ..
+ boost::this_thread::sleep_for(boost::chrono::seconds( 1 ));
+
+ // Starting data ...
+ if(!get_process_time(previous_process_time))
+ {
+ m_is_background_mining_started = false;
+ MERROR("get_process_time call failed!");
+ }
+ }
+
+ prev_total_time = current_total_time;
+ prev_idle_time = current_idle_time;
+ }
+ }
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::get_system_times(uint64_t& total_time, uint64_t& idle_time)
+ {
+ #ifdef _WIN32
+
+ FILETIME idleTime;
+ FILETIME kernelTime;
+ FILETIME userTime;
+ if ( GetSystemTimes( &idleTime, &kernelTime, &userTime ) != -1 )
+ {
+ total_time =
+ ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) )
+ + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) );
+
+ idle_time = ( (((uint64_t)(idleTime.dwHighDateTime)) << 32) | ((uint64_t)idleTime.dwLowDateTime) );
+
+ return true;
+ }
+
+ #elif defined(__linux__)
+
+ const std::string STR_CPU("cpu");
+ const std::size_t STR_CPU_LEN = STR_CPU.size();
+ const std::string STAT_FILE_PATH = "/proc/stat";
+
+ if( !epee::file_io_utils::is_file_exist(STAT_FILE_PATH) )
+ {
+ LOG_ERROR("'" << STAT_FILE_PATH << "' file does not exist");
+ return false;
+ }
+
+ std::ifstream stat_file_stream(STAT_FILE_PATH);
+ if( stat_file_stream.fail() )
+ {
+ LOG_ERROR("failed to open '" << STAT_FILE_PATH << "'");
+ return false;
+ }
+
+ std::string line;
+ std::getline(stat_file_stream, line);
+ std::istringstream stat_file_iss(line);
+ stat_file_iss.ignore(65536, ' '); // skip cpu label ...
+ uint64_t utime, ntime, stime, itime;
+ if( !(stat_file_iss >> utime && stat_file_iss >> ntime && stat_file_iss >> stime && stat_file_iss >> itime) )
+ {
+ LOG_ERROR("failed to read '" << STAT_FILE_PATH << "'");
+ return false;
+ }
+
+ idle_time = itime;
+ total_time = utime + ntime + stime + itime;
+
+ return true;
+
+ #endif
+
+ return false; // unsupported systemm..
+ }
+ //-----------------------------------------------------------------------------------------------------
+ bool miner::get_process_time(uint64_t& total_time)
+ {
+ #ifdef _WIN32
+
+ FILETIME createTime;
+ FILETIME exitTime;
+ FILETIME kernelTime;
+ FILETIME userTime;
+ if ( GetProcessTimes( GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime ) != -1 )
+ {
+ total_time =
+ ( (((uint64_t)(kernelTime.dwHighDateTime)) << 32) | ((uint64_t)kernelTime.dwLowDateTime) )
+ + ( (((uint64_t)(userTime.dwHighDateTime)) << 32) | ((uint64_t)userTime.dwLowDateTime) );
+
+ return true;
+ }
+
+ #elif defined(__linux__) && defined(_SC_CLK_TCK)
+
+ struct tms tms;
+ if ( times(&tms) != (clock_t)-1 )
+ {
+ total_time = tms.tms_utime + tms.tms_stime;
+ return true;
+ }
+
+ #endif
+
+ return false; // unsupported system..
+ }
+ //-----------------------------------------------------------------------------------------------------
+ uint8_t miner::get_percent_of_total(uint64_t other, uint64_t total)
+ {
+ return (uint8_t)( ceil( (other * 1.f / total * 1.f) * 100) );
+ }
+ //-----------------------------------------------------------------------------------------------------
+ boost::logic::tribool miner::on_battery_power()
+ {
+ #ifdef _WIN32
+
+ SYSTEM_POWER_STATUS power_status;
+ if ( GetSystemPowerStatus( &power_status ) != 0 )
+ {
+ return boost::logic::tribool(power_status.ACLineStatus != 1);
+ }
+
+ #elif defined(__linux__)
+
+ // i've only tested on UBUNTU, these paths might be different on other systems
+ // need to figure out a way to make this more flexible
+ std::string power_supply_path = "";
+ const std::string POWER_SUPPLY_STATUS_PATHS[] =
+ {
+ "/sys/class/power_supply/ACAD/online",
+ "/sys/class/power_supply/AC/online",
+ "/sys/class/power_supply/AC0/online",
+ "/sys/class/power_supply/ADP0/online"
+ };
+
+ for(const std::string& path : POWER_SUPPLY_STATUS_PATHS)
+ {
+ if( epee::file_io_utils::is_file_exist(path) )
+ {
+ power_supply_path = path;
+ break;
+ }
+ }
+
+ if( power_supply_path.empty() )
+ {
+ LOG_ERROR("Couldn't find battery/power status file, can't determine if plugged in!");
+ return boost::logic::tribool(boost::logic::indeterminate);;
+ }
+
+ std::ifstream power_stream(power_supply_path);
+ if( power_stream.fail() )
+ {
+ LOG_ERROR("failed to open '" << power_supply_path << "'");
+ return boost::logic::tribool(boost::logic::indeterminate);;
+ }
+
+ return boost::logic::tribool( (power_stream.get() != '1') );
+
+ #endif
+
+ LOG_ERROR("couldn't query power status");
+ return boost::logic::tribool(boost::logic::indeterminate);
+ }
+}
diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_basic/miner.h
index f24f6e960..964ee6a36 100644
--- a/src/cryptonote_core/miner.h
+++ b/src/cryptonote_basic/miner.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -31,11 +31,19 @@
#pragma once
#include <boost/program_options.hpp>
+#include <boost/logic/tribool_fwd.hpp>
#include <atomic>
#include "cryptonote_basic.h"
#include "difficulty.h"
#include "math_helper.h"
-
+#ifdef _WIN32
+#include <windows.h>
+#elif defined(__linux__)
+#include <unistd.h>
+#include <sys/resource.h>
+#include <sys/times.h>
+#include <time.h>
+#endif
namespace cryptonote
{
@@ -43,7 +51,7 @@ namespace cryptonote
struct i_miner_handler
{
virtual bool handle_block_found(block& b) = 0;
- virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0;
+ virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) = 0;
protected:
~i_miner_handler(){};
};
@@ -60,7 +68,7 @@ namespace cryptonote
static void init_options(boost::program_options::options_description& desc);
bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height);
bool on_block_chain_update();
- bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs);
+ bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false, bool ignore_battery = false);
uint64_t get_speed() const;
uint32_t get_threads_count() const;
void send_stop_signal();
@@ -74,6 +82,27 @@ namespace cryptonote
void pause();
void resume();
void do_print_hashrate(bool do_hr);
+ bool get_is_background_mining_enabled() const;
+ bool get_ignore_battery() const;
+ uint64_t get_min_idle_seconds() const;
+ bool set_min_idle_seconds(uint64_t min_idle_seconds);
+ uint8_t get_idle_threshold() const;
+ bool set_idle_threshold(uint8_t idle_threshold);
+ uint8_t get_mining_target() const;
+ bool set_mining_target(uint8_t mining_target);
+
+ static constexpr uint8_t BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE = 90;
+ static constexpr uint8_t BACKGROUND_MINING_MIN_IDLE_THRESHOLD_PERCENTAGE = 50;
+ static constexpr uint8_t BACKGROUND_MINING_MAX_IDLE_THRESHOLD_PERCENTAGE = 99;
+ static constexpr uint16_t BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS = 10;
+ static constexpr uint16_t BACKGROUND_MINING_MIN_MIN_IDLE_INTERVAL_IN_SECONDS = 10;
+ static constexpr uint16_t BACKGROUND_MINING_MAX_MIN_IDLE_INTERVAL_IN_SECONDS = 3600;
+ static constexpr uint8_t BACKGROUND_MINING_DEFAULT_MINING_TARGET_PERCENTAGE = 40;
+ static constexpr uint8_t BACKGROUND_MINING_MIN_MINING_TARGET_PERCENTAGE = 5;
+ static constexpr uint8_t BACKGROUND_MINING_MAX_MINING_TARGET_PERCENTAGE = 50;
+ static constexpr uint8_t BACKGROUND_MINING_MINER_MONITOR_INVERVAL_IN_SECONDS = 10;
+ static constexpr uint64_t BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS = 400; // ramp up
+ static constexpr uint64_t BACKGROUND_MINING_MIN_MINER_EXTRA_SLEEP_MILLIS = 5;
private:
bool worker_thread();
@@ -119,8 +148,26 @@ namespace cryptonote
bool m_do_print_hashrate;
bool m_do_mining;
+ // background mining stuffs ..
+
+ bool set_is_background_mining_enabled(bool is_background_mining_enabled);
+ void set_ignore_battery(bool ignore_battery);
+ bool background_worker_thread();
+ std::atomic<bool> m_is_background_mining_enabled;
+ bool m_ignore_battery;
+ boost::mutex m_is_background_mining_enabled_mutex;
+ boost::condition_variable m_is_background_mining_enabled_cond;
+ std::atomic<bool> m_is_background_mining_started;
+ boost::mutex m_is_background_mining_started_mutex;
+ boost::condition_variable m_is_background_mining_started_cond;
+ boost::thread m_background_mining_thread;
+ uint64_t m_min_idle_seconds;
+ uint8_t m_idle_threshold;
+ uint8_t m_mining_target;
+ std::atomic<uint64_t> m_miner_extra_sleep;
+ static bool get_system_times(uint64_t& total_time, uint64_t& idle_time);
+ static bool get_process_time(uint64_t& total_time);
+ static uint8_t get_percent_of_total(uint64_t some_time, uint64_t total_time);
+ static boost::logic::tribool on_battery_power();
};
}
-
-
-
diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_basic/tx_extra.h
index 6f5fbe466..5a6c3176d 100644
--- a/src/cryptonote_core/tx_extra.h
+++ b/src/cryptonote_basic/tx_extra.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_basic/verification_context.h
index 0bb635e84..ce885ec1d 100644
--- a/src/cryptonote_core/verification_context.h
+++ b/src/cryptonote_basic/verification_context.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 3f15cee3f..8dbf1b53b 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -57,6 +57,7 @@
#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 //size of block (bytes) after which reward for block calculated using block size - before first fork
+#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5
#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600
#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12
// COIN - number of smallest units in one coin
@@ -66,6 +67,7 @@
#define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9)
#define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9)
#define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12)
+#define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5)
#define ORPHANED_BLOCKS_MAX_COUNT 100
@@ -129,6 +131,10 @@
#define THREAD_STACK_SIZE 5 * 1024 * 1024
#define HF_VERSION_DYNAMIC_FEE 4
+#define HF_VERSION_MIN_MIXIN_4 6
+#define HF_VERSION_ENFORCE_RCT 6
+
+#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
// New constants are intended to go here
namespace config
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index ad1745b7f..5944ddcd1 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -27,38 +27,19 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(cryptonote_core_sources
- account.cpp
blockchain.cpp
- checkpoints.cpp
- cryptonote_basic_impl.cpp
cryptonote_core.cpp
- cryptonote_format_utils.cpp
- difficulty.cpp
- miner.cpp
tx_pool.cpp
- hardfork.cpp)
+ cryptonote_tx_utils.cpp)
set(cryptonote_core_headers)
set(cryptonote_core_private_headers
- account.h
- account_boost_serialization.h
blockchain_storage_boost_serialization.h
blockchain.h
- checkpoints.h
- connection_context.h
- cryptonote_basic.h
- cryptonote_basic_impl.h
- cryptonote_boost_serialization.h
cryptonote_core.h
- cryptonote_format_utils.h
- cryptonote_stat_info.h
- difficulty.h
- miner.h
- tx_extra.h
tx_pool.h
- verification_context.h
- hardfork.h)
+ cryptonote_tx_utils.h)
if(PER_BLOCK_CHECKPOINT)
set(Blocks "blocks")
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index b344c5597..74c5b3b83 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -34,14 +34,13 @@
#include <boost/range/adaptor/reversed.hpp>
#include "include_base_utils.h"
-#include "cryptonote_basic_impl.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "tx_pool.h"
#include "blockchain.h"
#include "blockchain_db/blockchain_db.h"
-#include "cryptonote_format_utils.h"
-#include "cryptonote_boost_serialization.h"
+#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
-#include "miner.h"
+#include "cryptonote_basic/miner.h"
#include "misc_language.h"
#include "profile_tools.h"
#include "file_io_utils.h"
@@ -49,8 +48,8 @@
#include "common/boost_serialization_helper.h"
#include "warnings.h"
#include "crypto/hash.h"
-#include "cryptonote_core/checkpoints.h"
-#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_basic/checkpoints.h"
+#include "cryptonote_core.h"
#include "ringct/rctSigs.h"
#include "common/perf_timer.h"
#if defined(PER_BLOCK_CHECKPOINT)
@@ -98,8 +97,8 @@ static const struct {
// version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18.
{ 4, 1220516, 0, 1483574400 },
- // version 5 starts from block 1406997, which is on or around the 20th of September, 2017. Fork time finalised on 2016-09-18.
- { 5, 1406997, 0, 1505865600 },
+ // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14.
+ { 5, 1288616, 0, 1489520158 },
};
static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
@@ -118,7 +117,7 @@ static const struct {
// versions 3-5 were passed in rapid succession from September 18th, 2016
{ 3, 800500, 0, 1472415034 },
{ 4, 801219, 0, 1472415035 },
- { 5, 802660, 0, 1472415036 },
+ { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
@@ -1073,7 +1072,7 @@ uint64_t Blockchain::get_current_cumulative_blocksize_limit() const
// in a lot of places. That flag is not referenced in any of the code
// nor any of the makefiles, howeve. Need to look into whether or not it's
// necessary at all.
-bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce)
+bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_size;
@@ -1097,7 +1096,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
size_t txs_size;
uint64_t fee;
- if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, m_hardfork->get_current_version()))
+ if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version()))
{
return false;
}
@@ -1432,7 +1431,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return true;
}
//------------------------------------------------------------------
-bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const
+bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1444,17 +1443,17 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block
return false;
}
- for(const block& blk : blocks)
+ for(const auto& blk : blocks)
{
std::list<crypto::hash> missed_ids;
- get_transactions(blk.tx_hashes, txs, missed_ids);
+ get_transactions_blobs(blk.second.tx_hashes, txs, missed_ids);
CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "has missed transactions in own block in main blockchain");
}
return true;
}
//------------------------------------------------------------------
-bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
+bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1463,7 +1462,12 @@ bool Blockchain::get_blocks(uint64_t start_offset, size_t count, std::list<block
for(size_t i = start_offset; i < start_offset + count && i < m_db->height();i++)
{
- blocks.push_back(m_db->get_block_from_height(i));
+ blocks.push_back(std::make_pair(m_db->get_block_blob_from_height(i), block()));
+ if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second))
+ {
+ LOG_ERROR("Invalid block");
+ return false;
+ }
}
return true;
}
@@ -1481,22 +1485,22 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
CRITICAL_REGION_LOCAL(m_blockchain_lock);
m_db->block_txn_start(true);
rsp.current_blockchain_height = get_current_blockchain_height();
- std::list<block> blocks;
+ std::list<std::pair<cryptonote::blobdata,block>> blocks;
get_blocks(arg.blocks, blocks, rsp.missed_ids);
for (const auto& bl: blocks)
{
std::list<crypto::hash> missed_tx_ids;
- std::list<transaction> txs;
+ std::list<cryptonote::blobdata> txs;
// FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids
// is for missed blocks, not missed transactions as well.
- get_transactions(bl.tx_hashes, txs, missed_tx_ids);
+ get_transactions_blobs(bl.second.tx_hashes, txs, missed_tx_ids);
if (missed_tx_ids.size() != 0)
{
LOG_ERROR("Error retrieving blocks, missed " << missed_tx_ids.size()
- << " transactions for block with hash: " << get_block_hash(bl)
+ << " transactions for block with hash: " << get_block_hash(bl.second)
<< std::endl
);
@@ -1511,17 +1515,17 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
rsp.blocks.push_back(block_complete_entry());
block_complete_entry& e = rsp.blocks.back();
//pack block
- e.block = t_serializable_object_to_blob(bl);
+ e.block = bl.first;
//pack transactions
- for (transaction& tx: txs)
- e.txs.push_back(t_serializable_object_to_blob(tx));
+ for (const cryptonote::blobdata& tx: txs)
+ e.txs.push_back(tx);
}
//get another transactions, if need
- std::list<transaction> txs;
- get_transactions(arg.txs, txs, rsp.missed_ids);
+ std::list<cryptonote::blobdata> txs;
+ get_transactions_blobs(arg.txs, txs, rsp.missed_ids);
//pack aside transactions
for (const auto& tx: txs)
- rsp.txs.push_back(t_serializable_object_to_blob(tx));
+ rsp.txs.push_back(tx);
m_db->block_txn_stop();
return true;
@@ -1890,7 +1894,12 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container
{
try
{
- blocks.push_back(m_db->get_block(block_hash));
+ blocks.push_back(std::make_pair(m_db->get_block_blob(block_hash), block()));
+ if (!parse_and_validate_block_from_blob(blocks.back().first, blocks.back().second))
+ {
+ LOG_ERROR("Invalid block");
+ return false;
+ }
}
catch (const BLOCK_DNE& e)
{
@@ -1907,7 +1916,7 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container
//TODO: return type should be void, throw on exception
// alternatively, return true only if no transactions missed
template<class t_ids_container, class t_tx_container, class t_missed_container>
-bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
+bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1916,8 +1925,8 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
{
try
{
- transaction tx;
- if (m_db->get_tx(tx_hash, tx))
+ cryptonote::blobdata tx;
+ if (m_db->get_tx_blob(tx_hash, tx))
txs.push_back(std::move(tx));
else
missed_txs.push_back(tx_hash);
@@ -1930,6 +1939,37 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
return true;
}
//------------------------------------------------------------------
+template<class t_ids_container, class t_tx_container, class t_missed_container>
+bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ for (const auto& tx_hash : txs_ids)
+ {
+ try
+ {
+ cryptonote::blobdata tx;
+ if (m_db->get_tx_blob(tx_hash, tx))
+ {
+ txs.push_back(transaction());
+ if (!parse_and_validate_tx_from_blob(tx, txs.back()))
+ {
+ LOG_ERROR("Invalid transaction");
+ return false;
+ }
+ }
+ else
+ missed_txs.push_back(tx_hash);
+ }
+ catch (const std::exception& e)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
void Blockchain::print_blockchain(uint64_t start_index, uint64_t end_index) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1987,12 +2027,14 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
return false;
}
+ m_db->block_txn_start(true);
resp.total_height = get_current_blockchain_height();
size_t count = 0;
for(size_t i = resp.start_height; i < resp.total_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
{
resp.m_block_ids.push_back(m_db->get_block_hash_from_height(i));
}
+ m_db->block_txn_stop();
return true;
}
//------------------------------------------------------------------
@@ -2000,7 +2042,7 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
// find split point between ours and foreign blockchain (or start at
// blockchain height <req_start_block>), and return up to max_count FULL
// blocks by reference.
-bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
+bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2023,16 +2065,20 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
}
}
+ m_db->block_txn_start(true);
total_height = get_current_blockchain_height();
size_t count = 0;
for(size_t i = start_height; i < total_height && count < max_count; i++, count++)
{
blocks.resize(blocks.size()+1);
- blocks.back().first = m_db->get_block_from_height(i);
+ blocks.back().first = m_db->get_block_blob_from_height(i);
+ block b;
+ CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first, b), false, "internal error, invalid block");
std::list<crypto::hash> mis;
- get_transactions(blocks.back().first.tx_hashes, blocks.back().second, mis);
+ get_transactions_blobs(b.tx_hashes, blocks.back().second, mis);
CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
}
+ m_db->block_txn_stop();
return true;
}
//------------------------------------------------------------------
@@ -2380,7 +2426,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
size_t n_unmixable = 0, n_mixable = 0;
size_t mixin = std::numeric_limits<size_t>::max();
- const size_t min_mixin = hf_version >= 5 ? 4 : 2;
+ const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2;
for (const auto& txin : tx.vin)
{
// non txin_to_key inputs will be rejected below
@@ -2433,7 +2479,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
tvc.m_verifivation_failed = true;
return false;
}
- const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= 5) ? 2 : 1);
+ const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= HF_VERSION_ENFORCE_RCT) ? 2 : 1);
if (tx.version < min_tx_version)
{
MERROR_VER("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version);
@@ -2750,21 +2796,43 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const
}
//------------------------------------------------------------------
-uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size)
+static uint64_t get_fee_quantization_mask()
{
- if (median_block_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2)
- median_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2;
+ static uint64_t mask = 0;
+ if (mask == 0)
+ {
+ mask = 1;
+ for (size_t n = PER_KB_FEE_QUANTIZATION_DECIMALS; n < CRYPTONOTE_DISPLAY_DECIMAL_POINT; ++n)
+ mask *= 10;
+ }
+ return mask;
+}
+
+//------------------------------------------------------------------
+uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version)
+{
+ const uint64_t min_block_size = get_min_block_size(version);
+ const uint64_t fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE;
+
+ if (median_block_size < min_block_size)
+ median_block_size = min_block_size;
- uint64_t unscaled_fee_per_kb = (DYNAMIC_FEE_PER_KB_BASE_FEE * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / median_block_size);
+ uint64_t unscaled_fee_per_kb = (fee_per_kb_base * min_block_size / median_block_size);
uint64_t hi, lo = mul128(unscaled_fee_per_kb, block_reward, &hi);
static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000");
static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large");
+
// divide in two steps, since the divisor must be 32 bits, but DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD isn't
div128_32(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000, &hi, &lo);
div128_32(hi, lo, 1000000, &hi, &lo);
assert(hi == 0);
- return lo;
+ // quantize fee up to 8 decimals
+ uint64_t mask = get_fee_quantization_mask();
+ uint64_t qlo = (lo + mask - 1) / mask * mask;
+ MDEBUG("lo " << print_money(lo) << ", qlo " << print_money(qlo) << ", mask " << mask);
+
+ return qlo;
}
//------------------------------------------------------------------
@@ -2784,7 +2852,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const
uint64_t base_reward;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false;
- fee_per_kb = get_dynamic_per_kb_fee(base_reward, median);
+ fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version);
}
MDEBUG("Using " << print_money(fee) << "/kB fee");
@@ -2792,7 +2860,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const
needed_fee += (blob_size % 1024) ? 1 : 0;
needed_fee *= fee_per_kb;
- if (fee < needed_fee)
+ if (fee < needed_fee * 0.98) // keep a little buffer on acceptance
{
MERROR_VER("transaction fee is not enough: " << print_money(fee) << ", minimum fee: " << print_money(needed_fee));
return false;
@@ -2811,14 +2879,15 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
+ const uint64_t min_block_size = get_min_block_size(version);
std::vector<size_t> sz;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
for (size_t i = 0; i < grace_blocks; ++i)
- sz.push_back(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2);
+ sz.push_back(min_block_size);
uint64_t median = epee::misc_utils::median(sz);
- if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2)
- median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2;
+ if(median <= min_block_size)
+ median = min_block_size;
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
uint64_t base_reward;
@@ -2828,7 +2897,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons
base_reward = BLOCK_REWARD_OVERESTIMATE;
}
- uint64_t fee = get_dynamic_per_kb_fee(base_reward, median);
+ uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version);
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB");
return fee;
}
@@ -3347,7 +3416,7 @@ leave:
//------------------------------------------------------------------
bool Blockchain::update_next_cumulative_size_limit()
{
- uint64_t full_reward_zone = get_current_hard_fork_version() < 2 ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2;
+ uint64_t full_reward_zone = get_min_block_size(get_current_hard_fork_version());
LOG_PRINT_L3("Blockchain::" << __func__);
std::vector<size_t> sz;
@@ -3541,7 +3610,7 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin
{
try
{
- m_db->get_output_key(amount, offsets, outputs);
+ m_db->get_output_key(amount, offsets, outputs, true);
}
catch (const std::exception& e)
{
@@ -3596,6 +3665,8 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
std::vector<std::unordered_map<crypto::hash, crypto::hash>> maps(threads);
std::vector < std::vector < block >> blocks(threads);
auto it = blocks_entry.begin();
+ boost::thread::attributes attrs;
+ attrs.set_stack_size(THREAD_STACK_SIZE);
for (uint64_t i = 0; i < threads; i++)
{
@@ -3655,7 +3726,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e
m_blocks_longhash_table.clear();
for (uint64_t i = 0; i < threads; i++)
{
- thread_list.push_back(new boost::thread(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i])));
+ thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i]))));
}
for (size_t j = 0; j < thread_list.size(); j++)
@@ -3949,10 +4020,37 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
+static const char expected_block_hashes_hash[] = "23d8a8c73de7b2383c72a016d9a6034e69d62dd48077d1c414e064ceab6daa94";
void Blockchain::load_compiled_in_block_hashes()
{
- if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr)
+ if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0)
{
+ MINFO("Loading precomputed blocks (" << get_blocks_dat_size(m_testnet) << " bytes)");
+
+ if (!m_testnet)
+ {
+ // first check hash
+ crypto::hash hash;
+ if (!tools::sha256sum(get_blocks_dat_start(m_testnet), get_blocks_dat_size(m_testnet), hash))
+ {
+ MERROR("Failed to hash precomputed blocks data");
+ return;
+ }
+ MINFO("precomputed blocks hash: " << hash << ", expected " << expected_block_hashes_hash);
+ cryptonote::blobdata expected_hash_data;
+ if (!epee::string_tools::parse_hexstr_to_binbuff(std::string(expected_block_hashes_hash), expected_hash_data) || expected_hash_data.size() != sizeof(crypto::hash))
+ {
+ MERROR("Failed to parse expected block hashes hash");
+ return;
+ }
+ const crypto::hash expected_hash = *reinterpret_cast<const crypto::hash*>(expected_hash_data.data());
+ if (hash != expected_hash)
+ {
+ MERROR("Block hash data does not match expected hash");
+ return;
+ }
+ }
+
if (get_blocks_dat_size(m_testnet) > 4)
{
const unsigned char *p = get_blocks_dat_start(m_testnet);
@@ -3960,7 +4058,6 @@ void Blockchain::load_compiled_in_block_hashes()
const size_t size_needed = 4 + nblocks * sizeof(crypto::hash);
if(nblocks > 0 && nblocks > m_db->height() && get_blocks_dat_size(m_testnet) >= size_needed)
{
- MINFO("Loading precomputed blocks: " << nblocks);
p += sizeof(uint32_t);
for (uint32_t i = 0; i < nblocks; i++)
{
@@ -3969,6 +4066,7 @@ void Blockchain::load_compiled_in_block_hashes()
p += sizeof(hash.data);
m_blocks_hash_check.push_back(hash);
}
+ MINFO(nblocks << " block hashes loaded");
// FIXME: clear tx_pool because the process might have been
// terminated and caused it to store txs kept by blocks.
@@ -4012,3 +4110,7 @@ bool Blockchain::for_all_outputs(std::function<bool(uint64_t amount, const crypt
{
return m_db->for_all_outputs(f);;
}
+
+namespace cryptonote {
+template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::list<transaction>&, std::list<crypto::hash>&) const;
+}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index cd452faf4..46f7ac682 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
+#include <boost/asio/io_service.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/version.hpp>
#include <boost/serialization/list.hpp>
@@ -42,16 +43,16 @@
#include "syncobj.h"
#include "string_tools.h"
-#include "cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_basic.h"
#include "common/util.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "rpc/core_rpc_server_commands_defs.h"
-#include "difficulty.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "verification_context.h"
+#include "cryptonote_basic/difficulty.h"
+#include "cryptonote_tx_utils.h"
+#include "cryptonote_basic/verification_context.h"
#include "crypto/hash.h"
-#include "checkpoints.h"
-#include "hardfork.h"
+#include "cryptonote_basic/checkpoints.h"
+#include "cryptonote_basic/hardfork.h"
#include "blockchain_db/blockchain_db.h"
namespace cryptonote
@@ -153,7 +154,7 @@ namespace cryptonote
*
* @return false if start_offset > blockchain height, else true
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const;
/**
* @brief get blocks from blocks based on start height and count
@@ -164,7 +165,7 @@ namespace cryptonote
*
* @return false if start_offset > blockchain height, else true
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const;
/**
* @brief compiles a list of all blocks stored as alternative chains
@@ -322,11 +323,12 @@ namespace cryptonote
* @param miner_address address new coins for the block will go to
* @param di return-by-reference tells the miner what the difficulty target is
* @param height return-by-reference tells the miner what height it's mining against
+ * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
* @param ex_nonce extra data to be added to the miner transaction's extra
*
* @return true if block template filled in successfully, else false
*/
- bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce);
+ bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
/**
* @brief checks if a block is known about with a given hash
@@ -407,7 +409,7 @@ namespace cryptonote
*
* @return true if a block found in common or req_start_block specified, else false
*/
- bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
+ bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
/**
* @brief retrieves a set of blocks and their transactions, and possibly other transactions
@@ -520,10 +522,11 @@ namespace cryptonote
*
* @param block_reward the current block reward
* @param median_block_size the median blob's size in the past window
+ * @param version hard fork version for rules and constants to use
*
* @return the per kB fee
*/
- static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size);
+ static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version);
/**
* @brief get dynamic per kB fee estimate for the next few blocks
@@ -621,9 +624,10 @@ namespace cryptonote
* @return false if an unexpected exception occurs, else true
*/
template<class t_ids_container, class t_tx_container, class t_missed_container>
+ bool get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const;
+ template<class t_ids_container, class t_tx_container, class t_missed_container>
bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) const;
-
//debug functions
/**
diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h
index 0518b9aa7..e87a51f10 100644
--- a/src/cryptonote_core/blockchain_storage_boost_serialization.h
+++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 900dc58ba..9c122f511 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -35,14 +35,16 @@ using namespace epee;
#include "cryptonote_core.h"
#include "common/command_line.h"
#include "common/util.h"
+#include "common/updates.h"
+#include "common/download.h"
#include "warnings.h"
#include "crypto/crypto.h"
#include "cryptonote_config.h"
-#include "cryptonote_format_utils.h"
+#include "cryptonote_tx_utils.h"
#include "misc_language.h"
#include <csignal>
#include <p2p/net_node.h>
-#include "cryptonote_core/checkpoints.h"
+#include "cryptonote_basic/checkpoints.h"
#include "ringct/rctTypes.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
@@ -58,6 +60,8 @@ DISABLE_VS_WARNINGS(4355)
#define MERROR_VER(x) MCERROR("verify", x)
+#define BAD_SEMANTICS_TXES_MAX_SIZE 100
+
namespace cryptonote
{
@@ -71,7 +75,8 @@ namespace cryptonote
m_target_blockchain_height(0),
m_checkpoints_path(""),
m_last_dns_checkpoints_update(0),
- m_last_json_checkpoints_update(0)
+ m_last_json_checkpoints_update(0),
+ m_update_download(0)
{
set_cryptonote_protocol(pprotocol);
}
@@ -130,6 +135,15 @@ namespace cryptonote
void core::stop()
{
m_blockchain_storage.cancel();
+
+ tools::download_async_handle handle;
+ {
+ boost::lock_guard<boost::mutex> lock(m_update_mutex);
+ handle = m_update_download;
+ m_update_download = 0;
+ }
+ if (handle)
+ tools::download_cancel(handle);
}
//-----------------------------------------------------------------------------------
void core::init_options(boost::program_options::options_description& desc)
@@ -148,6 +162,13 @@ namespace cryptonote
command_line::add_arg(desc, command_line::arg_db_sync_mode);
command_line::add_arg(desc, command_line::arg_show_time_stats);
command_line::add_arg(desc, command_line::arg_block_sync_size);
+ command_line::add_arg(desc, command_line::arg_check_updates);
+
+ // we now also need some of net_node's options (p2p bind arg, for separate data dir)
+ command_line::add_arg(desc, nodetool::arg_testnet_p2p_bind_port, false);
+ command_line::add_arg(desc, nodetool::arg_p2p_bind_port, false);
+
+ miner::init_options(desc);
}
//-----------------------------------------------------------------------------------------------
bool core::handle_command_line(const boost::program_options::variables_map& vm)
@@ -195,62 +216,44 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const
+ bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const
{
return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
+ bool core::get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const
{
return m_blockchain_storage.get_blocks(start_offset, count, blocks);
- } //-----------------------------------------------------------------------------------------------
- bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const
{
- return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs);
+ std::list<std::pair<cryptonote::blobdata, cryptonote::block>> bs;
+ if (!m_blockchain_storage.get_blocks(start_offset, count, bs))
+ return false;
+ for (const auto &b: bs)
+ blocks.push_back(b.second);
+ return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::get_alternative_blocks(std::list<block>& blocks) const
+ bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const
{
- return m_blockchain_storage.get_alternative_blocks(blocks);
+ return m_blockchain_storage.get_transactions_blobs(txs_ids, txs, missed_txs);
}
//-----------------------------------------------------------------------------------------------
- size_t core::get_alternative_blocks_count() const
+ bool core::get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const
{
- return m_blockchain_storage.get_alternative_blocks_count();
+ return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs);
}
//-----------------------------------------------------------------------------------------------
- bool core::lock_db_directory(const boost::filesystem::path &path)
+ bool core::get_alternative_blocks(std::list<block>& blocks) const
{
- // boost doesn't like locking directories...
- const boost::filesystem::path lock_path = path / ".daemon_lock";
-
- try
- {
- // ensure the file exists
- std::ofstream(lock_path.string(), std::ios::out).close();
-
- db_lock = boost::interprocess::file_lock(lock_path.string().c_str());
- LOG_PRINT_L1("Locking " << lock_path.string());
- if (!db_lock.try_lock())
- {
- LOG_ERROR("Failed to lock " << lock_path.string());
- return false;
- }
- return true;
- }
- catch (const std::exception &e)
- {
- LOG_ERROR("Error trying to lock " << lock_path.string() << ": " << e.what());
- return false;
- }
+ return m_blockchain_storage.get_alternative_blocks(blocks);
}
//-----------------------------------------------------------------------------------------------
- bool core::unlock_db_directory()
+ size_t core::get_alternative_blocks_count() const
{
- db_lock.unlock();
- db_lock = boost::interprocess::file_lock();
- LOG_PRINT_L1("Blockchain directory successfully unlocked");
- return true;
+ return m_blockchain_storage.get_alternative_blocks_count();
}
//-----------------------------------------------------------------------------------------------
bool core::init(const boost::program_options::variables_map& vm, const cryptonote::test_options *test_options)
@@ -276,6 +279,7 @@ namespace cryptonote
std::string db_sync_mode = command_line::get_arg(vm, command_line::arg_db_sync_mode);
bool fast_sync = command_line::get_arg(vm, command_line::arg_fast_block_sync) != 0;
uint64_t blocks_threads = command_line::get_arg(vm, command_line::arg_prep_blocks_threads);
+ std::string check_updates_string = command_line::get_arg(vm, command_line::arg_check_updates);
boost::filesystem::path folder(m_config_folder);
if (m_fakechain)
@@ -284,11 +288,6 @@ namespace cryptonote
// make sure the data directory exists, and try to lock it
CHECK_AND_ASSERT_MES (boost::filesystem::exists(folder) || boost::filesystem::create_directories(folder), false,
std::string("Failed to create directory ").append(folder.string()).c_str());
- if (!lock_db_directory (folder))
- {
- LOG_ERROR ("Failed to lock " << folder);
- return false;
- }
// check for blockchain.bin
try
@@ -418,6 +417,20 @@ namespace cryptonote
// with respect to what blocks we already have
CHECK_AND_ASSERT_MES(update_checkpoints(), false, "One or more checkpoints loaded from json or dns conflicted with existing checkpoints.");
+ // DNS versions checking
+ if (check_updates_string == "disabled")
+ check_updates_level = UPDATES_DISABLED;
+ else if (check_updates_string == "notify")
+ check_updates_level = UPDATES_NOTIFY;
+ else if (check_updates_string == "download")
+ check_updates_level = UPDATES_DOWNLOAD;
+ else if (check_updates_string == "update")
+ check_updates_level = UPDATES_UPDATE;
+ else {
+ MERROR("Invalid argument to --dns-versions-check: " << check_updates_string);
+ return false;
+ }
+
r = m_miner.init(vm, m_testnet);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance");
@@ -440,7 +453,6 @@ namespace cryptonote
m_miner.stop();
m_mempool.deinit();
m_blockchain_storage.deinit();
- unlock_db_directory();
return true;
}
//-----------------------------------------------------------------------------------------------
@@ -496,11 +508,14 @@ namespace cryptonote
}
//std::cout << "!"<< tx.vin.size() << std::endl;
- if (bad_semantics_txes.find(tx_hash) != bad_semantics_txes.end())
+ for (int idx = 0; idx < 2; ++idx)
{
- LOG_PRINT_L1("Transaction already seen with bad semantics, rejected");
- tvc.m_verifivation_failed = true;
- return false;
+ if (bad_semantics_txes[idx].find(tx_hash) != bad_semantics_txes[idx].end())
+ {
+ LOG_PRINT_L1("Transaction already seen with bad semantics, rejected");
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
}
uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
@@ -551,8 +566,13 @@ namespace cryptonote
if(!check_tx_semantic(tx, keeped_by_block))
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected");
- bad_semantics_txes.insert(tx_hash);
tvc.m_verifivation_failed = true;
+ bad_semantics_txes[0].insert(tx_hash);
+ if (bad_semantics_txes[0].size() >= BAD_SEMANTICS_TXES_MAX_SIZE)
+ {
+ std::swap(bad_semantics_txes[0], bad_semantics_txes[1]);
+ bad_semantics_txes[0].clear();
+ }
return false;
}
@@ -639,6 +659,12 @@ namespace cryptonote
return false;
}
+ if (!check_tx_inputs_keyimages_domain(tx))
+ {
+ MERROR_VER("tx uses key image not in the valid domain");
+ return false;
+ }
+
if (tx.version >= 2)
{
const rct::rctSig &rv = tx.rct_signatures;
@@ -722,6 +748,18 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const
+ {
+ std::unordered_set<crypto::key_image> ki;
+ for(const auto& in: tx.vin)
+ {
+ CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false);
+ if (!(rct::scalarmultKey(rct::ki2rct(tokey_in.k_image), rct::curveOrder()) == rct::identity()))
+ return false;
+ }
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{
crypto::hash tx_hash = get_transaction_hash(tx);
@@ -758,7 +796,7 @@ namespace cryptonote
{
// we attempt to relay txes that should be relayed, but were not
std::list<std::pair<crypto::hash, cryptonote::transaction>> txs;
- if (m_mempool.get_relayable_transactions(txs))
+ if (m_mempool.get_relayable_transactions(txs) && !txs.empty())
{
cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
@@ -789,9 +827,9 @@ namespace cryptonote
m_mempool.set_relayed(txs);
}
//-----------------------------------------------------------------------------------------------
- bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce)
+ bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
{
- return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce);
+ return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce);
}
//-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
@@ -799,7 +837,7 @@ namespace cryptonote
return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp);
}
//-----------------------------------------------------------------------------------------------
- bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
+ bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const
{
return m_blockchain_storage.find_blockchain_supplement(req_start_block, qblock_ids, blocks, total_height, start_height, max_count);
}
@@ -849,11 +887,28 @@ namespace cryptonote
m_miner.resume();
}
//-----------------------------------------------------------------------------------------------
+ block_complete_entry get_block_complete_entry(block& b, tx_memory_pool &pool)
+ {
+ block_complete_entry bce;
+ bce.block = cryptonote::block_to_blob(b);
+ for (const auto &tx_hash: b.tx_hashes)
+ {
+ cryptonote::transaction tx;
+ CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, tx), "Transaction not found in pool");
+ bce.txs.push_back(cryptonote::tx_to_blob(tx));
+ }
+ return bce;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::handle_block_found(block& b)
{
block_verification_context bvc = boost::value_initialized<block_verification_context>();
m_miner.pause();
+ std::list<block_complete_entry> blocks;
+ blocks.push_back(get_block_complete_entry(b, m_mempool));
+ prepare_handle_incoming_blocks(blocks);
m_blockchain_storage.add_new_block(b, bvc);
+ cleanup_handle_incoming_blocks(true);
//anyway - update miner template
update_miner_block_template();
m_miner.resume();
@@ -867,8 +922,8 @@ namespace cryptonote
arg.hop = 0;
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
std::list<crypto::hash> missed_txs;
- std::list<transaction> txs;
- m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs);
+ std::list<cryptonote::blobdata> txs;
+ m_blockchain_storage.get_transactions_blobs(b.tx_hashes, txs, missed_txs);
if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b))
{
LOG_PRINT_L1("Block found but, seems that reorganize just happened after that, do not relay this block");
@@ -880,7 +935,7 @@ namespace cryptonote
block_to_blob(b, arg.b.block);
//pack transactions
for(auto& tx: txs)
- arg.b.txs.push_back(t_serializable_object_to_blob(tx));
+ arg.b.txs.push_back(tx);
m_pprotocol->relay_block(arg, exclude_context);
}
@@ -981,7 +1036,13 @@ namespace cryptonote
m_mempool.get_transactions(txs);
return true;
}
- //-----------------------------------------------------------------------------------------------
+ //-----------------------------------------------------------------------------------------------
+ bool core::get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const
+ {
+ m_mempool.get_transaction_hashes(txs);
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::get_pool_transaction(const crypto::hash &id, transaction& tx) const
{
return m_mempool.get_transaction(id, tx);
@@ -1040,6 +1101,7 @@ namespace cryptonote
m_fork_moaner.do_call(boost::bind(&core::check_fork_time, this));
m_txpool_auto_relayer.do_call(boost::bind(&core::relay_txpool_transactions, this));
+ m_check_updates_interval.do_call(boost::bind(&core::check_updates, this));
m_miner.on_idle();
m_mempool.on_idle();
return true;
@@ -1067,6 +1129,99 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ bool core::check_updates()
+ {
+ static const char software[] = "monero";
+ static const char subdir[] = "cli"; // because it can never be simple
+#ifdef BUILD_TAG
+ static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG);
+#else
+ static const char buildtag[] = "source";
+#endif
+
+ if (check_updates_level == UPDATES_DISABLED)
+ return true;
+
+ std::string version, hash;
+ MCDEBUG("updates", "Checking for a new " << software << " version for " << buildtag);
+ if (!tools::check_updates(software, buildtag, version, hash))
+ return false;
+
+ if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0)
+ return true;
+
+ std::string url = tools::get_update_url(software, subdir, buildtag, version, true);
+ MCLOG_CYAN(el::Level::Info, "global", "Version " << version << " of " << software << " for " << buildtag << " is available: " << url << ", SHA256 hash " << hash);
+
+ if (check_updates_level == UPDATES_NOTIFY)
+ return true;
+
+ url = tools::get_update_url(software, subdir, buildtag, version, false);
+ std::string filename;
+ const char *slash = strrchr(url.c_str(), '/');
+ if (slash)
+ filename = slash + 1;
+ else
+ filename = std::string(software) + "-update-" + version;
+ boost::filesystem::path path(epee::string_tools::get_current_module_folder());
+ path /= filename;
+
+ boost::unique_lock<boost::mutex> lock(m_update_mutex);
+
+ if (m_update_download != 0)
+ {
+ MCDEBUG("updates", "Already downloading update");
+ return true;
+ }
+
+ crypto::hash file_hash;
+ if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash)))
+ {
+ MCDEBUG("updates", "We don't have that file already, downloading");
+ m_last_update_length = 0;
+ m_update_download = tools::download_async(path.string(), url, [this, hash](const std::string &path, const std::string &uri, bool success) {
+ if (success)
+ {
+ crypto::hash file_hash;
+ if (!tools::sha256sum(path, file_hash))
+ {
+ MCERROR("updates", "Failed to hash " << path);
+ }
+ if (hash != epee::string_tools::pod_to_hex(file_hash))
+ {
+ MCERROR("updates", "Download from " << uri << " does not match the expected hash");
+ }
+ MCLOG_CYAN(el::Level::Info, "updates", "New version downloaded to " << path);
+ }
+ else
+ {
+ MCERROR("updates", "Failed to download " << uri);
+ }
+ boost::unique_lock<boost::mutex> lock(m_update_mutex);
+ m_update_download = 0;
+ }, [this](const std::string &path, const std::string &uri, size_t length, ssize_t content_length) {
+ if (length >= m_last_update_length + 1024 * 1024 * 10)
+ {
+ m_last_update_length = length;
+ MCDEBUG("updates", "Downloaded " << length << "/" << (content_length ? std::to_string(content_length) : "unknown"));
+ }
+ return true;
+ });
+ }
+ else
+ {
+ MCDEBUG("updates", "We already have " << path << " with expected hash");
+ }
+
+ lock.unlock();
+
+ if (check_updates_level == UPDATES_DOWNLOAD)
+ return true;
+
+ MCERROR("updates", "Download/update not implemented yet");
+ return true;
+ }
+ //-----------------------------------------------------------------------------------------------
void core::set_target_blockchain_height(uint64_t target_blockchain_height)
{
m_target_blockchain_height = target_blockchain_height;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 1b9518c96..e56c2dcf1 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -39,11 +39,12 @@
#include "p2p/net_node_common.h"
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
#include "storages/portable_storage_template_helper.h"
+#include "common/download.h"
#include "tx_pool.h"
#include "blockchain.h"
-#include "miner.h"
-#include "connection_context.h"
-#include "cryptonote_core/cryptonote_stat_info.h"
+#include "cryptonote_basic/miner.h"
+#include "cryptonote_basic/connection_context.h"
+#include "cryptonote_basic/cryptonote_stat_info.h"
#include "warnings.h"
#include "crypto/hash.h"
@@ -179,7 +180,7 @@ namespace cryptonote
*
* @note see Blockchain::create_block_template
*/
- virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce);
+ virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce);
/**
* @brief called when a transaction is relayed
@@ -287,16 +288,23 @@ namespace cryptonote
bool get_blockchain_top(uint64_t& height, crypto::hash& top_id) const;
/**
- * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const
*
- * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&, std::list<transaction>&) const
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&, std::list<transaction>&) const
*/
- bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks, std::list<transaction>& txs) const;
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks, std::list<cryptonote::blobdata>& txs) const;
/**
- * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
*
- * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<block>&) const
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
+ */
+ bool get_blocks(uint64_t start_offset, size_t count, std::list<std::pair<cryptonote::blobdata,block>>& blocks) const;
+
+ /**
+ * @copydoc Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
+ *
+ * @note see Blockchain::get_blocks(uint64_t, size_t, std::list<std::pair<cryptonote::blobdata,block>>&) const
*/
bool get_blocks(uint64_t start_offset, size_t count, std::list<block>& blocks) const;
@@ -323,6 +331,13 @@ namespace cryptonote
*
* @note see Blockchain::get_transactions
*/
+ bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<cryptonote::blobdata>& txs, std::list<crypto::hash>& missed_txs) const;
+
+ /**
+ * @copydoc Blockchain::get_transactions
+ *
+ * @note see Blockchain::get_transactions
+ */
bool get_transactions(const std::vector<crypto::hash>& txs_ids, std::list<transaction>& txs, std::list<crypto::hash>& missed_txs) const;
/**
@@ -382,6 +397,13 @@ namespace cryptonote
bool get_pool_transactions(std::list<transaction>& txs) const;
/**
+ * @copydoc tx_memory_pool::get_transactions
+ *
+ * @note see tx_memory_pool::get_transactions
+ */
+ bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs) const;
+
+ /**
* @copydoc tx_memory_pool::get_transaction
*
* @note see tx_memory_pool::get_transaction
@@ -431,11 +453,11 @@ namespace cryptonote
bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
/**
- * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const
+ * @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >&, uint64_t&, uint64_t&, size_t) const
*
- * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<block, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const
+ * @note see Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::list<std::pair<cryptonote::blobdata, std::list<transaction> > >&, uint64_t&, uint64_t&, size_t) const
*/
- bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<block, std::list<transaction> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
+ bool find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) const;
/**
* @brief gets some stats about the daemon
@@ -745,6 +767,16 @@ namespace cryptonote
bool check_tx_inputs_keyimages_diff(const transaction& tx) const;
/**
+ * @brief verify that each input key image in a transaction is in
+ * the valid domain
+ *
+ * @param tx the transaction to check
+ *
+ * @return false if any key image is not in the valid domain, otherwise true
+ */
+ bool check_tx_inputs_keyimages_domain(const transaction& tx) const;
+
+ /**
* @brief checks HardFork status and prints messages about it
*
* Checks the status of HardFork and logs/prints if an update to
@@ -764,22 +796,11 @@ namespace cryptonote
bool relay_txpool_transactions();
/**
- * @brief locks a file in the BlockchainDB directory
- *
- * @param path the directory in which to place the file
- *
- * @return true if lock acquired successfully, otherwise false
- */
- bool lock_db_directory(const boost::filesystem::path &path);
-
- /**
- * @brief unlocks the db directory
- *
- * @note see lock_db_directory()
+ * @brief checks DNS versions
*
- * @return true
+ * @return true on success, false otherwise
*/
- bool unlock_db_directory();
+ bool check_updates();
bool m_test_drop_download = true; //!< whether or not to drop incoming blocks (for testing)
@@ -801,10 +822,10 @@ namespace cryptonote
cryptonote_protocol_stub m_protocol_stub; //!< cryptonote protocol stub instance
epee::math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; //!< interval for manual storing of Blockchain, if enabled
- epee::math_helper::once_a_time_seconds<60*60*2, false> m_fork_moaner; //!< interval for checking HardFork status
+ epee::math_helper::once_a_time_seconds<60*60*2, true> m_fork_moaner; //!< interval for checking HardFork status
epee::math_helper::once_a_time_seconds<60*2, false> m_txpool_auto_relayer; //!< interval for checking re-relaying txpool transactions
+ epee::math_helper::once_a_time_seconds<60*60*12, true> m_check_updates_interval; //!< interval for checking for new versions
- friend class tx_validate_inputs;
std::atomic<bool> m_starter_message_showed; //!< has the "daemon will sync now" message been shown?
uint64_t m_target_blockchain_height; //!< blockchain height target
@@ -819,13 +840,22 @@ namespace cryptonote
std::atomic_flag m_checkpoints_updating; //!< set if checkpoints are currently updating to avoid multiple threads attempting to update at once
- boost::interprocess::file_lock db_lock; //!< a lock object for a file lock in the db directory
-
size_t block_sync_size;
time_t start_time;
- std::unordered_set<crypto::hash> bad_semantics_txes;
+ std::unordered_set<crypto::hash> bad_semantics_txes[2];
+
+ enum {
+ UPDATES_DISABLED,
+ UPDATES_NOTIFY,
+ UPDATES_DOWNLOAD,
+ UPDATES_UPDATE,
+ } check_updates_level;
+
+ tools::download_async_handle m_update_download;
+ size_t m_last_update_length;
+ boost::mutex m_update_mutex;
};
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
new file mode 100644
index 000000000..26d5fb767
--- /dev/null
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -0,0 +1,499 @@
+// 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
+
+#include "include_base_utils.h"
+using namespace epee;
+
+#include "cryptonote_tx_utils.h"
+#include "cryptonote_config.h"
+#include "cryptonote_basic/miner.h"
+#include "crypto/crypto.h"
+#include "crypto/hash.h"
+#include "ringct/rctSigs.h"
+
+namespace cryptonote
+{
+ //---------------------------------------------------------------
+ bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
+ tx.vin.clear();
+ tx.vout.clear();
+ tx.extra.clear();
+
+ keypair txkey = keypair::generate();
+ add_tx_pub_key_to_extra(tx, txkey.pub);
+ if(!extra_nonce.empty())
+ if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
+ return false;
+
+ txin_gen in;
+ in.height = height;
+
+ uint64_t block_reward;
+ if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version))
+ {
+ LOG_PRINT_L0("Block is too big");
+ return false;
+ }
+
+#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
+ LOG_PRINT_L1("Creating block template: reward " << block_reward <<
+ ", fee " << fee);
+#endif
+ block_reward += fee;
+
+ // from hard fork 2, we cut out the low significant digits. This makes the tx smaller, and
+ // keeps the paid amount almost the same. The unpaid remainder gets pushed back to the
+ // emission schedule
+ // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller,
+ // and avoids the quantization. These outputs will be added as rct outputs with identity
+ // masks, to they can be used as rct inputs.
+ if (hard_fork_version >= 2 && hard_fork_version < 4) {
+ block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD;
+ }
+
+ std::vector<uint64_t> out_amounts;
+ decompose_amount_into_digits(block_reward, hard_fork_version >= 2 ? 0 : ::config::DEFAULT_DUST_THRESHOLD,
+ [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
+ [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
+
+ CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
+ if (height == 0 || hard_fork_version >= 4)
+ {
+ // the genesis block was not decomposed, for unknown reasons
+ while (max_outs < out_amounts.size())
+ {
+ //out_amounts[out_amounts.size() - 2] += out_amounts.back();
+ //out_amounts.resize(out_amounts.size() - 1);
+ out_amounts[1] += out_amounts[0];
+ for (size_t n = 1; n < out_amounts.size(); ++n)
+ out_amounts[n - 1] = out_amounts[n];
+ out_amounts.resize(out_amounts.size() - 1);
+ }
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(max_outs >= out_amounts.size(), false, "max_out exceeded");
+ }
+
+ uint64_t summary_amounts = 0;
+ for (size_t no = 0; no < out_amounts.size(); no++)
+ {
+ crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
+ crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
+ bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")");
+
+ r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key);
+ CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")");
+
+ txout_to_key tk;
+ tk.key = out_eph_public_key;
+
+ tx_out out;
+ summary_amounts += out.amount = out_amounts[no];
+ out.target = tk;
+ tx.vout.push_back(out);
+ }
+
+ CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward);
+
+ if (hard_fork_version >= 4)
+ tx.version = 2;
+ else
+ tx.version = 1;
+
+ //lock
+ tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
+ tx.vin.push_back(in);
+
+ tx.invalidate_hashes();
+
+ //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee)
+ // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2);
+ return true;
+ }
+ //---------------------------------------------------------------
+ crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys)
+ {
+ if (destinations.empty())
+ return null_pkey;
+ for (size_t n = 1; n < destinations.size(); ++n)
+ {
+ if (!memcmp(&destinations[n].addr, &sender_keys.m_account_address, sizeof(destinations[0].addr)))
+ continue;
+ if (destinations[n].amount == 0)
+ continue;
+ if (memcmp(&destinations[n].addr, &destinations[0].addr, sizeof(destinations[0].addr)))
+ return null_pkey;
+ }
+ return destinations[0].addr.m_view_public_key;
+ }
+ //---------------------------------------------------------------
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct)
+ {
+ std::vector<rct::key> amount_keys;
+ tx.set_null();
+ amount_keys.clear();
+
+ tx.version = rct ? 2 : 1;
+ tx.unlock_time = unlock_time;
+
+ tx.extra = extra;
+ keypair txkey = keypair::generate();
+ remove_field_from_tx_extra(tx.extra, typeid(tx_extra_pub_key));
+ add_tx_pub_key_to_extra(tx, txkey.pub);
+ tx_key = txkey.sec;
+
+ // if we have a stealth payment id, find it and encrypt it with the tx key now
+ std::vector<tx_extra_field> tx_extra_fields;
+ if (parse_tx_extra(tx.extra, tx_extra_fields))
+ {
+ tx_extra_nonce extra_nonce;
+ if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
+ {
+ crypto::hash8 payment_id = null_hash8;
+ if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ {
+ LOG_PRINT_L2("Encrypting payment id " << payment_id);
+ crypto::public_key view_key_pub = get_destination_view_key_pub(destinations, sender_account_keys);
+ if (view_key_pub == null_pkey)
+ {
+ LOG_ERROR("Destinations have to have exactly one output to support encrypted payment ids");
+ return false;
+ }
+
+ if (!encrypt_payment_id(payment_id, view_key_pub, txkey.sec))
+ {
+ LOG_ERROR("Failed to encrypt payment id");
+ return false;
+ }
+
+ std::string extra_nonce;
+ set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
+ remove_field_from_tx_extra(tx.extra, typeid(tx_extra_nonce));
+ if (!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
+ {
+ LOG_ERROR("Failed to add encrypted payment id to tx extra");
+ return false;
+ }
+ LOG_PRINT_L1("Encrypted payment ID: " << payment_id);
+ }
+ }
+ }
+ else
+ {
+ LOG_ERROR("Failed to parse tx extra");
+ return false;
+ }
+
+ struct input_generation_context_data
+ {
+ keypair in_ephemeral;
+ };
+ std::vector<input_generation_context_data> in_contexts;
+
+ uint64_t summary_inputs_money = 0;
+ //fill inputs
+ int idx = -1;
+ for(const tx_source_entry& src_entr: sources)
+ {
+ ++idx;
+ if(src_entr.real_output >= src_entr.outputs.size())
+ {
+ LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size());
+ return false;
+ }
+ summary_inputs_money += src_entr.amount;
+
+ //key_derivation recv_derivation;
+ in_contexts.push_back(input_generation_context_data());
+ keypair& in_ephemeral = in_contexts.back().in_ephemeral;
+ crypto::key_image img;
+ if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img))
+ return false;
+
+ //check that derivated key is equal with real output key
+ if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second.dest) )
+ {
+ LOG_ERROR("derived public key mismatch with output public key at index " << idx << ", real out " << src_entr.real_output << "! "<< ENDL << "derived_key:"
+ << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
+ << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) );
+ LOG_ERROR("amount " << src_entr.amount << ", rct " << src_entr.rct);
+ LOG_ERROR("tx pubkey " << src_entr.real_out_tx_key << ", real_output_in_tx_index " << src_entr.real_output_in_tx_index);
+ return false;
+ }
+
+ //put key image into tx input
+ txin_to_key input_to_key;
+ input_to_key.amount = src_entr.amount;
+ input_to_key.k_image = img;
+
+ //fill outputs array and use relative offsets
+ for(const tx_source_entry::output_entry& out_entry: src_entr.outputs)
+ input_to_key.key_offsets.push_back(out_entry.first);
+
+ input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
+ tx.vin.push_back(input_to_key);
+ }
+
+ // "Shuffle" outs
+ std::vector<tx_destination_entry> shuffled_dsts(destinations);
+ std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } );
+
+ uint64_t summary_outs_money = 0;
+ //fill outputs
+ size_t output_index = 0;
+ for(const tx_destination_entry& dst_entr: shuffled_dsts)
+ {
+ CHECK_AND_ASSERT_MES(dst_entr.amount > 0 || tx.version > 1, false, "Destination with wrong amount: " << dst_entr.amount);
+ crypto::key_derivation derivation;
+ crypto::public_key out_eph_public_key;
+ bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation);
+ CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")");
+
+ if (tx.version > 1)
+ {
+ crypto::secret_key scalar1;
+ crypto::derivation_to_scalar(derivation, output_index, scalar1);
+ amount_keys.push_back(rct::sk2rct(scalar1));
+ }
+ r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key);
+ CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")");
+
+ tx_out out;
+ out.amount = dst_entr.amount;
+ txout_to_key tk;
+ tk.key = out_eph_public_key;
+ out.target = tk;
+ tx.vout.push_back(out);
+ output_index++;
+ summary_outs_money += dst_entr.amount;
+ }
+
+ //check money
+ if(summary_outs_money > summary_inputs_money )
+ {
+ LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")");
+ return false;
+ }
+
+ // check for watch only wallet
+ bool zero_secret_key = true;
+ for (size_t i = 0; i < sizeof(sender_account_keys.m_spend_secret_key); ++i)
+ zero_secret_key &= (sender_account_keys.m_spend_secret_key.data[i] == 0);
+ if (zero_secret_key)
+ {
+ MDEBUG("Null secret key, skipping signatures");
+ }
+
+ if (tx.version == 1)
+ {
+ //generate ring signatures
+ crypto::hash tx_prefix_hash;
+ get_transaction_prefix_hash(tx, tx_prefix_hash);
+
+ std::stringstream ss_ring_s;
+ size_t i = 0;
+ for(const tx_source_entry& src_entr: sources)
+ {
+ ss_ring_s << "pub_keys:" << ENDL;
+ std::vector<const crypto::public_key*> keys_ptrs;
+ std::vector<crypto::public_key> keys(src_entr.outputs.size());
+ size_t ii = 0;
+ for(const tx_source_entry::output_entry& o: src_entr.outputs)
+ {
+ keys[ii] = rct2pk(o.second.dest);
+ keys_ptrs.push_back(&keys[ii]);
+ ss_ring_s << o.second.dest << ENDL;
+ ++ii;
+ }
+
+ tx.signatures.push_back(std::vector<crypto::signature>());
+ std::vector<crypto::signature>& sigs = tx.signatures.back();
+ sigs.resize(src_entr.outputs.size());
+ if (!zero_secret_key)
+ crypto::generate_ring_signature(tx_prefix_hash, boost::get<txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data());
+ ss_ring_s << "signatures:" << ENDL;
+ std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;});
+ ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL;
+ i++;
+ }
+
+ MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str());
+ }
+ else
+ {
+ size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct
+
+ // the non-simple version is slightly smaller, but assumes all real inputs
+ // are on the same index, so can only be used if there just one ring.
+ bool use_simple_rct = sources.size() > 1;
+
+ if (!use_simple_rct)
+ {
+ // non simple ringct requires all real inputs to be at the same index for all inputs
+ for(const tx_source_entry& src_entr: sources)
+ {
+ if(src_entr.real_output != sources.begin()->real_output)
+ {
+ LOG_ERROR("All inputs must have the same index for non-simple ringct");
+ return false;
+ }
+ }
+
+ // enforce same mixin for all outputs
+ for (size_t i = 1; i < sources.size(); ++i) {
+ if (n_total_outs != sources[i].outputs.size()) {
+ LOG_ERROR("Non-simple ringct transaction has varying mixin");
+ return false;
+ }
+ }
+ }
+
+ uint64_t amount_in = 0, amount_out = 0;
+ rct::ctkeyV inSk;
+ // mixRing indexing is done the other way round for simple
+ rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs);
+ rct::keyV destinations;
+ std::vector<uint64_t> inamounts, outamounts;
+ std::vector<unsigned int> index;
+ for (size_t i = 0; i < sources.size(); ++i)
+ {
+ rct::ctkey ctkey;
+ amount_in += sources[i].amount;
+ inamounts.push_back(sources[i].amount);
+ index.push_back(sources[i].real_output);
+ // inSk: (secret key, mask)
+ ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec);
+ ctkey.mask = sources[i].mask;
+ inSk.push_back(ctkey);
+ // inPk: (public key, commitment)
+ // will be done when filling in mixRing
+ }
+ for (size_t i = 0; i < tx.vout.size(); ++i)
+ {
+ destinations.push_back(rct::pk2rct(boost::get<txout_to_key>(tx.vout[i].target).key));
+ outamounts.push_back(tx.vout[i].amount);
+ amount_out += tx.vout[i].amount;
+ }
+
+ if (use_simple_rct)
+ {
+ // mixRing indexing is done the other way round for simple
+ for (size_t i = 0; i < sources.size(); ++i)
+ {
+ mixRing[i].resize(sources[i].outputs.size());
+ for (size_t n = 0; n < sources[i].outputs.size(); ++n)
+ {
+ mixRing[i][n] = sources[i].outputs[n].second;
+ }
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < n_total_outs; ++i) // same index assumption
+ {
+ mixRing[i].resize(sources.size());
+ for (size_t n = 0; n < sources.size(); ++n)
+ {
+ mixRing[i][n] = sources[n].outputs[i].second;
+ }
+ }
+ }
+
+ // fee
+ if (!use_simple_rct && amount_in > amount_out)
+ outamounts.push_back(amount_in - amount_out);
+
+ // zero out all amounts to mask rct outputs, real amounts are now encrypted
+ for (size_t i = 0; i < tx.vin.size(); ++i)
+ {
+ if (sources[i].rct)
+ boost::get<txin_to_key>(tx.vin[i]).amount = 0;
+ }
+ for (size_t i = 0; i < tx.vout.size(); ++i)
+ tx.vout[i].amount = 0;
+
+ crypto::hash tx_prefix_hash;
+ get_transaction_prefix_hash(tx, tx_prefix_hash);
+ rct::ctkeyV outSk;
+ if (use_simple_rct)
+ tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk);
+ else
+ tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk); // same index assumption
+
+ CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
+
+ MCINFO("construct_tx", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL);
+ }
+
+ tx.invalidate_hashes();
+
+ return true;
+ }
+ //---------------------------------------------------------------
+ bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time)
+ {
+ crypto::secret_key tx_key;
+ return construct_tx_and_get_tx_key(sender_account_keys, sources, destinations, extra, tx, unlock_time, tx_key);
+ }
+ //---------------------------------------------------------------
+ bool generate_genesis_block(
+ block& bl
+ , std::string const & genesis_tx
+ , uint32_t nonce
+ )
+ {
+ //genesis block
+ bl = boost::value_initialized<block>();
+
+
+ account_public_address ac = boost::value_initialized<account_public_address>();
+ std::vector<size_t> sz;
+ construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis
+ blobdata txb = tx_to_blob(bl.miner_tx);
+ std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb);
+
+ std::string genesis_coinbase_tx_hex = config::GENESIS_TX;
+
+ blobdata tx_bl;
+ string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl);
+ bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx);
+ CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
+ bl.major_version = CURRENT_BLOCK_MAJOR_VERSION;
+ bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
+ bl.timestamp = 0;
+ bl.nonce = nonce;
+ miner::find_nonce_for_given_block(bl, 1, 0);
+ bl.invalidate_hashes();
+ return true;
+ }
+ //---------------------------------------------------------------
+}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
new file mode 100644
index 000000000..933070e1e
--- /dev/null
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -0,0 +1,101 @@
+// 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 "cryptonote_basic/cryptonote_format_utils.h"
+#include <boost/serialization/vector.hpp>
+#include <boost/serialization/utility.hpp>
+
+namespace cryptonote
+{
+ //---------------------------------------------------------------
+ bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
+
+ struct tx_source_entry
+ {
+ typedef std::pair<uint64_t, rct::ctkey> output_entry;
+
+ std::vector<output_entry> outputs; //index + key + optional ringct commitment
+ size_t real_output; //index in outputs vector of real output_entry
+ crypto::public_key real_out_tx_key; //incoming real tx public key
+ size_t real_output_in_tx_index; //index in transaction outputs vector
+ uint64_t amount; //money
+ bool rct; //true if the output is rct
+ rct::key mask; //ringct amount mask
+
+ void push_output(uint64_t idx, const crypto::public_key &k, uint64_t amount) { outputs.push_back(std::make_pair(idx, rct::ctkey({rct::pk2rct(k), rct::zeroCommit(amount)}))); }
+ };
+
+ struct tx_destination_entry
+ {
+ uint64_t amount; //money
+ account_public_address addr; //destination address
+
+ tx_destination_entry() : amount(0), addr(AUTO_VAL_INIT(addr)) { }
+ tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { }
+
+ BEGIN_SERIALIZE_OBJECT()
+ VARINT_FIELD(amount)
+ FIELD(addr)
+ END_SERIALIZE()
+ };
+
+ //---------------------------------------------------------------
+ crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys);
+ bool construct_tx(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, bool rct = false);
+
+ bool generate_genesis_block(
+ block& bl
+ , std::string const & genesis_tx
+ , uint32_t nonce
+ );
+
+}
+
+BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 0)
+
+namespace boost
+{
+ namespace serialization
+ {
+ template <class Archive>
+ inline void serialize(Archive &a, cryptonote::tx_source_entry &x, const boost::serialization::version_type ver)
+ {
+ a & x.outputs;
+ a & x.real_output;
+ a & x.real_out_tx_key;
+ a & x.real_output_in_tx_index;
+ a & x.amount;
+ a & x.rct;
+ a & x.mask;
+ }
+ }
+}
diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp
deleted file mode 100644
index 88c631f80..000000000
--- a/src/cryptonote_core/miner.cpp
+++ /dev/null
@@ -1,414 +0,0 @@
-// Copyright (c) 2014-2016, The Monero Project
-//
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without modification, are
-// permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice, this list of
-// conditions and the following disclaimer.
-//
-// 2. Redistributions in binary form must reproduce the above copyright notice, this list
-// of conditions and the following disclaimer in the documentation and/or other
-// materials provided with the distribution.
-//
-// 3. Neither the name of the copyright holder nor the names of its contributors may be
-// used to endorse or promote products derived from this software without specific
-// prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
-// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
-// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
-// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
-
-#include <sstream>
-#include <numeric>
-#include <boost/utility/value_init.hpp>
-#include <boost/interprocess/detail/atomic.hpp>
-#include <boost/limits.hpp>
-#include "misc_language.h"
-#include "include_base_utils.h"
-#include "cryptonote_basic_impl.h"
-#include "cryptonote_format_utils.h"
-#include "file_io_utils.h"
-#include "common/command_line.h"
-#include "string_coding.h"
-#include "storages/portable_storage_template_helper.h"
-
-#undef MONERO_DEFAULT_LOG_CATEGORY
-#define MONERO_DEFAULT_LOG_CATEGORY "miner"
-
-using namespace epee;
-
-#include "miner.h"
-
-
-extern "C" void slow_hash_allocate_state();
-extern "C" void slow_hash_free_state();
-namespace cryptonote
-{
-
- namespace
- {
- const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true};
- const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true};
- const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true};
- }
-
-
- miner::miner(i_miner_handler* phandler):m_stop(1),
- m_template(boost::value_initialized<block>()),
- m_template_no(0),
- m_diffic(0),
- m_thread_index(0),
- m_phandler(phandler),
- m_height(0),
- m_pausers_count(0),
- m_threads_total(0),
- m_starter_nonce(0),
- m_last_hr_merge_time(0),
- m_hashes(0),
- m_do_print_hashrate(false),
- m_do_mining(false),
- m_current_hash_rate(0)
- {
-
- }
- //-----------------------------------------------------------------------------------------------------
- miner::~miner()
- {
- stop();
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height)
- {
- CRITICAL_REGION_LOCAL(m_template_lock);
- m_template = bl;
- m_diffic = di;
- m_height = height;
- ++m_template_no;
- m_starter_nonce = crypto::rand<uint32_t>();
- return true;
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::on_block_chain_update()
- {
- if(!is_mining())
- return true;
-
- return request_block_template();
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::request_block_template()
- {
- block bl = AUTO_VAL_INIT(bl);
- difficulty_type di = AUTO_VAL_INIT(di);
- uint64_t height = AUTO_VAL_INIT(height);
- cryptonote::blobdata extra_nonce;
- if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size())
- {
- extra_nonce = m_extra_messages[m_config.current_extra_message_index];
- }
-
- if(!m_phandler->get_block_template(bl, m_mine_address, di, height, extra_nonce))
- {
- LOG_ERROR("Failed to get_block_template(), stopping mining");
- return false;
- }
- set_block_template(bl, di, height);
- return true;
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::on_idle()
- {
- m_update_block_template_interval.do_call([&](){
- if(is_mining())request_block_template();
- return true;
- });
-
- m_update_merge_hr_interval.do_call([&](){
- merge_hr();
- return true;
- });
-
- return true;
- }
- //-----------------------------------------------------------------------------------------------------
- void miner::do_print_hashrate(bool do_hr)
- {
- m_do_print_hashrate = do_hr;
- }
- //-----------------------------------------------------------------------------------------------------
- void miner::merge_hr()
- {
- if(m_last_hr_merge_time && is_mining())
- {
- m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1));
- CRITICAL_REGION_LOCAL(m_last_hash_rates_lock);
- m_last_hash_rates.push_back(m_current_hash_rate);
- if(m_last_hash_rates.size() > 19)
- m_last_hash_rates.pop_front();
- if(m_do_print_hashrate)
- {
- uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0);
- float hr = static_cast<float>(total_hr)/static_cast<float>(m_last_hash_rates.size());
- std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL;
- }
- }
- m_last_hr_merge_time = misc_utils::get_tick_count();
- m_hashes = 0;
- }
- //-----------------------------------------------------------------------------------------------------
- void miner::init_options(boost::program_options::options_description& desc)
- {
- command_line::add_arg(desc, arg_extra_messages);
- command_line::add_arg(desc, arg_start_mining);
- command_line::add_arg(desc, arg_mining_threads);
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::init(const boost::program_options::variables_map& vm, bool testnet)
- {
- if(command_line::has_arg(vm, arg_extra_messages))
- {
- std::string buff;
- bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff);
- CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages));
- std::vector<std::string> extra_vec;
- boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on );
- m_extra_messages.resize(extra_vec.size());
- for(size_t i = 0; i != extra_vec.size(); i++)
- {
- string_tools::trim(extra_vec[i]);
- if(!extra_vec[i].size())
- continue;
- std::string buff = string_encoding::base64_decode(extra_vec[i]);
- if(buff != "0")
- m_extra_messages[i] = buff;
- }
- m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string();
- m_config = AUTO_VAL_INIT(m_config);
- epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME);
- MINFO("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index);
- }
-
- if(command_line::has_arg(vm, arg_start_mining))
- {
- if(!cryptonote::get_account_address_from_str(m_mine_address, testnet, command_line::get_arg(vm, arg_start_mining)))
- {
- LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled");
- return false;
- }
- m_threads_total = 1;
- m_do_mining = true;
- if(command_line::has_arg(vm, arg_mining_threads))
- {
- m_threads_total = command_line::get_arg(vm, arg_mining_threads);
- }
- }
-
- return true;
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::is_mining() const
- {
- return !m_stop;
- }
- //-----------------------------------------------------------------------------------------------------
- const account_public_address& miner::get_mining_address() const
- {
- return m_mine_address;
- }
- //-----------------------------------------------------------------------------------------------------
- uint32_t miner::get_threads_count() const {
- return m_threads_total;
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs)
- {
- m_mine_address = adr;
- m_threads_total = static_cast<uint32_t>(threads_count);
- m_starter_nonce = crypto::rand<uint32_t>();
- CRITICAL_REGION_LOCAL(m_threads_lock);
- if(is_mining())
- {
- LOG_ERROR("Starting miner but it's already started");
- return false;
- }
-
- if(!m_threads.empty())
- {
- LOG_ERROR("Unable to start miner because there are active mining threads");
- return false;
- }
-
- if(!m_template_no)
- request_block_template();//lets update block template
-
- boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0);
- boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0);
-
- for(size_t i = 0; i != threads_count; i++)
- {
- m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this)));
- }
-
- LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" );
- return true;
- }
- //-----------------------------------------------------------------------------------------------------
- uint64_t miner::get_speed() const
- {
- if(is_mining()) {
- return m_current_hash_rate;
- }
- else {
- return 0;
- }
- }
- //-----------------------------------------------------------------------------------------------------
- void miner::send_stop_signal()
- {
- boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1);
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::stop()
- {
- MTRACE("Miner has received stop signal");
-
- if (!is_mining())
- {
- MDEBUG("Not mining - nothing to stop" );
- return true;
- }
-
- send_stop_signal();
- CRITICAL_REGION_LOCAL(m_threads_lock);
-
- for(boost::thread& th: m_threads)
- th.join();
-
- MINFO("Mining has been stopped, " << m_threads.size() << " finished" );
- m_threads.clear();
- return true;
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height)
- {
- for(; bl.nonce != std::numeric_limits<uint32_t>::max(); bl.nonce++)
- {
- crypto::hash h;
- get_block_longhash(bl, h, height);
-
- if(check_hash(h, diffic))
- {
- return true;
- }
- }
- return false;
- }
- //-----------------------------------------------------------------------------------------------------
- void miner::on_synchronized()
- {
- if(m_do_mining)
- {
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
-
- start(m_mine_address, m_threads_total, attrs);
- }
- }
- //-----------------------------------------------------------------------------------------------------
- void miner::pause()
- {
- CRITICAL_REGION_LOCAL(m_miners_count_lock);
- ++m_pausers_count;
- if(m_pausers_count == 1 && is_mining())
- MDEBUG("MINING PAUSED");
- }
- //-----------------------------------------------------------------------------------------------------
- void miner::resume()
- {
- CRITICAL_REGION_LOCAL(m_miners_count_lock);
- --m_pausers_count;
- if(m_pausers_count < 0)
- {
- m_pausers_count = 0;
- MERROR("Unexpected miner::resume() called");
- }
- if(!m_pausers_count && is_mining())
- MDEBUG("MINING RESUMED");
- }
- //-----------------------------------------------------------------------------------------------------
- bool miner::worker_thread()
- {
- uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index);
- MGINFO("Miner thread was started ["<< th_local_index << "]");
- MLOG_SET_THREAD_NAME(std::string("[miner ") + std::to_string(th_local_index) + "]");
- uint32_t nonce = m_starter_nonce + th_local_index;
- uint64_t height = 0;
- difficulty_type local_diff = 0;
- uint32_t local_template_ver = 0;
- block b;
- slow_hash_allocate_state();
- while(!m_stop)
- {
- if(m_pausers_count)//anti split workaround
- {
- misc_utils::sleep_no_w(100);
- continue;
- }
-
- if(local_template_ver != m_template_no)
- {
- CRITICAL_REGION_BEGIN(m_template_lock);
- b = m_template;
- local_diff = m_diffic;
- height = m_height;
- CRITICAL_REGION_END();
- local_template_ver = m_template_no;
- nonce = m_starter_nonce + th_local_index;
- }
-
- if(!local_template_ver)//no any set_block_template call
- {
- LOG_PRINT_L2("Block template not set yet");
- epee::misc_utils::sleep_no_w(1000);
- continue;
- }
-
- b.nonce = nonce;
- crypto::hash h;
- get_block_longhash(b, h, height);
-
- if(check_hash(h, local_diff))
- {
- //we lucky!
- ++m_config.current_extra_message_index;
- MGINFO_GREEN("Found block for difficulty: " << local_diff);
- if(!m_phandler->handle_block_found(b))
- {
- --m_config.current_extra_message_index;
- }else
- {
- //success update, lets update config
- if (!m_config_folder_path.empty())
- epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME);
- }
- }
- nonce+=m_threads_total;
- ++m_hashes;
- }
- slow_hash_free_state();
- MGINFO("Miner thread stopped ["<< th_local_index << "]");
- return true;
- }
- //-----------------------------------------------------------------------------------------------------
-}
-
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index e37ddec0d..f78f673c7 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -34,8 +34,8 @@
#include <vector>
#include "tx_pool.h"
-#include "cryptonote_format_utils.h"
-#include "cryptonote_boost_serialization.h"
+#include "cryptonote_tx_utils.h"
+#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
#include "blockchain.h"
#include "common/boost_serialization_helper.h"
@@ -59,8 +59,6 @@ namespace cryptonote
// codebase. As it stands, it is at best nontrivial to test
// whether or not changing these parameters (or adding new)
// will work correctly.
- size_t const TRANSACTION_SIZE_LIMIT_V1 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
- size_t const TRANSACTION_SIZE_LIMIT_V2 = (((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
time_t const MIN_RELAY_TIME = (60 * 5); // only start re-relaying transactions after that many seconds
time_t const MAX_RELAY_TIME = (60 * 60 * 4); // at most that many seconds between resends
float const ACCEPT_THRESHOLD = 1.0f;
@@ -78,6 +76,11 @@ namespace cryptonote
{
return amount * ACCEPT_THRESHOLD;
}
+
+ uint64_t get_transaction_size_limit(uint8_t version)
+ {
+ return get_min_block_size(version) * 125 / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ }
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
@@ -149,7 +152,7 @@ namespace cryptonote
return false;
}
- size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2);
+ size_t tx_size_limit = get_transaction_size_limit(version);
if (!kept_by_block && blob_size >= tx_size_limit)
{
LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << tx_size_limit);
@@ -255,6 +258,7 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)blob_size, receive_time), id);
+ MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size));
return true;
}
//---------------------------------------------------------------------------------
@@ -419,6 +423,13 @@ namespace cryptonote
txs.push_back(tx_vt.second.tx);
}
//------------------------------------------------------------------
+ void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ for(const auto& tx_vt: m_transactions)
+ txs.push_back(get_transaction_hash(tx_vt.second.tx));
+ }
+ //------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const
{
@@ -602,7 +613,7 @@ namespace cryptonote
}
//---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
- bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version)
+ bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
{
// Warning: This function takes already_generated_
// coins as an argument and appears to do nothing
@@ -610,7 +621,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL(m_transactions_lock);
- uint64_t best_coinbase = 0;
+ uint64_t best_coinbase = 0, coinbase = 0;
total_size = 0;
fee = 0;
@@ -618,7 +629,9 @@ namespace cryptonote
get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version);
- size_t max_total_size = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ size_t max_total_size_pre_v5 = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ size_t max_total_size_v5 = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
+ size_t max_total_size = version >= 5 ? max_total_size_v5 : max_total_size_pre_v5;
std::unordered_set<crypto::key_image> k_images;
LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
@@ -636,29 +649,49 @@ namespace cryptonote
continue;
}
- // If we're getting lower coinbase tx,
- // stop including more tx
- uint64_t block_reward;
- if(!get_block_reward(median_size, total_size + tx_it->second.blob_size, already_generated_coins, block_reward, version))
+ // start using the optimal filling algorithm from v5
+ if (version >= 5)
{
- LOG_PRINT_L2(" would exceed maximum block size");
- sorted_it++;
- continue;
+ // If we're getting lower coinbase tx,
+ // stop including more tx
+ uint64_t block_reward;
+ if(!get_block_reward(median_size, total_size + tx_it->second.blob_size, already_generated_coins, block_reward, version))
+ {
+ LOG_PRINT_L2(" would exceed maximum block size");
+ sorted_it++;
+ continue;
+ }
+ coinbase = block_reward + fee + tx_it->second.fee;
+ if (coinbase < template_accept_threshold(best_coinbase))
+ {
+ LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase));
+ sorted_it++;
+ continue;
+ }
}
- uint64_t coinbase = block_reward + fee + tx_it->second.fee;
- if (coinbase < template_accept_threshold(best_coinbase))
+ else
{
- LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase));
- sorted_it++;
- continue;
+ // If we've exceeded the penalty free size,
+ // stop including more tx
+ if (total_size > median_size)
+ {
+ LOG_PRINT_L2(" would exceed median block size");
+ break;
+ }
}
// Skip transactions that are not ready to be
// included into the blockchain or that are
// missing key images
- if (!is_transaction_ready_to_go(tx_it->second) || have_key_images(k_images, tx_it->second.tx))
+ if (!is_transaction_ready_to_go(tx_it->second))
{
- LOG_PRINT_L2(" not ready to go, or key images already seen");
+ LOG_PRINT_L2(" not ready to go");
+ sorted_it++;
+ continue;
+ }
+ if (have_key_images(k_images, tx_it->second.tx))
+ {
+ LOG_PRINT_L2(" key images already seen");
sorted_it++;
continue;
}
@@ -672,6 +705,7 @@ namespace cryptonote
LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase));
}
+ expected_reward = best_coinbase;
LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size "
<< total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)
<< " (including " << print_money(fee) << " in fees)");
@@ -682,15 +716,24 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
size_t n_removed = 0;
- size_t tx_size_limit = (version < 2 ? TRANSACTION_SIZE_LIMIT_V1 : TRANSACTION_SIZE_LIMIT_V2);
+ size_t tx_size_limit = get_transaction_size_limit(version);
for (auto it = m_transactions.begin(); it != m_transactions.end(); ) {
+ bool remove = false;
+ const crypto::hash &txid = get_transaction_hash(it->second.tx);
if (it->second.blob_size >= tx_size_limit) {
- LOG_PRINT_L1("Transaction " << get_transaction_hash(it->second.tx) << " is too big (" << it->second.blob_size << " bytes), removing it from pool");
+ LOG_PRINT_L1("Transaction " << txid << " is too big (" << it->second.blob_size << " bytes), removing it from pool");
+ remove = true;
+ }
+ else if (m_blockchain.have_tx(txid)) {
+ LOG_PRINT_L1("Transaction " << txid << " is in the blockchain, removing it from pool");
+ remove = true;
+ }
+ if (remove) {
remove_transaction_keyimages(it->second.tx);
- auto sorted_it = find_tx_in_sorted_container(it->first);
+ auto sorted_it = find_tx_in_sorted_container(txid);
if (sorted_it == m_txs_by_fee_and_receive_time.end())
{
- LOG_PRINT_L1("Removing tx " << it->first << " from tx pool, but it was not found in the sorted txs container!");
+ LOG_PRINT_L1("Removing tx " << txid << " from tx pool, but it was not found in the sorted txs container!");
}
else
{
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 2712f75bb..f68bc0bb9 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -41,8 +41,8 @@
#include "string_tools.h"
#include "syncobj.h"
#include "math_helper.h"
-#include "cryptonote_basic_impl.h"
-#include "verification_context.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "cryptonote_basic/verification_context.h"
#include "crypto/hash.h"
#include "rpc/core_rpc_server_commands_defs.h"
@@ -220,11 +220,12 @@ namespace cryptonote
* @param already_generated_coins the current total number of coins "minted"
* @param total_size return-by-reference the total size of the new block
* @param fee return-by-reference the total of fees from the included transactions
+ * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
* @param version hard fork version to use for consensus rules
*
* @return true
*/
- bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint8_t version);
+ bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
/**
* @brief get a list of all transactions in the pool
@@ -234,6 +235,13 @@ namespace cryptonote
void get_transactions(std::list<transaction>& txs) const;
/**
+ * @brief get a list of all transaction hashes in the pool
+ *
+ * @param txs return-by-reference the list of transactions
+ */
+ void get_transaction_hashes(std::vector<crypto::hash>& txs) const;
+
+ /**
* @brief get information about all transactions and key images in the pool
*
* see documentation on tx_info and spent_key_image_info for more details
diff --git a/src/cryptonote_protocol/CMakeLists.txt b/src/cryptonote_protocol/CMakeLists.txt
index 65473b117..4ce380a48 100644
--- a/src/cryptonote_protocol/CMakeLists.txt
+++ b/src/cryptonote_protocol/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -35,6 +35,8 @@ source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL})
#monero_private_headers(cryptonote_protocol ${CRYPTONOTE_PROTOCOL})
monero_add_library(cryptonote_protocol ${CRYPTONOTE_PROTOCOL})
target_link_libraries(cryptonote_protocol
+ PUBLIC
+ p2p
PRIVATE
${EXTRA_LIBRARIES})
add_dependencies(cryptonote_protocol
diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h
index 17285ee1c..2d12a84af 100644
--- a/src/cryptonote_protocol/blobdatatype.h
+++ b/src/cryptonote_protocol/blobdatatype.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index 7adc69080..fd5b980b8 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -32,7 +32,7 @@
#include <list>
#include "serialization/keyvalue_serialization.h"
-#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_protocol/blobdatatype.h"
namespace cryptonote
{
@@ -257,16 +257,16 @@ namespace cryptonote
struct request
{
- block_complete_entry b;
+ crypto::hash block_hash;
uint64_t current_blockchain_height;
std::vector<size_t> missing_tx_indices;
uint32_t hop;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(b)
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices)
- KV_SERIALIZE(hop)
- KV_SERIALIZE(current_blockchain_height)
+ KV_SERIALIZE_VAL_POD_AS_BLOB(block_hash)
+ KV_SERIALIZE(current_blockchain_height)
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices)
+ KV_SERIALIZE(hop)
END_KV_SERIALIZE_MAP()
};
};
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp
index 09c202e79..e31276031 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler-base.cpp
@@ -2,7 +2,7 @@
/// @author rfree (current maintainer in monero.cc project)
/// @brief This is the place to implement our handlers for protocol network actions, e.g. for ratelimit for download-requests
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -122,7 +122,7 @@ cryptonote_protocol_handler_base::~cryptonote_protocol_handler_base() {
void cryptonote_protocol_handler_base::handler_request_blocks_history(std::list<crypto::hash>& ids) {
using namespace epee::net_utils;
MDEBUG("### ~~~RRRR~~~~ ### sending request (type 2), limit = " << ids.size());
- MWARNING("RATE LIMIT NOT IMPLEMENTED HERE YET (download at unlimited speed?)");
+ MDEBUG("RATE LIMIT NOT IMPLEMENTED HERE YET (download at unlimited speed?)");
// TODO
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h
index 4b2de39b9..9d8bc43c2 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -2,7 +2,7 @@
/// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote)
/// @brief This is the orginal cryptonote protocol network-events handler, modified by us
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -42,9 +42,9 @@
#include "warnings.h"
#include "cryptonote_protocol_defs.h"
#include "cryptonote_protocol_handler_common.h"
-#include "cryptonote_core/connection_context.h"
-#include "cryptonote_core/cryptonote_stat_info.h"
-#include "cryptonote_core/verification_context.h"
+#include "cryptonote_basic/connection_context.h"
+#include "cryptonote_basic/cryptonote_stat_info.h"
+#include "cryptonote_basic/verification_context.h"
// #include <netinet/in.h>
#include <boost/circular_buffer.hpp>
@@ -135,6 +135,7 @@ namespace cryptonote
std::atomic<bool> m_synchronized;
bool m_one_request = true;
std::atomic<bool> m_stopping;
+ epee::critical_section m_sync_lock;
boost::mutex m_buffer_mutex;
double get_avg_block_size();
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 1309ff742..79578a34e 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -2,7 +2,7 @@
/// @author rfree (current maintainer/user in monero.cc project - most of code is from CryptoNote)
/// @brief This is the orginal cryptonote protocol network-events handler, modified by us
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -39,7 +39,7 @@
#include <list>
#include <unordered_map>
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "profile_tools.h"
#include "../../src/p2p/network_throttle-detail.hpp"
@@ -265,19 +265,24 @@ namespace cryptonote
if(context.m_state == cryptonote_connection_context::state_synchronizing)
return true;
+ uint64_t target = m_core.get_target_blockchain_height();
+ if (target == 0)
+ target = m_core.get_current_blockchain_height();
+
if(m_core.have_block(hshd.top_id))
{
context.m_state = cryptonote_connection_context::state_normal;
- if(is_inital)
+ if(is_inital && target == m_core.get_current_blockchain_height())
on_connection_synchronized();
return true;
}
+ if (hshd.current_height > target)
+ {
/* As I don't know if accessing hshd from core could be a good practice,
I prefer pushing target height to the core at the same time it is pushed to the user.
Nz. */
m_core.set_target_blockchain_height(static_cast<int64_t>(hshd.current_height));
-
int64_t diff = static_cast<int64_t>(hshd.current_height) - static_cast<int64_t>(m_core.get_current_blockchain_height());
int64_t max_block_height = max(static_cast<int64_t>(hshd.current_height),static_cast<int64_t>(m_core.get_current_blockchain_height()));
int64_t last_block_v1 = 1009826;
@@ -286,6 +291,7 @@ namespace cryptonote
<< " [Your node is " << std::abs(diff) << " blocks (" << ((abs(diff) - diff_v2) / (24 * 60 * 60 / DIFFICULTY_TARGET_V1)) + (diff_v2 / (24 * 60 * 60 / DIFFICULTY_TARGET_V2)) << " days) "
<< (0 <= diff ? std::string("behind") : std::string("ahead"))
<< "] " << ENDL << "SYNCHRONIZATION started");
+ }
LOG_PRINT_L1("Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id);
context.m_state = cryptonote_connection_context::state_synchronizing;
context.m_remote_blockchain_height = hshd.current_height;
@@ -367,7 +373,7 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context)
{
- MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (hop " << arg.hop << ", " << arg.b.txs.size() << " txes)");
+ MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (height " << arg.current_blockchain_height << ", hop " << arg.hop << ", " << arg.b.txs.size() << " txes)");
if(context.m_state != cryptonote_connection_context::state_normal)
return 1;
@@ -377,7 +383,7 @@ namespace cryptonote
transaction miner_tx;
if(parse_and_validate_block_from_blob(arg.b.block, new_block))
{
- // This is a seccond notification, we must have asked for some missing tx
+ // This is a second notification, we must have asked for some missing tx
if(!context.m_requested_objects.empty())
{
// What we asked for != to what we received ..
@@ -475,6 +481,7 @@ namespace cryptonote
// sent in our pool, so don't verify again..
if(!m_core.get_pool_transaction(tx_hash, tx))
{
+ MDEBUG("Incoming tx " << tx_hash << " not in pool, adding");
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true, false) || tvc.m_verifivation_failed)
{
@@ -496,9 +503,9 @@ namespace cryptonote
{
LOG_ERROR_CCONTEXT
(
- "sent wrong tx: failed to parse and validate transaction: \r\n"
+ "sent wrong tx: failed to parse and validate transaction: "
<< epee::string_tools::buff_to_hex_nodelimer(tx_blob)
- << "\r\n dropping connection"
+ << ", dropping connection"
);
m_p2p->drop_connection(context);
@@ -535,7 +542,28 @@ namespace cryptonote
}
else
{
- need_tx_indices.push_back(tx_idx);
+ std::vector<crypto::hash> tx_ids;
+ std::list<transaction> txes;
+ std::list<crypto::hash> missing;
+ tx_ids.push_back(tx_hash);
+ if (m_core.get_transactions(tx_ids, txes, missing) && missing.empty())
+ {
+ if (txes.size() == 1)
+ {
+ have_tx.push_back(tx_to_blob(txes.front()));
+ }
+ else
+ {
+ MERROR("1 tx requested, none not found, but " << txes.size() << " returned");
+ m_core.resume_mine();
+ return 1;
+ }
+ }
+ else
+ {
+ MDEBUG("Tx " << tx_hash << " not found in pool");
+ need_tx_indices.push_back(tx_idx);
+ }
}
++tx_idx;
@@ -544,8 +572,11 @@ namespace cryptonote
if(!need_tx_indices.empty()) // drats, we don't have everything..
{
// request non-mempool txs
+ MDEBUG("We are missing " << need_tx_indices.size() << " txes for this fluffy block");
+ for (auto txidx: need_tx_indices)
+ MDEBUG(" tx " << new_block.tx_hashes[txidx]);
NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req;
- missing_tx_req.b = arg.b;
+ missing_tx_req.block_hash = get_block_hash(new_block);
missing_tx_req.hop = arg.hop;
missing_tx_req.current_blockchain_height = arg.current_blockchain_height;
missing_tx_req.missing_tx_indices = std::move(need_tx_indices);
@@ -555,6 +586,8 @@ namespace cryptonote
}
else // whoo-hoo we've got em all ..
{
+ MDEBUG("We have all needed txes for this fluffy block");
+
block_complete_entry b;
b.block = arg.b.block;
b.txs = have_tx;
@@ -581,7 +614,7 @@ namespace cryptonote
NOTIFY_NEW_BLOCK::request reg_arg = AUTO_VAL_INIT(reg_arg);
reg_arg.hop = arg.hop;
reg_arg.current_blockchain_height = arg.current_blockchain_height;
- reg_arg.b.block = b.block;
+ reg_arg.b = b;
relay_block(reg_arg, context);
}
else if( bvc.m_marked_as_orphaned )
@@ -598,9 +631,9 @@ namespace cryptonote
{
LOG_ERROR_CCONTEXT
(
- "sent wrong block: failed to parse and validate block: \r\n"
+ "sent wrong block: failed to parse and validate block: "
<< epee::string_tools::buff_to_hex_nodelimer(arg.b.block)
- << "\r\n dropping connection"
+ << ", dropping connection"
);
m_core.resume_mine();
@@ -615,34 +648,32 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context)
{
- MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes)");
+ MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes), block hash " << arg.block_hash);
- std::list<block> local_blocks;
- std::list<transaction> local_txs;
- if(!m_core.get_blocks(arg.current_blockchain_height - 1, 1, local_blocks, local_txs))
- {
+ std::list<std::pair<cryptonote::blobdata, block>> local_blocks;
+ std::list<cryptonote::blobdata> local_txs;
- LOG_ERROR_CCONTEXT
- (
- "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX"
- << ", get_blocks( start_offset = " << (arg.current_blockchain_height - 1) << " ) failed"
- << ", dropping connection"
- );
-
+ block b;
+ if (!m_core.get_block_by_hash(arg.block_hash, b))
+ {
+ LOG_ERROR_CCONTEXT("failed to find block: " << arg.block_hash << ", dropping connection");
m_p2p->drop_connection(context);
- return 1;
+ return 1;
}
+ for (auto txidx: arg.missing_tx_indices)
+ MDEBUG(" tx " << b.tx_hashes[txidx]);
+
+ std::vector<crypto::hash> txids;
NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response;
- fluffy_response.b = arg.b;
- fluffy_response.current_blockchain_height = m_core.get_current_blockchain_height();
+ fluffy_response.b.block = t_serializable_object_to_blob(b);
+ fluffy_response.current_blockchain_height = arg.current_blockchain_height;
fluffy_response.hop = arg.hop;
- size_t local_txs_count = local_txs.size();
for(auto& tx_idx: arg.missing_tx_indices)
{
- if(tx_idx < local_txs_count)
+ if(tx_idx < b.tx_hashes.size())
{
- fluffy_response.b.txs.push_back(t_serializable_object_to_blob( *(std::next(local_txs.begin(), tx_idx)) ));
+ txids.push_back(b.tx_hashes[tx_idx]);
}
else
{
@@ -650,7 +681,8 @@ namespace cryptonote
(
"Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX"
<< ", request is asking for a tx whose index is out of bounds "
- << ", tx index = " << tx_idx << ", block_height = " << arg.current_blockchain_height
+ << ", tx index = " << tx_idx << ", block tx count " << b.tx_hashes.size()
+ << ", block_height = " << arg.current_blockchain_height
<< ", dropping connection"
);
@@ -658,7 +690,29 @@ namespace cryptonote
return 1;
}
}
-
+
+ std::list<cryptonote::transaction> txs;
+ std::list<crypto::hash> missed;
+ if (!m_core.get_transactions(txids, txs, missed))
+ {
+ LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, "
+ << "failed to get requested transactions");
+ m_p2p->drop_connection(context);
+ return 1;
+ }
+ if (!missed.empty() || txs.size() != txids.size())
+ {
+ LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, "
+ << missed.size() << " requested transactions not found" << ", dropping connection");
+ m_p2p->drop_connection(context);
+ return 1;
+ }
+
+ for(auto& tx: txs)
+ {
+ fluffy_response.b.txs.push_back(t_serializable_object_to_blob(tx));
+ }
+
LOG_PRINT_CCONTEXT_L2
(
"-->>NOTIFY_RESPONSE_FLUFFY_MISSING_TX: "
@@ -790,6 +844,8 @@ namespace cryptonote
context.m_remote_blockchain_height = arg.current_blockchain_height;
size_t count = 0;
+ std::vector<crypto::hash> block_hashes;
+ block_hashes.reserve(arg.blocks.size());
for(const block_complete_entry& block_entry: arg.blocks)
{
if (m_stopping)
@@ -801,15 +857,16 @@ namespace cryptonote
block b;
if(!parse_and_validate_block_from_blob(block_entry.block, b))
{
- LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n"
- << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection");
+ LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: "
+ << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection");
m_p2p->drop_connection(context);
return 1;
}
//to avoid concurrency in core between connections, suspend connections which delivered block later then first one
+ const crypto::hash block_hash = get_block_hash(b);
if(count == 2)
{
- if(m_core.have_block(get_block_hash(b)))
+ if(m_core.have_block(block_hash))
{
context.m_state = cryptonote_connection_context::state_idle;
context.m_needed_objects.clear();
@@ -819,7 +876,7 @@ namespace cryptonote
}
}
- auto req_it = context.m_requested_objects.find(get_block_hash(b));
+ auto req_it = context.m_requested_objects.find(block_hash);
if(req_it == context.m_requested_objects.end())
{
LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block))
@@ -836,6 +893,7 @@ namespace cryptonote
}
context.m_requested_objects.erase(req_it);
+ block_hashes.push_back(block_hash);
}
if(context.m_requested_objects.size())
@@ -848,17 +906,44 @@ namespace cryptonote
{
- m_core.pause_mine();
- epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler(
- boost::bind(&t_core::resume_mine, &m_core));
-
MLOG_YELLOW(el::Level::Debug, "Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size());
if (m_core.get_test_drop_download() && m_core.get_test_drop_download_height()) { // DISCARD BLOCKS for testing
- uint64_t previous_height = m_core.get_current_blockchain_height();
+ // we lock all the rest to avoid having multiple connections redo a lot
+ // of the same work, and one of them doing it for nothing: subsequent
+ // connections will wait until the current one's added its blocks, then
+ // will add any extra it has, if any
+ CRITICAL_REGION_LOCAL(m_sync_lock);
+
+ m_core.pause_mine();
+ epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler(
+ boost::bind(&t_core::resume_mine, &m_core));
+
+ const uint64_t previous_height = m_core.get_current_blockchain_height();
+
+ // dismiss what another connection might already have done (likely everything)
+ uint64_t top_height;
+ crypto::hash top_hash;
+ if (m_core.get_blockchain_top(top_height, top_hash)) {
+ uint64_t dismiss = 1;
+ for (const auto &h: block_hashes) {
+ if (top_hash == h) {
+ LOG_DEBUG_CC(context, "Found current top block in synced blocks, dismissing "
+ << dismiss << "/" << arg.blocks.size() << " blocks");
+ while (dismiss--)
+ arg.blocks.pop_front();
+ break;
+ }
+ ++dismiss;
+ }
+ }
+
+ if (arg.blocks.empty())
+ goto skip;
m_core.prepare_handle_incoming_blocks(arg.blocks);
+
for(const block_complete_entry& block_entry: arg.blocks)
{
if (m_stopping)
@@ -875,7 +960,7 @@ namespace cryptonote
m_core.handle_incoming_tx(tx_blob, tvc, true, true, false);
if(tvc.m_verifivation_failed)
{
- LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = "
+ LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = "
<< epee::string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection");
m_p2p->drop_connection(context);
m_core.cleanup_handle_incoming_blocks();
@@ -922,6 +1007,7 @@ namespace cryptonote
}
+skip:
request_missing_objects(context, true);
return 1;
}
@@ -1071,9 +1157,9 @@ namespace cryptonote
context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1;
if(context.m_last_response_height > context.m_remote_blockchain_height)
{
- LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height
- << "\r\nm_start_height=" << arg.start_height
- << "\r\nm_block_ids.size()=" << arg.m_block_ids.size());
+ LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with m_total_height=" << arg.total_height
+ << ", m_start_height=" << arg.start_height
+ << ", m_block_ids.size()=" << arg.m_block_ids.size());
m_p2p->drop_connection(context);
}
@@ -1110,12 +1196,12 @@ namespace cryptonote
{
if(m_core.get_testnet() && (support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS))
{
- MDEBUG("PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK");
+ LOG_DEBUG_CC(context, "PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK");
fluffyConnections.push_back(context.m_connection_id);
}
else
{
- MDEBUG("PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK");
+ LOG_DEBUG_CC(context, "PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK");
fullConnections.push_back(context.m_connection_id);
}
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
index 06061f5d0..1163a0fe8 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -32,7 +32,7 @@
#include "p2p/net_node_common.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
-#include "cryptonote_core/connection_context.h"
+#include "cryptonote_basic/connection_context.h"
namespace cryptonote
{
/************************************************************************/
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index 1b6363f7b..649823a59 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -29,9 +29,9 @@
set(blocksdat "")
if(PER_BLOCK_CHECKPOINT)
if(APPLE)
- add_custom_command(OUTPUT blocksdat.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
else()
- add_custom_command(OUTPUT blocksdat.o COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
endif()
set(blocksdat "blocksdat.o")
endif()
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index cb9fb6014..8eb3db195 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 27f9d0fd7..a7caeeffc 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -37,11 +37,11 @@ namespace daemonize {
t_command_parser_executor::t_command_parser_executor(
uint32_t ip
, uint16_t port
- , const std::string &user_agent
+ , const boost::optional<tools::login>& login
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server
)
- : m_executor(ip, port, user_agent, is_rpc, rpc_server)
+ : m_executor(ip, port, login, is_rpc, rpc_server)
{}
bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& args)
@@ -51,6 +51,13 @@ bool t_command_parser_executor::print_peer_list(const std::vector<std::string>&
return m_executor.print_peer_list();
}
+bool t_command_parser_executor::print_peer_list_stats(const std::vector<std::string>& args)
+{
+ if (!args.empty()) return false;
+
+ return m_executor.print_peer_list_stats();
+}
+
bool t_command_parser_executor::save_blockchain(const std::vector<std::string>& args)
{
if (!args.empty()) return false;
@@ -271,17 +278,30 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
if(testnet)
std::cout << "Mining to a testnet address, make sure this is intentional!" << std::endl;
uint64_t threads_count = 1;
- if(args.size() > 2)
+ bool do_background_mining = false;
+ bool ignore_battery = false;
+ if(args.size() > 4)
{
return false;
}
- else if(args.size() == 2)
+
+ if(args.size() == 4)
+ {
+ ignore_battery = args[3] == "true";
+ }
+
+ if(args.size() >= 3)
+ {
+ do_background_mining = args[2] == "true";
+ }
+
+ if(args.size() >= 2)
{
bool ok = epee::string_tools::get_xtype_from_string(threads_count, args[1]);
threads_count = (ok && 0 < threads_count) ? threads_count : 1;
}
- m_executor.start_mining(adr, threads_count, testnet);
+ m_executor.start_mining(adr, threads_count, testnet, do_background_mining, ignore_battery);
return true;
}
@@ -532,4 +552,30 @@ bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector
return m_executor.print_blockchain_dynamic_stats(nblocks);
}
+bool t_command_parser_executor::update(const std::vector<std::string>& args)
+{
+ if(args.size() != 1)
+ {
+ std::cout << "Exactly one parameter is needed: check, download, or update" << std::endl;
+ return false;
+ }
+
+ return m_executor.update(args.front());
+}
+
+bool t_command_parser_executor::relay_tx(const std::vector<std::string>& args)
+{
+ if (args.size() != 1) return false;
+
+ std::string txid;
+ crypto::hash hash;
+ if (!parse_hash256(args[0], hash))
+ {
+ std::cout << "failed to parse tx id" << std::endl;
+ return true;
+ }
+ txid = args[0];
+ return m_executor.relay_tx(txid);
+}
+
} // namespace daemonize
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 15293ade9..a453553f1 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -6,7 +6,7 @@
*/
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -36,7 +36,10 @@
#pragma once
+#include <boost/optional/optional_fwd.hpp>
+
#include "daemon/rpc_command_executor.h"
+#include "common/common_fwd.h"
#include "rpc/core_rpc_server.h"
namespace daemonize {
@@ -49,13 +52,15 @@ public:
t_command_parser_executor(
uint32_t ip
, uint16_t port
- , const std::string &user_agent
+ , const boost::optional<tools::login>& login
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server = NULL
);
bool print_peer_list(const std::vector<std::string>& args);
+ bool print_peer_list_stats(const std::vector<std::string>& args);
+
bool save_blockchain(const std::vector<std::string>& args);
bool show_hash_rate(const std::vector<std::string>& args);
@@ -125,6 +130,10 @@ public:
bool alt_chain_info(const std::vector<std::string>& args);
bool print_blockchain_dynamic_stats(const std::vector<std::string>& args);
+
+ bool update(const std::vector<std::string>& args);
+
+ bool relay_tx(const std::vector<std::string>& args);
};
} // namespace daemonize
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 95fd3178c..21f550a85 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -40,11 +40,11 @@ namespace p = std::placeholders;
t_command_server::t_command_server(
uint32_t ip
, uint16_t port
- , const std::string &user_agent
+ , const boost::optional<tools::login>& login
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server
)
- : m_parser(ip, port, user_agent, is_rpc, rpc_server)
+ : m_parser(ip, port, login, is_rpc, rpc_server)
, m_command_lookup()
, m_is_rpc(is_rpc)
{
@@ -69,6 +69,11 @@ t_command_server::t_command_server(
, "Print peer list"
);
m_command_lookup.set_handler(
+ "print_pl_stats"
+ , std::bind(&t_command_parser_executor::print_peer_list_stats, &m_parser, p::_1)
+ , "Print peer list stats"
+ );
+ m_command_lookup.set_handler(
"print_cn"
, std::bind(&t_command_parser_executor::print_connections, &m_parser, p::_1)
, "Print connections"
@@ -96,7 +101,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"start_mining"
, std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1)
- , "Start mining for specified address, start_mining <addr> [<threads>], default 1 thread"
+ , "Start mining for specified address, start_mining <addr> [<threads>] [do_background_mining] [ignore_battery], default 1 thread, no background mining"
);
m_command_lookup.set_handler(
"stop_mining"
@@ -238,6 +243,16 @@ t_command_server::t_command_server(
, std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1)
, "Print information about current blockchain dynamic state"
);
+ m_command_lookup.set_handler(
+ "update"
+ , std::bind(&t_command_parser_executor::update, &m_parser, p::_1)
+ , "subcommands: check (check if an update is available), download (download it is there is), update (not implemented)"
+ );
+ m_command_lookup.set_handler(
+ "relay_tx"
+ , std::bind(&t_command_parser_executor::relay_tx, &m_parser, p::_1)
+ , "Relay a given transaction by its txid"
+ );
}
bool t_command_server::process_command_str(const std::string& cmd)
diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h
index fb1702aae..476b75141 100644
--- a/src/daemon/command_server.h
+++ b/src/daemon/command_server.h
@@ -9,7 +9,7 @@ Passing RPC commands:
*/
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -39,6 +39,8 @@ Passing RPC commands:
#pragma once
+#include <boost/optional/optional_fwd.hpp>
+#include "common/common_fwd.h"
#include "console_handler.h"
#include "daemon/command_parser_executor.h"
@@ -54,7 +56,7 @@ public:
t_command_server(
uint32_t ip
, uint16_t port
- , const std::string &user_agent
+ , const boost::optional<tools::login>& login
, bool is_rpc = true
, cryptonote::core_rpc_server* rpc_server = NULL
);
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 23f7a9f63..9e6ff5e29 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -45,7 +45,6 @@ public:
static void init_options(boost::program_options::options_description & option_spec)
{
cryptonote::core::init_options(option_spec);
- cryptonote::miner::init_options(option_spec);
}
private:
typedef cryptonote::t_cryptonote_protocol_handler<cryptonote::core> t_protocol_raw;
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 287c30cb4..241cb3883 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -33,6 +33,7 @@
#include "misc_log_ex.h"
#include "daemon/daemon.h"
+#include "common/password.h"
#include "common/util.h"
#include "daemon/core.h"
#include "daemon/p2p.h"
@@ -123,22 +124,24 @@ bool t_daemon::run(bool interactive)
return false;
mp_internals->rpc.run();
- daemonize::t_command_server* rpc_commands;
+ std::unique_ptr<daemonize::t_command_server> rpc_commands;
if (interactive)
{
- rpc_commands = new daemonize::t_command_server(0, 0, "", false, mp_internals->rpc.get_server());
+ // The first three variables are not used when the fourth is false
+ rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpc.get_server()));
rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
}
mp_internals->p2p.run(); // blocks until p2p goes down
- if (interactive)
+ if (rpc_commands)
{
rpc_commands->stop_handling();
}
mp_internals->rpc.stop();
+ mp_internals->core.get().get_miner().stop();
MGINFO("Node stopped.");
return true;
}
@@ -160,6 +163,7 @@ void t_daemon::stop()
{
throw std::runtime_error{"Can't stop stopped daemon"};
}
+ mp_internals->core.get().get_miner().stop();
mp_internals->p2p.stop();
mp_internals->rpc.stop();
mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return
diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h
index c8fae5c28..2b9f18669 100644
--- a/src/daemon/daemon.h
+++ b/src/daemon/daemon.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp
index ac5803cfb..6130bfa28 100644
--- a/src/daemon/executor.cpp
+++ b/src/daemon/executor.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -63,6 +63,13 @@ namespace daemonize
return t_daemon{vm};
}
+ bool t_executor::run_non_interactive(
+ boost::program_options::variables_map const & vm
+ )
+ {
+ return t_daemon{vm}.run(false);
+ }
+
bool t_executor::run_interactive(
boost::program_options::variables_map const & vm
)
diff --git a/src/daemon/executor.h b/src/daemon/executor.h
index a6b47b93d..137e7209c 100644
--- a/src/daemon/executor.h
+++ b/src/daemon/executor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -56,6 +56,10 @@ namespace daemonize
boost::program_options::variables_map const & vm
);
+ bool run_non_interactive(
+ boost::program_options::variables_map const & vm
+ );
+
bool run_interactive(
boost::program_options::variables_map const & vm
);
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index e08065ccd..19dd02171 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,9 +30,10 @@
#include "common/command_line.h"
#include "common/scoped_message_writer.h"
+#include "common/password.h"
#include "common/util.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/miner.h"
+#include "cryptonote_basic/miner.h"
#include "daemon/command_server.h"
#include "daemon/daemon.h"
#include "daemon/executor.h"
@@ -40,6 +41,7 @@
#include "misc_log_ex.h"
#include "p2p/net_node.h"
#include "rpc/core_rpc_server.h"
+#include "rpc/rpc_args.h"
#include "daemon/command_line_args.h"
#include "blockchain_db/db_types.h"
@@ -166,7 +168,6 @@ int main(int argc, char const * argv[])
// Create data dir if it doesn't exist
boost::filesystem::path data_dir = boost::filesystem::absolute(
command_line::get_arg(vm, data_dir_arg));
- tools::create_directories_if_necessary(data_dir.string());
// FIXME: not sure on windows implementation default, needs further review
//bf::path relative_path_base = daemonizer::get_relative_path_base(vm);
@@ -214,19 +215,22 @@ int main(int argc, char const * argv[])
mlog_set_log(command_line::get_arg(vm, daemon_args::arg_log_level).c_str());
}
+ // after logs initialized
+ tools::create_directories_if_necessary(data_dir.string());
+
// If there are positional options, we're running a daemon command
{
auto command = command_line::get_arg(vm, daemon_args::arg_command);
if (command.size())
{
- auto rpc_ip_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_ip);
+ const cryptonote::rpc_args::descriptors arg{};
+ auto rpc_ip_str = command_line::get_arg(vm, arg.rpc_bind_ip);
auto rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_rpc_bind_port);
if (testnet_mode)
{
rpc_port_str = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_testnet_rpc_bind_port);
}
- auto user_agent = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_user_agent);
uint32_t rpc_ip;
uint16_t rpc_port;
@@ -241,7 +245,20 @@ int main(int argc, char const * argv[])
return 1;
}
- daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, user_agent};
+ boost::optional<tools::login> login{};
+ if (command_line::has_arg(vm, arg.rpc_login))
+ {
+ login = tools::login::parse(
+ command_line::get_arg(vm, arg.rpc_login), false, "Daemon client password"
+ );
+ if (!login)
+ {
+ std::cerr << "Failed to obtain password" << std::endl;
+ return 1;
+ }
+ }
+
+ daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, std::move(login)};
if (rpc_commands.process_command_vec(command))
{
return 0;
diff --git a/src/daemon/p2p.h b/src/daemon/p2p.h
index f29c2d822..309eb7453 100644
--- a/src/daemon/p2p.h
+++ b/src/daemon/p2p.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemon/protocol.h b/src/daemon/protocol.h
index 0b0f95988..fc5edbcaa 100644
--- a/src/daemon/protocol.h
+++ b/src/daemon/protocol.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -79,7 +79,6 @@ public:
m_protocol.deinit();
m_protocol.set_p2p_endpoint(nullptr);
MGINFO("Cryptonote protocol stopped successfully");
- tools::success_msg_writer() << "Daemon stopped successfully";
} catch (...) {
LOG_ERROR("Failed to stop cryptonote protocol!");
}
diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h
index 8b0d5808d..0ecfdd120 100644
--- a/src/daemon/rpc.h
+++ b/src/daemon/rpc.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index f7d85b5ef..31f432918 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -29,11 +29,12 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "string_tools.h"
+#include "common/password.h"
#include "common/scoped_message_writer.h"
#include "daemon/rpc_command_executor.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "cryptonote_core/cryptonote_core.h"
-#include "cryptonote_core/hardfork.h"
+#include "cryptonote_basic/hardfork.h"
#include <boost/format.hpp>
#include <ctime>
#include <string>
@@ -90,12 +91,19 @@ namespace {
s = boost::lexical_cast<std::string>(dt/(3600*24)) + " days";
return s + " " + (t > now ? "in the future" : "ago");
}
+
+ std::string make_error(const std::string &base, const std::string &status)
+ {
+ if (status == CORE_RPC_STATUS_OK)
+ return base;
+ return base + " -- " + status;
+ }
}
t_rpc_command_executor::t_rpc_command_executor(
uint32_t ip
, uint16_t port
- , const std::string &user_agent
+ , const boost::optional<tools::login>& login
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server
)
@@ -103,7 +111,10 @@ t_rpc_command_executor::t_rpc_command_executor(
{
if (is_rpc)
{
- m_rpc_client = new tools::t_rpc_client(ip, port);
+ boost::optional<epee::net_utils::http::login> http_login{};
+ if (login)
+ http_login.emplace(login->username, login->password.password());
+ m_rpc_client = new tools::t_rpc_client(ip, port, std::move(http_login));
}
else
{
@@ -158,6 +169,34 @@ bool t_rpc_command_executor::print_peer_list() {
return true;
}
+bool t_rpc_command_executor::print_peer_list_stats() {
+ cryptonote::COMMAND_RPC_GET_PEER_LIST::request req;
+ cryptonote::COMMAND_RPC_GET_PEER_LIST::response res;
+
+ std::string failure_message = "Couldn't retrieve peer list";
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str()))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_peer_list(req, res) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << failure_message;
+ return false;
+ }
+ }
+
+ tools::msg_writer()
+ << "White list size: " << res.white_list.size() << "/" << P2P_LOCAL_WHITE_PEERLIST_LIMIT << " (" << res.white_list.size() * 100.0 / P2P_LOCAL_WHITE_PEERLIST_LIMIT << "%)" << std::endl
+ << "Gray list size: " << res.gray_list.size() << "/" << P2P_LOCAL_GRAY_PEERLIST_LIMIT << " (" << res.gray_list.size() * 100.0 / P2P_LOCAL_GRAY_PEERLIST_LIMIT << "%)";
+
+ return true;
+}
+
bool t_rpc_command_executor::save_blockchain() {
cryptonote::COMMAND_RPC_SAVE_BC::request req;
cryptonote::COMMAND_RPC_SAVE_BC::response res;
@@ -175,7 +214,7 @@ bool t_rpc_command_executor::save_blockchain() {
{
if (!m_rpc_server->on_save_bc(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -203,7 +242,7 @@ bool t_rpc_command_executor::show_hash_rate() {
{
if (!m_rpc_server->on_set_log_hash_rate(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
}
}
@@ -230,7 +269,7 @@ bool t_rpc_command_executor::hide_hash_rate() {
{
if (!m_rpc_server->on_set_log_hash_rate(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -257,7 +296,7 @@ bool t_rpc_command_executor::show_difficulty() {
{
if (!m_rpc_server->on_get_info(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message.c_str(), res.status);
return true;
}
}
@@ -341,12 +380,12 @@ bool t_rpc_command_executor::show_status() {
{
if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, ires.status);
return true;
}
if (!m_rpc_server->on_hard_fork_info(hfreq, hfres, error_resp) || hfres.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, hfres.status);
return true;
}
if (!m_rpc_server->on_mining_status(mreq, mres))
@@ -361,19 +400,19 @@ bool t_rpc_command_executor::show_status() {
}
else if (mres.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, mres.status);
return true;
}
}
std::time_t uptime = std::time(nullptr) - ires.start_time;
- tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u+%u connections, uptime %ud %uh %um %us")
+ tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
% (unsigned long long)ires.height
% (unsigned long long)(ires.target_height >= ires.height ? ires.target_height : ires.height)
% get_sync_percentage(ires)
% (ires.testnet ? "testnet" : "mainnet")
- % (mining_busy ? "syncing" : mres.active ? "mining at " + get_mining_speed(mres.speed) : "not mining")
+ % (mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining")
% get_mining_speed(ires.difficulty / ires.target)
% (unsigned)hfres.version
% get_fork_extra_info(hfres.earliest_height, ires.height, ires.target)
@@ -407,7 +446,7 @@ bool t_rpc_command_executor::print_connections() {
{
if (!m_rpc_server->on_get_connections(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -472,7 +511,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
{
if (!m_rpc_server->on_get_block_headers_range(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -512,7 +551,7 @@ bool t_rpc_command_executor::set_log_level(int8_t level) {
{
if (!m_rpc_server->on_set_log_level(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -540,7 +579,7 @@ bool t_rpc_command_executor::set_log_categories(const std::string &categories) {
{
if (!m_rpc_server->on_set_log_categories(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -567,7 +606,7 @@ bool t_rpc_command_executor::print_height() {
{
if (!m_rpc_server->on_get_height(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -597,7 +636,7 @@ bool t_rpc_command_executor::print_block_by_hash(crypto::hash block_hash) {
{
if (!m_rpc_server->on_get_block(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -628,7 +667,7 @@ bool t_rpc_command_executor::print_block_by_height(uint64_t height) {
{
if (!m_rpc_server->on_get_block(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -657,7 +696,7 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash) {
{
if (!m_rpc_server->on_get_transactions(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -720,7 +759,7 @@ bool t_rpc_command_executor::is_key_image_spent(const crypto::key_image &ki) {
{
if (!m_rpc_server->on_is_key_image_spent(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -755,7 +794,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
{
if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -774,6 +813,7 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
<< tx_info.tx_json << std::endl
<< "blob_size: " << tx_info.blob_size << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
+ << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
@@ -838,7 +878,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
{
if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -855,6 +895,7 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
tools::msg_writer() << "id: " << tx_info.id_hash << std::endl
<< "blob_size: " << tx_info.blob_size << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
+ << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
@@ -886,7 +927,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
{
if (!m_rpc_server->on_get_transaction_pool(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -919,18 +960,20 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
size_t avg_bytes = n_transactions ? bytes / n_transactions : 0;
tools::msg_writer() << n_transactions << " tx(es), " << bytes << " bytes total (min " << min_bytes << ", max " << max_bytes << ", avg " << avg_bytes << ")" << std::endl
- << "fees " << cryptonote::print_money(fee) << " (avg " << cryptonote::print_money(n_transactions ? fee / n_transactions : 0) << " per tx)" << std::endl
+ << "fees " << cryptonote::print_money(fee) << " (avg " << cryptonote::print_money(n_transactions ? fee / n_transactions : 0) << " per tx" << ", " << cryptonote::print_money(bytes ? fee / bytes : 0) << " per byte )" << std::endl
<< n_not_relayed << " not relayed, " << n_failing << " failing, " << n_10m << " older than 10 minutes (oldest " << (oldest == 0 ? "-" : get_human_time_ago(oldest, now)) << ")" << std::endl;
return true;
}
-bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet) {
+bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining, bool ignore_battery) {
cryptonote::COMMAND_RPC_START_MINING::request req;
cryptonote::COMMAND_RPC_START_MINING::response res;
req.miner_address = cryptonote::get_account_address_as_str(testnet, address);
req.threads_count = num_threads;
-
+ req.do_background_mining = do_background_mining;
+ req.ignore_battery = ignore_battery;
+
std::string fail_message = "Mining did not start";
if (m_is_rpc)
@@ -944,7 +987,7 @@ bool t_rpc_command_executor::start_mining(cryptonote::account_public_address add
{
if (!m_rpc_server->on_start_mining(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -969,7 +1012,7 @@ bool t_rpc_command_executor::stop_mining() {
{
if (!m_rpc_server->on_stop_mining(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1010,7 +1053,7 @@ bool t_rpc_command_executor::stop_daemon()
{
if (!m_rpc_server->on_stop_daemon(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1108,7 +1151,7 @@ bool t_rpc_command_executor::out_peers(uint64_t limit)
{
if (!m_rpc_server->on_out_peers(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1136,7 +1179,7 @@ bool t_rpc_command_executor::start_save_graph()
{
if (!m_rpc_server->on_start_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1162,7 +1205,7 @@ bool t_rpc_command_executor::stop_save_graph()
{
if (!m_rpc_server->on_stop_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1189,7 +1232,7 @@ bool t_rpc_command_executor::hard_fork_info(uint8_t version)
{
if (!m_rpc_server->on_hard_fork_info(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1220,7 +1263,7 @@ bool t_rpc_command_executor::print_bans()
{
if (!m_rpc_server->on_get_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1262,7 +1305,7 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
{
if (!m_rpc_server->on_set_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1298,7 +1341,7 @@ bool t_rpc_command_executor::unban(const std::string &ip)
{
if (!m_rpc_server->on_set_bans(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1325,9 +1368,9 @@ bool t_rpc_command_executor::flush_txpool(const std::string &txid)
}
else
{
- if (!m_rpc_server->on_flush_txpool(req, res, error_resp))
+ if (!m_rpc_server->on_flush_txpool(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1356,9 +1399,9 @@ bool t_rpc_command_executor::output_histogram(uint64_t min_count, uint64_t max_c
}
else
{
- if (!m_rpc_server->on_get_output_histogram(req, res, error_resp))
+ if (!m_rpc_server->on_get_output_histogram(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1393,9 +1436,9 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou
}
else
{
- if (!m_rpc_server->on_get_coinbase_tx_sum(req, res, error_resp))
+ if (!m_rpc_server->on_get_coinbase_tx_sum(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1410,6 +1453,8 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou
bool t_rpc_command_executor::alt_chain_info()
{
+ cryptonote::COMMAND_RPC_GET_INFO::request ireq;
+ cryptonote::COMMAND_RPC_GET_INFO::response ires;
cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::request req;
cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::response res;
epee::json_rpc::error error_resp;
@@ -1418,6 +1463,10 @@ bool t_rpc_command_executor::alt_chain_info()
if (m_is_rpc)
{
+ if (!m_rpc_client->rpc_request(ireq, ires, "/getinfo", fail_message.c_str()))
+ {
+ return true;
+ }
if (!m_rpc_client->json_rpc_request(req, res, "get_alternate_chains", fail_message.c_str()))
{
return true;
@@ -1425,9 +1474,14 @@ bool t_rpc_command_executor::alt_chain_info()
}
else
{
+ if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, ires.status);
+ return true;
+ }
if (!m_rpc_server->on_get_alternate_chains(req, res, error_resp))
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
return true;
}
}
@@ -1435,8 +1489,9 @@ bool t_rpc_command_executor::alt_chain_info()
tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
for (const auto chain: res.chains)
{
- tools::msg_writer() << chain.length << " blocks long, branching at height " << (chain.height - chain.length + 1)
- << ", difficulty " << chain.difficulty << ": " << chain.block_hash;
+ uint64_t start_height = (chain.height - chain.length + 1);
+ tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
+ << " deep), diff " << chain.difficulty << ": " << chain.block_hash;
}
return true;
}
@@ -1460,7 +1515,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
{
return true;
}
- if (!m_rpc_client->rpc_request(fereq, feres, "/get_fee_estimate", fail_message.c_str()))
+ if (!m_rpc_client->json_rpc_request(fereq, feres, "get_fee_estimate", fail_message.c_str()))
{
return true;
}
@@ -1469,12 +1524,12 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
{
if (!m_rpc_server->on_get_info(ireq, ires) || ires.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, ires.status);
return true;
}
if (!m_rpc_server->on_get_per_kb_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, feres.status);
return true;
}
}
@@ -1491,7 +1546,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
bhreq.end_height = ires.height - 1;
if (m_is_rpc)
{
- if (!m_rpc_client->rpc_request(bhreq, bhres, "/getblockheadersrange", fail_message.c_str()))
+ if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheadersrange", fail_message.c_str()))
{
return true;
}
@@ -1500,7 +1555,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
{
if (!m_rpc_server->on_get_block_headers_range(bhreq, bhres, error_resp) || bhres.status != CORE_RPC_STATUS_OK)
{
- tools::fail_msg_writer() << fail_message.c_str();
+ tools::fail_msg_writer() << make_error(fail_message, bhres.status);
return true;
}
}
@@ -1546,4 +1601,79 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
return true;
}
+bool t_rpc_command_executor::update(const std::string &command)
+{
+ cryptonote::COMMAND_RPC_UPDATE::request req;
+ cryptonote::COMMAND_RPC_UPDATE::response res;
+ epee::json_rpc::error error_resp;
+
+ std::string fail_message = "Problem fetching info";
+
+ req.command = command;
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->rpc_request(req, res, "/update", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_update(req, res) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+
+ if (!res.update)
+ {
+ tools::msg_writer() << "No update available";
+ return true;
+ }
+
+ tools::msg_writer() << "Update available: v" << res.version << ": " << res.user_uri << ", hash " << res.hash;
+ if (command == "check")
+ return true;
+
+ if (!res.path.empty())
+ tools::msg_writer() << "Update downloaded to: " << res.path;
+ else
+ tools::msg_writer() << "Update download failed: " << res.status;
+ if (command == "download")
+ return true;
+
+ tools::msg_writer() << "'update' not implemented yet";
+
+ return true;
+}
+
+bool t_rpc_command_executor::relay_tx(const std::string &txid)
+{
+ cryptonote::COMMAND_RPC_RELAY_TX::request req;
+ cryptonote::COMMAND_RPC_RELAY_TX::response res;
+ std::string fail_message = "Unsuccessful";
+ epee::json_rpc::error error_resp;
+
+ req.txids.push_back(txid);
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(req, res, "relay_tx", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_relay_tx(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+
+ return true;
+}
+
}// namespace daemonize
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index afcd99d32..3f551bd14 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -6,7 +6,7 @@
*/
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -38,6 +38,9 @@
#pragma once
+#include <boost/optional/optional_fwd.hpp>
+
+#include "common/common_fwd.h"
#include "common/rpc_client.h"
#include "misc_log_ex.h"
#include "cryptonote_core/cryptonote_core.h"
@@ -60,7 +63,7 @@ public:
t_rpc_command_executor(
uint32_t ip
, uint16_t port
- , const std::string &user_agent
+ , const boost::optional<tools::login>& user
, bool is_rpc = true
, cryptonote::core_rpc_server* rpc_server = NULL
);
@@ -69,6 +72,8 @@ public:
bool print_peer_list();
+ bool print_peer_list_stats();
+
bool save_blockchain();
bool show_hash_rate();
@@ -103,7 +108,7 @@ public:
bool print_transaction_pool_stats();
- bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet);
+ bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, bool testnet, bool do_background_mining = false, bool ignore_battery = false);
bool stop_mining();
@@ -146,6 +151,10 @@ public:
bool alt_chain_info();
bool print_blockchain_dynamic_stats(uint64_t nblocks);
+
+ bool update(const std::string &command);
+
+ bool relay_tx(const std::string &txid);
};
} // namespace daemonize
diff --git a/src/daemonizer/CMakeLists.txt b/src/daemonizer/CMakeLists.txt
index 964c8cc6f..c8cb1b445 100644
--- a/src/daemonizer/CMakeLists.txt
+++ b/src/daemonizer/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
diff --git a/src/daemonizer/daemonizer.h b/src/daemonizer/daemonizer.h
index 3e30d85ee..5f53d062b 100644
--- a/src/daemonizer/daemonizer.h
+++ b/src/daemonizer/daemonizer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl
index 926f0a3ac..f8be15dda 100644
--- a/src/daemonizer/posix_daemonizer.inl
+++ b/src/daemonizer/posix_daemonizer.inl
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -43,6 +43,10 @@ namespace daemonizer
"detach"
, "Run as daemon"
};
+ const command_line::arg_descriptor<bool> arg_non_interactive = {
+ "non-interactive"
+ , "Run non-interactive"
+ };
}
inline void init_options(
@@ -51,6 +55,7 @@ namespace daemonizer
)
{
command_line::add_arg(normal_options, arg_detach);
+ command_line::add_arg(normal_options, arg_non_interactive);
}
inline boost::filesystem::path get_default_data_dir()
@@ -79,6 +84,10 @@ namespace daemonizer
auto daemon = executor.create_daemon(vm);
return daemon.run();
}
+ else if (command_line::has_arg(vm, arg_non_interactive))
+ {
+ return executor.run_non_interactive(vm);
+ }
else
{
//LOG_PRINT_L0("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL);
diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h
index ef627a43e..459417d25 100644
--- a/src/daemonizer/posix_fork.h
+++ b/src/daemonizer/posix_fork.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl
index d8023944e..012cd1e67 100644
--- a/src/daemonizer/windows_daemonizer.inl
+++ b/src/daemonizer/windows_daemonizer.inl
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp
index f8cc0c6c7..d540f5bf8 100644
--- a/src/daemonizer/windows_service.cpp
+++ b/src/daemonizer/windows_service.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemonizer/windows_service.h b/src/daemonizer/windows_service.h
index 2712d13a3..070434b04 100644
--- a/src/daemonizer/windows_service.h
+++ b/src/daemonizer/windows_service.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/daemonizer/windows_service_runner.h b/src/daemonizer/windows_service_runner.h
index f4258a215..528d13a53 100644
--- a/src/daemonizer/windows_service_runner.h
+++ b/src/daemonizer/windows_service_runner.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt
index 936c43b99..4db19d195 100644
--- a/src/mnemonics/CMakeLists.txt
+++ b/src/mnemonics/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -33,7 +33,10 @@ set(mnemonics_headers)
set(mnemonics_private_headers
electrum-words.h
+ chinese_simplified.h
english.h
+ dutch.h
+ french.h
german.h
italian.h
japanese.h
@@ -51,5 +54,7 @@ monero_add_library(mnemonics
${mnemonics_headers}
${mnemonics_private_headers})
target_link_libraries(mnemonics
+ PUBLIC
+ ${Boost_SYSTEM_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h
new file mode 100644
index 000000000..413186733
--- /dev/null
+++ b/src/mnemonics/chinese_simplified.h
@@ -0,0 +1,1709 @@
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is 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.
+
+/*!
+ * \file chinese_simplified.h
+ *
+ * \brief Simplified Chinese word list and map.
+ */
+
+#ifndef CHINESE_SIMPLIFIED_H
+#define CHINESE_SIMPLIFIED_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class Chinese_Simplified: public Base
+ {
+ public:
+ Chinese_Simplified(): Base("Chinese (Simplified)", std::vector<std::string>({
+ "的",
+ "一",
+ "是",
+ "在",
+ "不",
+ "了",
+ "有",
+ "和",
+ "人",
+ "这",
+ "中",
+ "大",
+ "为",
+ "上",
+ "个",
+ "国",
+ "我",
+ "以",
+ "要",
+ "他",
+ "时",
+ "来",
+ "用",
+ "们",
+ "生",
+ "到",
+ "作",
+ "地",
+ "于",
+ "出",
+ "就",
+ "分",
+ "对",
+ "成",
+ "会",
+ "可",
+ "主",
+ "发",
+ "年",
+ "动",
+ "同",
+ "工",
+ "也",
+ "能",
+ "下",
+ "过",
+ "子",
+ "说",
+ "产",
+ "种",
+ "面",
+ "而",
+ "方",
+ "后",
+ "多",
+ "定",
+ "行",
+ "学",
+ "法",
+ "所",
+ "民",
+ "得",
+ "经",
+ "十",
+ "三",
+ "之",
+ "进",
+ "着",
+ "等",
+ "部",
+ "度",
+ "家",
+ "电",
+ "力",
+ "里",
+ "如",
+ "水",
+ "化",
+ "高",
+ "自",
+ "二",
+ "理",
+ "起",
+ "小",
+ "物",
+ "现",
+ "实",
+ "加",
+ "量",
+ "都",
+ "两",
+ "体",
+ "制",
+ "机",
+ "当",
+ "使",
+ "点",
+ "从",
+ "业",
+ "本",
+ "去",
+ "把",
+ "性",
+ "好",
+ "应",
+ "开",
+ "它",
+ "合",
+ "还",
+ "因",
+ "由",
+ "其",
+ "些",
+ "然",
+ "前",
+ "外",
+ "天",
+ "政",
+ "四",
+ "日",
+ "那",
+ "社",
+ "义",
+ "事",
+ "平",
+ "形",
+ "相",
+ "全",
+ "表",
+ "间",
+ "样",
+ "与",
+ "关",
+ "各",
+ "重",
+ "新",
+ "线",
+ "内",
+ "数",
+ "正",
+ "心",
+ "反",
+ "你",
+ "明",
+ "看",
+ "原",
+ "又",
+ "么",
+ "利",
+ "比",
+ "或",
+ "但",
+ "质",
+ "气",
+ "第",
+ "向",
+ "道",
+ "命",
+ "此",
+ "变",
+ "条",
+ "只",
+ "没",
+ "结",
+ "解",
+ "问",
+ "意",
+ "建",
+ "月",
+ "公",
+ "无",
+ "系",
+ "军",
+ "很",
+ "情",
+ "者",
+ "最",
+ "立",
+ "代",
+ "想",
+ "已",
+ "通",
+ "并",
+ "提",
+ "直",
+ "题",
+ "党",
+ "程",
+ "展",
+ "五",
+ "果",
+ "料",
+ "象",
+ "员",
+ "革",
+ "位",
+ "入",
+ "常",
+ "文",
+ "总",
+ "次",
+ "品",
+ "式",
+ "活",
+ "设",
+ "及",
+ "管",
+ "特",
+ "件",
+ "长",
+ "求",
+ "老",
+ "头",
+ "基",
+ "资",
+ "边",
+ "流",
+ "路",
+ "级",
+ "少",
+ "图",
+ "山",
+ "统",
+ "接",
+ "知",
+ "较",
+ "将",
+ "组",
+ "见",
+ "计",
+ "别",
+ "她",
+ "手",
+ "角",
+ "期",
+ "根",
+ "论",
+ "运",
+ "农",
+ "指",
+ "几",
+ "九",
+ "区",
+ "强",
+ "放",
+ "决",
+ "西",
+ "被",
+ "干",
+ "做",
+ "必",
+ "战",
+ "先",
+ "回",
+ "则",
+ "任",
+ "取",
+ "据",
+ "处",
+ "队",
+ "南",
+ "给",
+ "色",
+ "光",
+ "门",
+ "即",
+ "保",
+ "治",
+ "北",
+ "造",
+ "百",
+ "规",
+ "热",
+ "领",
+ "七",
+ "海",
+ "口",
+ "东",
+ "导",
+ "器",
+ "压",
+ "志",
+ "世",
+ "金",
+ "增",
+ "争",
+ "济",
+ "阶",
+ "油",
+ "思",
+ "术",
+ "极",
+ "交",
+ "受",
+ "联",
+ "什",
+ "认",
+ "六",
+ "共",
+ "权",
+ "收",
+ "证",
+ "改",
+ "清",
+ "美",
+ "再",
+ "采",
+ "转",
+ "更",
+ "单",
+ "风",
+ "切",
+ "打",
+ "白",
+ "教",
+ "速",
+ "花",
+ "带",
+ "安",
+ "场",
+ "身",
+ "车",
+ "例",
+ "真",
+ "务",
+ "具",
+ "万",
+ "每",
+ "目",
+ "至",
+ "达",
+ "走",
+ "积",
+ "示",
+ "议",
+ "声",
+ "报",
+ "斗",
+ "完",
+ "类",
+ "八",
+ "离",
+ "华",
+ "名",
+ "确",
+ "才",
+ "科",
+ "张",
+ "信",
+ "马",
+ "节",
+ "话",
+ "米",
+ "整",
+ "空",
+ "元",
+ "况",
+ "今",
+ "集",
+ "温",
+ "传",
+ "土",
+ "许",
+ "步",
+ "群",
+ "广",
+ "石",
+ "记",
+ "需",
+ "段",
+ "研",
+ "界",
+ "拉",
+ "林",
+ "律",
+ "叫",
+ "且",
+ "究",
+ "观",
+ "越",
+ "织",
+ "装",
+ "影",
+ "算",
+ "低",
+ "持",
+ "音",
+ "众",
+ "书",
+ "布",
+ "复",
+ "容",
+ "儿",
+ "须",
+ "际",
+ "商",
+ "非",
+ "验",
+ "连",
+ "断",
+ "深",
+ "难",
+ "近",
+ "矿",
+ "千",
+ "周",
+ "委",
+ "素",
+ "技",
+ "备",
+ "半",
+ "办",
+ "青",
+ "省",
+ "列",
+ "习",
+ "响",
+ "约",
+ "支",
+ "般",
+ "史",
+ "感",
+ "劳",
+ "便",
+ "团",
+ "往",
+ "酸",
+ "历",
+ "市",
+ "克",
+ "何",
+ "除",
+ "消",
+ "构",
+ "府",
+ "称",
+ "太",
+ "准",
+ "精",
+ "值",
+ "号",
+ "率",
+ "族",
+ "维",
+ "划",
+ "选",
+ "标",
+ "写",
+ "存",
+ "候",
+ "毛",
+ "亲",
+ "快",
+ "效",
+ "斯",
+ "院",
+ "查",
+ "江",
+ "型",
+ "眼",
+ "王",
+ "按",
+ "格",
+ "养",
+ "易",
+ "置",
+ "派",
+ "层",
+ "片",
+ "始",
+ "却",
+ "专",
+ "状",
+ "育",
+ "厂",
+ "京",
+ "识",
+ "适",
+ "属",
+ "圆",
+ "包",
+ "火",
+ "住",
+ "调",
+ "满",
+ "县",
+ "局",
+ "照",
+ "参",
+ "红",
+ "细",
+ "引",
+ "听",
+ "该",
+ "铁",
+ "价",
+ "严",
+ "首",
+ "底",
+ "液",
+ "官",
+ "德",
+ "随",
+ "病",
+ "苏",
+ "失",
+ "尔",
+ "死",
+ "讲",
+ "配",
+ "女",
+ "黄",
+ "推",
+ "显",
+ "谈",
+ "罪",
+ "神",
+ "艺",
+ "呢",
+ "席",
+ "含",
+ "企",
+ "望",
+ "密",
+ "批",
+ "营",
+ "项",
+ "防",
+ "举",
+ "球",
+ "英",
+ "氧",
+ "势",
+ "告",
+ "李",
+ "台",
+ "落",
+ "木",
+ "帮",
+ "轮",
+ "破",
+ "亚",
+ "师",
+ "围",
+ "注",
+ "远",
+ "字",
+ "材",
+ "排",
+ "供",
+ "河",
+ "态",
+ "封",
+ "另",
+ "施",
+ "减",
+ "树",
+ "溶",
+ "怎",
+ "止",
+ "案",
+ "言",
+ "士",
+ "均",
+ "武",
+ "固",
+ "叶",
+ "鱼",
+ "波",
+ "视",
+ "仅",
+ "费",
+ "紧",
+ "爱",
+ "左",
+ "章",
+ "早",
+ "朝",
+ "害",
+ "续",
+ "轻",
+ "服",
+ "试",
+ "食",
+ "充",
+ "兵",
+ "源",
+ "判",
+ "护",
+ "司",
+ "足",
+ "某",
+ "练",
+ "差",
+ "致",
+ "板",
+ "田",
+ "降",
+ "黑",
+ "犯",
+ "负",
+ "击",
+ "范",
+ "继",
+ "兴",
+ "似",
+ "余",
+ "坚",
+ "曲",
+ "输",
+ "修",
+ "故",
+ "城",
+ "夫",
+ "够",
+ "送",
+ "笔",
+ "船",
+ "占",
+ "右",
+ "财",
+ "吃",
+ "富",
+ "春",
+ "职",
+ "觉",
+ "汉",
+ "画",
+ "功",
+ "巴",
+ "跟",
+ "虽",
+ "杂",
+ "飞",
+ "检",
+ "吸",
+ "助",
+ "升",
+ "阳",
+ "互",
+ "初",
+ "创",
+ "抗",
+ "考",
+ "投",
+ "坏",
+ "策",
+ "古",
+ "径",
+ "换",
+ "未",
+ "跑",
+ "留",
+ "钢",
+ "曾",
+ "端",
+ "责",
+ "站",
+ "简",
+ "述",
+ "钱",
+ "副",
+ "尽",
+ "帝",
+ "射",
+ "草",
+ "冲",
+ "承",
+ "独",
+ "令",
+ "限",
+ "阿",
+ "宣",
+ "环",
+ "双",
+ "请",
+ "超",
+ "微",
+ "让",
+ "控",
+ "州",
+ "良",
+ "轴",
+ "找",
+ "否",
+ "纪",
+ "益",
+ "依",
+ "优",
+ "顶",
+ "础",
+ "载",
+ "倒",
+ "房",
+ "突",
+ "坐",
+ "粉",
+ "敌",
+ "略",
+ "客",
+ "袁",
+ "冷",
+ "胜",
+ "绝",
+ "析",
+ "块",
+ "剂",
+ "测",
+ "丝",
+ "协",
+ "诉",
+ "念",
+ "陈",
+ "仍",
+ "罗",
+ "盐",
+ "友",
+ "洋",
+ "错",
+ "苦",
+ "夜",
+ "刑",
+ "移",
+ "频",
+ "逐",
+ "靠",
+ "混",
+ "母",
+ "短",
+ "皮",
+ "终",
+ "聚",
+ "汽",
+ "村",
+ "云",
+ "哪",
+ "既",
+ "距",
+ "卫",
+ "停",
+ "烈",
+ "央",
+ "察",
+ "烧",
+ "迅",
+ "境",
+ "若",
+ "印",
+ "洲",
+ "刻",
+ "括",
+ "激",
+ "孔",
+ "搞",
+ "甚",
+ "室",
+ "待",
+ "核",
+ "校",
+ "散",
+ "侵",
+ "吧",
+ "甲",
+ "游",
+ "久",
+ "菜",
+ "味",
+ "旧",
+ "模",
+ "湖",
+ "货",
+ "损",
+ "预",
+ "阻",
+ "毫",
+ "普",
+ "稳",
+ "乙",
+ "妈",
+ "植",
+ "息",
+ "扩",
+ "银",
+ "语",
+ "挥",
+ "酒",
+ "守",
+ "拿",
+ "序",
+ "纸",
+ "医",
+ "缺",
+ "雨",
+ "吗",
+ "针",
+ "刘",
+ "啊",
+ "急",
+ "唱",
+ "误",
+ "训",
+ "愿",
+ "审",
+ "附",
+ "获",
+ "茶",
+ "鲜",
+ "粮",
+ "斤",
+ "孩",
+ "脱",
+ "硫",
+ "肥",
+ "善",
+ "龙",
+ "演",
+ "父",
+ "渐",
+ "血",
+ "欢",
+ "械",
+ "掌",
+ "歌",
+ "沙",
+ "刚",
+ "攻",
+ "谓",
+ "盾",
+ "讨",
+ "晚",
+ "粒",
+ "乱",
+ "燃",
+ "矛",
+ "乎",
+ "杀",
+ "药",
+ "宁",
+ "鲁",
+ "贵",
+ "钟",
+ "煤",
+ "读",
+ "班",
+ "伯",
+ "香",
+ "介",
+ "迫",
+ "句",
+ "丰",
+ "培",
+ "握",
+ "兰",
+ "担",
+ "弦",
+ "蛋",
+ "沉",
+ "假",
+ "穿",
+ "执",
+ "答",
+ "乐",
+ "谁",
+ "顺",
+ "烟",
+ "缩",
+ "征",
+ "脸",
+ "喜",
+ "松",
+ "脚",
+ "困",
+ "异",
+ "免",
+ "背",
+ "星",
+ "福",
+ "买",
+ "染",
+ "井",
+ "概",
+ "慢",
+ "怕",
+ "磁",
+ "倍",
+ "祖",
+ "皇",
+ "促",
+ "静",
+ "补",
+ "评",
+ "翻",
+ "肉",
+ "践",
+ "尼",
+ "衣",
+ "宽",
+ "扬",
+ "棉",
+ "希",
+ "伤",
+ "操",
+ "垂",
+ "秋",
+ "宜",
+ "氢",
+ "套",
+ "督",
+ "振",
+ "架",
+ "亮",
+ "末",
+ "宪",
+ "庆",
+ "编",
+ "牛",
+ "触",
+ "映",
+ "雷",
+ "销",
+ "诗",
+ "座",
+ "居",
+ "抓",
+ "裂",
+ "胞",
+ "呼",
+ "娘",
+ "景",
+ "威",
+ "绿",
+ "晶",
+ "厚",
+ "盟",
+ "衡",
+ "鸡",
+ "孙",
+ "延",
+ "危",
+ "胶",
+ "屋",
+ "乡",
+ "临",
+ "陆",
+ "顾",
+ "掉",
+ "呀",
+ "灯",
+ "岁",
+ "措",
+ "束",
+ "耐",
+ "剧",
+ "玉",
+ "赵",
+ "跳",
+ "哥",
+ "季",
+ "课",
+ "凯",
+ "胡",
+ "额",
+ "款",
+ "绍",
+ "卷",
+ "齐",
+ "伟",
+ "蒸",
+ "殖",
+ "永",
+ "宗",
+ "苗",
+ "川",
+ "炉",
+ "岩",
+ "弱",
+ "零",
+ "杨",
+ "奏",
+ "沿",
+ "露",
+ "杆",
+ "探",
+ "滑",
+ "镇",
+ "饭",
+ "浓",
+ "航",
+ "怀",
+ "赶",
+ "库",
+ "夺",
+ "伊",
+ "灵",
+ "税",
+ "途",
+ "灭",
+ "赛",
+ "归",
+ "召",
+ "鼓",
+ "播",
+ "盘",
+ "裁",
+ "险",
+ "康",
+ "唯",
+ "录",
+ "菌",
+ "纯",
+ "借",
+ "糖",
+ "盖",
+ "横",
+ "符",
+ "私",
+ "努",
+ "堂",
+ "域",
+ "枪",
+ "润",
+ "幅",
+ "哈",
+ "竟",
+ "熟",
+ "虫",
+ "泽",
+ "脑",
+ "壤",
+ "碳",
+ "欧",
+ "遍",
+ "侧",
+ "寨",
+ "敢",
+ "彻",
+ "虑",
+ "斜",
+ "薄",
+ "庭",
+ "纳",
+ "弹",
+ "饲",
+ "伸",
+ "折",
+ "麦",
+ "湿",
+ "暗",
+ "荷",
+ "瓦",
+ "塞",
+ "床",
+ "筑",
+ "恶",
+ "户",
+ "访",
+ "塔",
+ "奇",
+ "透",
+ "梁",
+ "刀",
+ "旋",
+ "迹",
+ "卡",
+ "氯",
+ "遇",
+ "份",
+ "毒",
+ "泥",
+ "退",
+ "洗",
+ "摆",
+ "灰",
+ "彩",
+ "卖",
+ "耗",
+ "夏",
+ "择",
+ "忙",
+ "铜",
+ "献",
+ "硬",
+ "予",
+ "繁",
+ "圈",
+ "雪",
+ "函",
+ "亦",
+ "抽",
+ "篇",
+ "阵",
+ "阴",
+ "丁",
+ "尺",
+ "追",
+ "堆",
+ "雄",
+ "迎",
+ "泛",
+ "爸",
+ "楼",
+ "避",
+ "谋",
+ "吨",
+ "野",
+ "猪",
+ "旗",
+ "累",
+ "偏",
+ "典",
+ "馆",
+ "索",
+ "秦",
+ "脂",
+ "潮",
+ "爷",
+ "豆",
+ "忽",
+ "托",
+ "惊",
+ "塑",
+ "遗",
+ "愈",
+ "朱",
+ "替",
+ "纤",
+ "粗",
+ "倾",
+ "尚",
+ "痛",
+ "楚",
+ "谢",
+ "奋",
+ "购",
+ "磨",
+ "君",
+ "池",
+ "旁",
+ "碎",
+ "骨",
+ "监",
+ "捕",
+ "弟",
+ "暴",
+ "割",
+ "贯",
+ "殊",
+ "释",
+ "词",
+ "亡",
+ "壁",
+ "顿",
+ "宝",
+ "午",
+ "尘",
+ "闻",
+ "揭",
+ "炮",
+ "残",
+ "冬",
+ "桥",
+ "妇",
+ "警",
+ "综",
+ "招",
+ "吴",
+ "付",
+ "浮",
+ "遭",
+ "徐",
+ "您",
+ "摇",
+ "谷",
+ "赞",
+ "箱",
+ "隔",
+ "订",
+ "男",
+ "吹",
+ "园",
+ "纷",
+ "唐",
+ "败",
+ "宋",
+ "玻",
+ "巨",
+ "耕",
+ "坦",
+ "荣",
+ "闭",
+ "湾",
+ "键",
+ "凡",
+ "驻",
+ "锅",
+ "救",
+ "恩",
+ "剥",
+ "凝",
+ "碱",
+ "齿",
+ "截",
+ "炼",
+ "麻",
+ "纺",
+ "禁",
+ "废",
+ "盛",
+ "版",
+ "缓",
+ "净",
+ "睛",
+ "昌",
+ "婚",
+ "涉",
+ "筒",
+ "嘴",
+ "插",
+ "岸",
+ "朗",
+ "庄",
+ "街",
+ "藏",
+ "姑",
+ "贸",
+ "腐",
+ "奴",
+ "啦",
+ "惯",
+ "乘",
+ "伙",
+ "恢",
+ "匀",
+ "纱",
+ "扎",
+ "辩",
+ "耳",
+ "彪",
+ "臣",
+ "亿",
+ "璃",
+ "抵",
+ "脉",
+ "秀",
+ "萨",
+ "俄",
+ "网",
+ "舞",
+ "店",
+ "喷",
+ "纵",
+ "寸",
+ "汗",
+ "挂",
+ "洪",
+ "贺",
+ "闪",
+ "柬",
+ "爆",
+ "烯",
+ "津",
+ "稻",
+ "墙",
+ "软",
+ "勇",
+ "像",
+ "滚",
+ "厘",
+ "蒙",
+ "芳",
+ "肯",
+ "坡",
+ "柱",
+ "荡",
+ "腿",
+ "仪",
+ "旅",
+ "尾",
+ "轧",
+ "冰",
+ "贡",
+ "登",
+ "黎",
+ "削",
+ "钻",
+ "勒",
+ "逃",
+ "障",
+ "氨",
+ "郭",
+ "峰",
+ "币",
+ "港",
+ "伏",
+ "轨",
+ "亩",
+ "毕",
+ "擦",
+ "莫",
+ "刺",
+ "浪",
+ "秘",
+ "援",
+ "株",
+ "健",
+ "售",
+ "股",
+ "岛",
+ "甘",
+ "泡",
+ "睡",
+ "童",
+ "铸",
+ "汤",
+ "阀",
+ "休",
+ "汇",
+ "舍",
+ "牧",
+ "绕",
+ "炸",
+ "哲",
+ "磷",
+ "绩",
+ "朋",
+ "淡",
+ "尖",
+ "启",
+ "陷",
+ "柴",
+ "呈",
+ "徒",
+ "颜",
+ "泪",
+ "稍",
+ "忘",
+ "泵",
+ "蓝",
+ "拖",
+ "洞",
+ "授",
+ "镜",
+ "辛",
+ "壮",
+ "锋",
+ "贫",
+ "虚",
+ "弯",
+ "摩",
+ "泰",
+ "幼",
+ "廷",
+ "尊",
+ "窗",
+ "纲",
+ "弄",
+ "隶",
+ "疑",
+ "氏",
+ "宫",
+ "姐",
+ "震",
+ "瑞",
+ "怪",
+ "尤",
+ "琴",
+ "循",
+ "描",
+ "膜",
+ "违",
+ "夹",
+ "腰",
+ "缘",
+ "珠",
+ "穷",
+ "森",
+ "枝",
+ "竹",
+ "沟",
+ "催",
+ "绳",
+ "忆",
+ "邦",
+ "剩",
+ "幸",
+ "浆",
+ "栏",
+ "拥",
+ "牙",
+ "贮",
+ "礼",
+ "滤",
+ "钠",
+ "纹",
+ "罢",
+ "拍",
+ "咱",
+ "喊",
+ "袖",
+ "埃",
+ "勤",
+ "罚",
+ "焦",
+ "潜",
+ "伍",
+ "墨",
+ "欲",
+ "缝",
+ "姓",
+ "刊",
+ "饱",
+ "仿",
+ "奖",
+ "铝",
+ "鬼",
+ "丽",
+ "跨",
+ "默",
+ "挖",
+ "链",
+ "扫",
+ "喝",
+ "袋",
+ "炭",
+ "污",
+ "幕",
+ "诸",
+ "弧",
+ "励",
+ "梅",
+ "奶",
+ "洁",
+ "灾",
+ "舟",
+ "鉴",
+ "苯",
+ "讼",
+ "抱",
+ "毁",
+ "懂",
+ "寒",
+ "智",
+ "埔",
+ "寄",
+ "届",
+ "跃",
+ "渡",
+ "挑",
+ "丹",
+ "艰",
+ "贝",
+ "碰",
+ "拔",
+ "爹",
+ "戴",
+ "码",
+ "梦",
+ "芽",
+ "熔",
+ "赤",
+ "渔",
+ "哭",
+ "敬",
+ "颗",
+ "奔",
+ "铅",
+ "仲",
+ "虎",
+ "稀",
+ "妹",
+ "乏",
+ "珍",
+ "申",
+ "桌",
+ "遵",
+ "允",
+ "隆",
+ "螺",
+ "仓",
+ "魏",
+ "锐",
+ "晓",
+ "氮",
+ "兼",
+ "隐",
+ "碍",
+ "赫",
+ "拨",
+ "忠",
+ "肃",
+ "缸",
+ "牵",
+ "抢",
+ "博",
+ "巧",
+ "壳",
+ "兄",
+ "杜",
+ "讯",
+ "诚",
+ "碧",
+ "祥",
+ "柯",
+ "页",
+ "巡",
+ "矩",
+ "悲",
+ "灌",
+ "龄",
+ "伦",
+ "票",
+ "寻",
+ "桂",
+ "铺",
+ "圣",
+ "恐",
+ "恰",
+ "郑",
+ "趣",
+ "抬",
+ "荒",
+ "腾",
+ "贴",
+ "柔",
+ "滴",
+ "猛",
+ "阔",
+ "辆",
+ "妻",
+ "填",
+ "撤",
+ "储",
+ "签",
+ "闹",
+ "扰",
+ "紫",
+ "砂",
+ "递",
+ "戏",
+ "吊",
+ "陶",
+ "伐",
+ "喂",
+ "疗",
+ "瓶",
+ "婆",
+ "抚",
+ "臂",
+ "摸",
+ "忍",
+ "虾",
+ "蜡",
+ "邻",
+ "胸",
+ "巩",
+ "挤",
+ "偶",
+ "弃",
+ "槽",
+ "劲",
+ "乳",
+ "邓",
+ "吉",
+ "仁",
+ "烂",
+ "砖",
+ "租",
+ "乌",
+ "舰",
+ "伴",
+ "瓜",
+ "浅",
+ "丙",
+ "暂",
+ "燥",
+ "橡",
+ "柳",
+ "迷",
+ "暖",
+ "牌",
+ "秧",
+ "胆",
+ "详",
+ "簧",
+ "踏",
+ "瓷",
+ "谱",
+ "呆",
+ "宾",
+ "糊",
+ "洛",
+ "辉",
+ "愤",
+ "竞",
+ "隙",
+ "怒",
+ "粘",
+ "乃",
+ "绪",
+ "肩",
+ "籍",
+ "敏",
+ "涂",
+ "熙",
+ "皆",
+ "侦",
+ "悬",
+ "掘",
+ "享",
+ "纠",
+ "醒",
+ "狂",
+ "锁",
+ "淀",
+ "恨",
+ "牲",
+ "霸",
+ "爬",
+ "赏",
+ "逆",
+ "玩",
+ "陵",
+ "祝",
+ "秒",
+ "浙",
+ "貌"
+ }), 1)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h
new file mode 100644
index 000000000..0d7a637e6
--- /dev/null
+++ b/src/mnemonics/dutch.h
@@ -0,0 +1,1686 @@
+// 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.
+
+/*!
+ * \file dutch.h
+ *
+ * \brief New Dutch word list and map.
+ */
+
+#ifndef DUTCH_H
+#define DUTCH_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class Dutch: public Base
+ {
+ public:
+ Dutch(): Base("Dutch", std::vector<std::string>({
+ "aalglad",
+ "aalscholver",
+ "aambeeld",
+ "aangeef",
+ "aanlandig",
+ "aanvaard",
+ "aanwakker",
+ "aapmens",
+ "aarten",
+ "abdicatie",
+ "abnormaal",
+ "abrikoos",
+ "accu",
+ "acuut",
+ "adjudant",
+ "admiraal",
+ "advies",
+ "afbidding",
+ "afdracht",
+ "affaire",
+ "affiche",
+ "afgang",
+ "afkick",
+ "afknap",
+ "aflees",
+ "afmijner",
+ "afname",
+ "afpreekt",
+ "afrader",
+ "afspeel",
+ "aftocht",
+ "aftrek",
+ "afzijdig",
+ "ahornboom",
+ "aktetas",
+ "akzo",
+ "alchemist",
+ "alcohol",
+ "aldaar",
+ "alexander",
+ "alfabet",
+ "alfredo",
+ "alice",
+ "alikruik",
+ "allrisk",
+ "altsax",
+ "alufolie",
+ "alziend",
+ "amai",
+ "ambacht",
+ "ambieer",
+ "amina",
+ "amnestie",
+ "amok",
+ "ampul",
+ "amuzikaal",
+ "angela",
+ "aniek",
+ "antje",
+ "antwerpen",
+ "anya",
+ "aorta",
+ "apache",
+ "apekool",
+ "appelaar",
+ "arganolie",
+ "argeloos",
+ "armoede",
+ "arrenslee",
+ "artritis",
+ "arubaan",
+ "asbak",
+ "ascii",
+ "asgrauw",
+ "asjes",
+ "asml",
+ "aspunt",
+ "asurn",
+ "asveld",
+ "aterling",
+ "atomair",
+ "atrium",
+ "atsma",
+ "atypisch",
+ "auping",
+ "aura",
+ "avifauna",
+ "axiaal",
+ "azoriaan",
+ "azteek",
+ "azuur",
+ "bachelor",
+ "badderen",
+ "badhotel",
+ "badmantel",
+ "badsteden",
+ "balie",
+ "ballans",
+ "balvers",
+ "bamibal",
+ "banneling",
+ "barracuda",
+ "basaal",
+ "batelaan",
+ "batje",
+ "beambte",
+ "bedlamp",
+ "bedwelmd",
+ "befaamd",
+ "begierd",
+ "begraaf",
+ "behield",
+ "beijaard",
+ "bejaagd",
+ "bekaaid",
+ "beks",
+ "bektas",
+ "belaad",
+ "belboei",
+ "belderbos",
+ "beloerd",
+ "beluchten",
+ "bemiddeld",
+ "benadeeld",
+ "benijd",
+ "berechten",
+ "beroemd",
+ "besef",
+ "besseling",
+ "best",
+ "betichten",
+ "bevind",
+ "bevochten",
+ "bevraagd",
+ "bewust",
+ "bidplaats",
+ "biefstuk",
+ "biemans",
+ "biezen",
+ "bijbaan",
+ "bijeenkom",
+ "bijfiguur",
+ "bijkaart",
+ "bijlage",
+ "bijpaard",
+ "bijtgaar",
+ "bijweg",
+ "bimmel",
+ "binck",
+ "bint",
+ "biobak",
+ "biotisch",
+ "biseks",
+ "bistro",
+ "bitter",
+ "bitumen",
+ "bizar",
+ "blad",
+ "bleken",
+ "blender",
+ "bleu",
+ "blief",
+ "blijven",
+ "blozen",
+ "bock",
+ "boef",
+ "boei",
+ "boks",
+ "bolder",
+ "bolus",
+ "bolvormig",
+ "bomaanval",
+ "bombarde",
+ "bomma",
+ "bomtapijt",
+ "bookmaker",
+ "boos",
+ "borg",
+ "bosbes",
+ "boshuizen",
+ "bosloop",
+ "botanicus",
+ "bougie",
+ "bovag",
+ "boxspring",
+ "braad",
+ "brasem",
+ "brevet",
+ "brigade",
+ "brinckman",
+ "bruid",
+ "budget",
+ "buffel",
+ "buks",
+ "bulgaar",
+ "buma",
+ "butaan",
+ "butler",
+ "buuf",
+ "cactus",
+ "cafeetje",
+ "camcorder",
+ "cannabis",
+ "canyon",
+ "capoeira",
+ "capsule",
+ "carkit",
+ "casanova",
+ "catalaan",
+ "ceintuur",
+ "celdeling",
+ "celplasma",
+ "cement",
+ "censeren",
+ "ceramisch",
+ "cerberus",
+ "cerebraal",
+ "cesium",
+ "cirkel",
+ "citeer",
+ "civiel",
+ "claxon",
+ "clenbuterol",
+ "clicheren",
+ "clijsen",
+ "coalitie",
+ "coassistentschap",
+ "coaxiaal",
+ "codetaal",
+ "cofinanciering",
+ "cognac",
+ "coltrui",
+ "comfort",
+ "commandant",
+ "condensaat",
+ "confectie",
+ "conifeer",
+ "convector",
+ "copier",
+ "corfu",
+ "correct",
+ "coup",
+ "couvert",
+ "creatie",
+ "credit",
+ "crematie",
+ "cricket",
+ "croupier",
+ "cruciaal",
+ "cruijff",
+ "cuisine",
+ "culemborg",
+ "culinair",
+ "curve",
+ "cyrano",
+ "dactylus",
+ "dading",
+ "dagblind",
+ "dagje",
+ "daglicht",
+ "dagprijs",
+ "dagranden",
+ "dakdekker",
+ "dakpark",
+ "dakterras",
+ "dalgrond",
+ "dambord",
+ "damkat",
+ "damlengte",
+ "damman",
+ "danenberg",
+ "debbie",
+ "decibel",
+ "defect",
+ "deformeer",
+ "degelijk",
+ "degradant",
+ "dejonghe",
+ "dekken",
+ "deppen",
+ "derek",
+ "derf",
+ "derhalve",
+ "detineren",
+ "devalueer",
+ "diaken",
+ "dicht",
+ "dictaat",
+ "dief",
+ "digitaal",
+ "dijbreuk",
+ "dijkmans",
+ "dimbaar",
+ "dinsdag",
+ "diode",
+ "dirigeer",
+ "disbalans",
+ "dobermann",
+ "doenbaar",
+ "doerak",
+ "dogma",
+ "dokhaven",
+ "dokwerker",
+ "doling",
+ "dolphijn",
+ "dolven",
+ "dombo",
+ "dooraderd",
+ "dopeling",
+ "doping",
+ "draderig",
+ "drama",
+ "drenkbak",
+ "dreumes",
+ "drol",
+ "drug",
+ "duaal",
+ "dublin",
+ "duplicaat",
+ "durven",
+ "dusdanig",
+ "dutchbat",
+ "dutje",
+ "dutten",
+ "duur",
+ "duwwerk",
+ "dwaal",
+ "dweil",
+ "dwing",
+ "dyslexie",
+ "ecostroom",
+ "ecotaks",
+ "educatie",
+ "eeckhout",
+ "eede",
+ "eemland",
+ "eencellig",
+ "eeneiig",
+ "eenruiter",
+ "eenwinter",
+ "eerenberg",
+ "eerrover",
+ "eersel",
+ "eetmaal",
+ "efteling",
+ "egaal",
+ "egtberts",
+ "eickhoff",
+ "eidooier",
+ "eiland",
+ "eind",
+ "eisden",
+ "ekster",
+ "elburg",
+ "elevatie",
+ "elfkoppig",
+ "elfrink",
+ "elftal",
+ "elimineer",
+ "elleboog",
+ "elma",
+ "elodie",
+ "elsa",
+ "embleem",
+ "embolie",
+ "emoe",
+ "emonds",
+ "emplooi",
+ "enduro",
+ "enfin",
+ "engageer",
+ "entourage",
+ "entstof",
+ "epileer",
+ "episch",
+ "eppo",
+ "erasmus",
+ "erboven",
+ "erebaan",
+ "erelijst",
+ "ereronden",
+ "ereteken",
+ "erfhuis",
+ "erfwet",
+ "erger",
+ "erica",
+ "ermitage",
+ "erna",
+ "ernie",
+ "erts",
+ "ertussen",
+ "eruitzien",
+ "ervaar",
+ "erven",
+ "erwt",
+ "esbeek",
+ "escort",
+ "esdoorn",
+ "essing",
+ "etage",
+ "eter",
+ "ethanol",
+ "ethicus",
+ "etholoog",
+ "eufonisch",
+ "eurocent",
+ "evacuatie",
+ "exact",
+ "examen",
+ "executant",
+ "exen",
+ "exit",
+ "exogeen",
+ "exotherm",
+ "expeditie",
+ "expletief",
+ "expres",
+ "extase",
+ "extinctie",
+ "faal",
+ "faam",
+ "fabel",
+ "facultair",
+ "fakir",
+ "fakkel",
+ "faliekant",
+ "fallisch",
+ "famke",
+ "fanclub",
+ "fase",
+ "fatsoen",
+ "fauna",
+ "federaal",
+ "feedback",
+ "feest",
+ "feilbaar",
+ "feitelijk",
+ "felblauw",
+ "figurante",
+ "fiod",
+ "fitheid",
+ "fixeer",
+ "flap",
+ "fleece",
+ "fleur",
+ "flexibel",
+ "flits",
+ "flos",
+ "flow",
+ "fluweel",
+ "foezelen",
+ "fokkelman",
+ "fokpaard",
+ "fokvee",
+ "folder",
+ "follikel",
+ "folmer",
+ "folteraar",
+ "fooi",
+ "foolen",
+ "forfait",
+ "forint",
+ "formule",
+ "fornuis",
+ "fosfaat",
+ "foxtrot",
+ "foyer",
+ "fragiel",
+ "frater",
+ "freak",
+ "freddie",
+ "fregat",
+ "freon",
+ "frijnen",
+ "fructose",
+ "frunniken",
+ "fuiven",
+ "funshop",
+ "furieus",
+ "fysica",
+ "gadget",
+ "galder",
+ "galei",
+ "galg",
+ "galvlieg",
+ "galzuur",
+ "ganesh",
+ "gaswet",
+ "gaza",
+ "gazelle",
+ "geaaid",
+ "gebiecht",
+ "gebufferd",
+ "gedijd",
+ "geef",
+ "geflanst",
+ "gefreesd",
+ "gegaan",
+ "gegijzeld",
+ "gegniffel",
+ "gegraaid",
+ "gehikt",
+ "gehobbeld",
+ "gehucht",
+ "geiser",
+ "geiten",
+ "gekaakt",
+ "gekheid",
+ "gekijf",
+ "gekmakend",
+ "gekocht",
+ "gekskap",
+ "gekte",
+ "gelubberd",
+ "gemiddeld",
+ "geordend",
+ "gepoederd",
+ "gepuft",
+ "gerda",
+ "gerijpt",
+ "geseald",
+ "geshockt",
+ "gesierd",
+ "geslaagd",
+ "gesnaaid",
+ "getracht",
+ "getwijfel",
+ "geuit",
+ "gevecht",
+ "gevlagd",
+ "gewicht",
+ "gezaagd",
+ "gezocht",
+ "ghanees",
+ "giebelen",
+ "giechel",
+ "giepmans",
+ "gips",
+ "giraal",
+ "gistachtig",
+ "gitaar",
+ "glaasje",
+ "gletsjer",
+ "gleuf",
+ "glibberen",
+ "glijbaan",
+ "gloren",
+ "gluipen",
+ "gluren",
+ "gluur",
+ "gnoe",
+ "goddelijk",
+ "godgans",
+ "godschalk",
+ "godzalig",
+ "goeierd",
+ "gogme",
+ "goklustig",
+ "gokwereld",
+ "gonggrijp",
+ "gonje",
+ "goor",
+ "grabbel",
+ "graf",
+ "graveer",
+ "grif",
+ "grolleman",
+ "grom",
+ "groosman",
+ "grubben",
+ "gruijs",
+ "grut",
+ "guacamole",
+ "guido",
+ "guppy",
+ "haazen",
+ "hachelijk",
+ "haex",
+ "haiku",
+ "hakhout",
+ "hakken",
+ "hanegem",
+ "hans",
+ "hanteer",
+ "harrie",
+ "hazebroek",
+ "hedonist",
+ "heil",
+ "heineken",
+ "hekhuis",
+ "hekman",
+ "helbig",
+ "helga",
+ "helwegen",
+ "hengelaar",
+ "herkansen",
+ "hermafrodiet",
+ "hertaald",
+ "hiaat",
+ "hikspoors",
+ "hitachi",
+ "hitparade",
+ "hobo",
+ "hoeve",
+ "holocaust",
+ "hond",
+ "honnepon",
+ "hoogacht",
+ "hotelbed",
+ "hufter",
+ "hugo",
+ "huilbier",
+ "hulk",
+ "humus",
+ "huwbaar",
+ "huwelijk",
+ "hype",
+ "iconisch",
+ "idema",
+ "ideogram",
+ "idolaat",
+ "ietje",
+ "ijker",
+ "ijkheid",
+ "ijklijn",
+ "ijkmaat",
+ "ijkwezen",
+ "ijmuiden",
+ "ijsbox",
+ "ijsdag",
+ "ijselijk",
+ "ijskoud",
+ "ilse",
+ "immuun",
+ "impliceer",
+ "impuls",
+ "inbijten",
+ "inbuigen",
+ "indijken",
+ "induceer",
+ "indy",
+ "infecteer",
+ "inhaak",
+ "inkijk",
+ "inluiden",
+ "inmijnen",
+ "inoefenen",
+ "inpolder",
+ "inrijden",
+ "inslaan",
+ "invitatie",
+ "inwaaien",
+ "ionisch",
+ "isaac",
+ "isolatie",
+ "isotherm",
+ "isra",
+ "italiaan",
+ "ivoor",
+ "jacobs",
+ "jakob",
+ "jammen",
+ "jampot",
+ "jarig",
+ "jehova",
+ "jenever",
+ "jezus",
+ "joana",
+ "jobdienst",
+ "josua",
+ "joule",
+ "juich",
+ "jurk",
+ "juut",
+ "kaas",
+ "kabelaar",
+ "kabinet",
+ "kagenaar",
+ "kajuit",
+ "kalebas",
+ "kalm",
+ "kanjer",
+ "kapucijn",
+ "karregat",
+ "kart",
+ "katvanger",
+ "katwijk",
+ "kegelaar",
+ "keiachtig",
+ "keizer",
+ "kenletter",
+ "kerdijk",
+ "keus",
+ "kevlar",
+ "kezen",
+ "kickback",
+ "kieviet",
+ "kijken",
+ "kikvors",
+ "kilheid",
+ "kilobit",
+ "kilsdonk",
+ "kipschnitzel",
+ "kissebis",
+ "klad",
+ "klagelijk",
+ "klak",
+ "klapbaar",
+ "klaver",
+ "klene",
+ "klets",
+ "klijnhout",
+ "klit",
+ "klok",
+ "klonen",
+ "klotefilm",
+ "kluif",
+ "klumper",
+ "klus",
+ "knabbel",
+ "knagen",
+ "knaven",
+ "kneedbaar",
+ "knmi",
+ "knul",
+ "knus",
+ "kokhals",
+ "komiek",
+ "komkommer",
+ "kompaan",
+ "komrij",
+ "komvormig",
+ "koning",
+ "kopbal",
+ "kopklep",
+ "kopnagel",
+ "koppejan",
+ "koptekst",
+ "kopwand",
+ "koraal",
+ "kosmisch",
+ "kostbaar",
+ "kram",
+ "kraneveld",
+ "kras",
+ "kreling",
+ "krengen",
+ "kribbe",
+ "krik",
+ "kruid",
+ "krulbol",
+ "kuijper",
+ "kuipbank",
+ "kuit",
+ "kuiven",
+ "kutsmoes",
+ "kuub",
+ "kwak",
+ "kwatong",
+ "kwetsbaar",
+ "kwezelaar",
+ "kwijnen",
+ "kwik",
+ "kwinkslag",
+ "kwitantie",
+ "lading",
+ "lakbeits",
+ "lakken",
+ "laklaag",
+ "lakmoes",
+ "lakwijk",
+ "lamheid",
+ "lamp",
+ "lamsbout",
+ "lapmiddel",
+ "larve",
+ "laser",
+ "latijn",
+ "latuw",
+ "lawaai",
+ "laxeerpil",
+ "lebberen",
+ "ledeboer",
+ "leefbaar",
+ "leeman",
+ "lefdoekje",
+ "lefhebber",
+ "legboor",
+ "legsel",
+ "leguaan",
+ "leiplaat",
+ "lekdicht",
+ "lekrijden",
+ "leksteen",
+ "lenen",
+ "leraar",
+ "lesbienne",
+ "leugenaar",
+ "leut",
+ "lexicaal",
+ "lezing",
+ "lieten",
+ "liggeld",
+ "lijdzaam",
+ "lijk",
+ "lijmstang",
+ "lijnschip",
+ "likdoorn",
+ "likken",
+ "liksteen",
+ "limburg",
+ "link",
+ "linoleum",
+ "lipbloem",
+ "lipman",
+ "lispelen",
+ "lissabon",
+ "litanie",
+ "liturgie",
+ "lochem",
+ "loempia",
+ "loesje",
+ "logheid",
+ "lonen",
+ "lonneke",
+ "loom",
+ "loos",
+ "losbaar",
+ "loslaten",
+ "losplaats",
+ "loting",
+ "lotnummer",
+ "lots",
+ "louie",
+ "lourdes",
+ "louter",
+ "lowbudget",
+ "luijten",
+ "luikenaar",
+ "luilak",
+ "luipaard",
+ "luizenbos",
+ "lulkoek",
+ "lumen",
+ "lunzen",
+ "lurven",
+ "lutjeboer",
+ "luttel",
+ "lutz",
+ "luuk",
+ "luwte",
+ "luyendijk",
+ "lyceum",
+ "lynx",
+ "maakbaar",
+ "magdalena",
+ "malheid",
+ "manchet",
+ "manfred",
+ "manhaftig",
+ "mank",
+ "mantel",
+ "marion",
+ "marxist",
+ "masmeijer",
+ "massaal",
+ "matsen",
+ "matverf",
+ "matze",
+ "maude",
+ "mayonaise",
+ "mechanica",
+ "meifeest",
+ "melodie",
+ "meppelink",
+ "midvoor",
+ "midweeks",
+ "midzomer",
+ "miezel",
+ "mijnraad",
+ "minus",
+ "mirck",
+ "mirte",
+ "mispakken",
+ "misraden",
+ "miswassen",
+ "mitella",
+ "moker",
+ "molecule",
+ "mombakkes",
+ "moonen",
+ "mopperaar",
+ "moraal",
+ "morgana",
+ "mormel",
+ "mosselaar",
+ "motregen",
+ "mouw",
+ "mufheid",
+ "mutueel",
+ "muzelman",
+ "naaidoos",
+ "naald",
+ "nadeel",
+ "nadruk",
+ "nagy",
+ "nahon",
+ "naima",
+ "nairobi",
+ "napalm",
+ "napels",
+ "napijn",
+ "napoleon",
+ "narigheid",
+ "narratief",
+ "naseizoen",
+ "nasibal",
+ "navigatie",
+ "nawijn",
+ "negatief",
+ "nekletsel",
+ "nekwervel",
+ "neolatijn",
+ "neonataal",
+ "neptunus",
+ "nerd",
+ "nest",
+ "neuzelaar",
+ "nihiliste",
+ "nijenhuis",
+ "nijging",
+ "nijhoff",
+ "nijl",
+ "nijptang",
+ "nippel",
+ "nokkenas",
+ "noordam",
+ "noren",
+ "normaal",
+ "nottelman",
+ "notulant",
+ "nout",
+ "nuance",
+ "nuchter",
+ "nudorp",
+ "nulde",
+ "nullijn",
+ "nulmeting",
+ "nunspeet",
+ "nylon",
+ "obelisk",
+ "object",
+ "oblie",
+ "obsceen",
+ "occlusie",
+ "oceaan",
+ "ochtend",
+ "ockhuizen",
+ "oerdom",
+ "oergezond",
+ "oerlaag",
+ "oester",
+ "okhuijsen",
+ "olifant",
+ "olijfboer",
+ "omaans",
+ "ombudsman",
+ "omdat",
+ "omdijken",
+ "omdoen",
+ "omgebouwd",
+ "omkeer",
+ "omkomen",
+ "ommegaand",
+ "ommuren",
+ "omroep",
+ "omruil",
+ "omslaan",
+ "omsmeden",
+ "omvaar",
+ "onaardig",
+ "onedel",
+ "onenig",
+ "onheilig",
+ "onrecht",
+ "onroerend",
+ "ontcijfer",
+ "onthaal",
+ "ontvallen",
+ "ontzadeld",
+ "onzacht",
+ "onzin",
+ "onzuiver",
+ "oogappel",
+ "ooibos",
+ "ooievaar",
+ "ooit",
+ "oorarts",
+ "oorhanger",
+ "oorijzer",
+ "oorklep",
+ "oorschelp",
+ "oorworm",
+ "oorzaak",
+ "opdagen",
+ "opdien",
+ "opdweilen",
+ "opel",
+ "opgebaard",
+ "opinie",
+ "opjutten",
+ "opkijken",
+ "opklaar",
+ "opkuisen",
+ "opkwam",
+ "opnaaien",
+ "opossum",
+ "opsieren",
+ "opsmeer",
+ "optreden",
+ "opvijzel",
+ "opvlammen",
+ "opwind",
+ "oraal",
+ "orchidee",
+ "orkest",
+ "ossuarium",
+ "ostendorf",
+ "oublie",
+ "oudachtig",
+ "oudbakken",
+ "oudnoors",
+ "oudshoorn",
+ "oudtante",
+ "oven",
+ "over",
+ "oxidant",
+ "pablo",
+ "pacht",
+ "paktafel",
+ "pakzadel",
+ "paljas",
+ "panharing",
+ "papfles",
+ "paprika",
+ "parochie",
+ "paus",
+ "pauze",
+ "paviljoen",
+ "peek",
+ "pegel",
+ "peigeren",
+ "pekela",
+ "pendant",
+ "penibel",
+ "pepmiddel",
+ "peptalk",
+ "periferie",
+ "perron",
+ "pessarium",
+ "peter",
+ "petfles",
+ "petgat",
+ "peuk",
+ "pfeifer",
+ "picknick",
+ "pief",
+ "pieneman",
+ "pijlkruid",
+ "pijnacker",
+ "pijpelink",
+ "pikdonker",
+ "pikeer",
+ "pilaar",
+ "pionier",
+ "pipet",
+ "piscine",
+ "pissebed",
+ "pitchen",
+ "pixel",
+ "plamuren",
+ "plan",
+ "plausibel",
+ "plegen",
+ "plempen",
+ "pleonasme",
+ "plezant",
+ "podoloog",
+ "pofmouw",
+ "pokdalig",
+ "ponywagen",
+ "popachtig",
+ "popidool",
+ "porren",
+ "positie",
+ "potten",
+ "pralen",
+ "prezen",
+ "prijzen",
+ "privaat",
+ "proef",
+ "prooi",
+ "prozawerk",
+ "pruik",
+ "prul",
+ "publiceer",
+ "puck",
+ "puilen",
+ "pukkelig",
+ "pulveren",
+ "pupil",
+ "puppy",
+ "purmerend",
+ "pustjens",
+ "putemmer",
+ "puzzelaar",
+ "queenie",
+ "quiche",
+ "raam",
+ "raar",
+ "raat",
+ "raes",
+ "ralf",
+ "rally",
+ "ramona",
+ "ramselaar",
+ "ranonkel",
+ "rapen",
+ "rapunzel",
+ "rarekiek",
+ "rarigheid",
+ "rattenhol",
+ "ravage",
+ "reactie",
+ "recreant",
+ "redacteur",
+ "redster",
+ "reewild",
+ "regie",
+ "reijnders",
+ "rein",
+ "replica",
+ "revanche",
+ "rigide",
+ "rijbaan",
+ "rijdansen",
+ "rijgen",
+ "rijkdom",
+ "rijles",
+ "rijnwijn",
+ "rijpma",
+ "rijstafel",
+ "rijtaak",
+ "rijzwepen",
+ "rioleer",
+ "ripdeal",
+ "riphagen",
+ "riskant",
+ "rits",
+ "rivaal",
+ "robbedoes",
+ "robot",
+ "rockact",
+ "rodijk",
+ "rogier",
+ "rohypnol",
+ "rollaag",
+ "rolpaal",
+ "roltafel",
+ "roof",
+ "roon",
+ "roppen",
+ "rosbief",
+ "rosharig",
+ "rosielle",
+ "rotan",
+ "rotleven",
+ "rotten",
+ "rotvaart",
+ "royaal",
+ "royeer",
+ "rubato",
+ "ruby",
+ "ruche",
+ "rudge",
+ "ruggetje",
+ "rugnummer",
+ "rugpijn",
+ "rugtitel",
+ "rugzak",
+ "ruilbaar",
+ "ruis",
+ "ruit",
+ "rukwind",
+ "rulijs",
+ "rumoeren",
+ "rumsdorp",
+ "rumtaart",
+ "runnen",
+ "russchen",
+ "ruwkruid",
+ "saboteer",
+ "saksisch",
+ "salade",
+ "salpeter",
+ "sambabal",
+ "samsam",
+ "satelliet",
+ "satineer",
+ "saus",
+ "scampi",
+ "scarabee",
+ "scenario",
+ "schobben",
+ "schubben",
+ "scout",
+ "secessie",
+ "secondair",
+ "seculair",
+ "sediment",
+ "seeland",
+ "settelen",
+ "setwinst",
+ "sheriff",
+ "shiatsu",
+ "siciliaan",
+ "sidderaal",
+ "sigma",
+ "sijben",
+ "silvana",
+ "simkaart",
+ "sinds",
+ "situatie",
+ "sjaak",
+ "sjardijn",
+ "sjezen",
+ "sjor",
+ "skinhead",
+ "skylab",
+ "slamixen",
+ "sleijpen",
+ "slijkerig",
+ "slordig",
+ "slowaak",
+ "sluieren",
+ "smadelijk",
+ "smiecht",
+ "smoel",
+ "smos",
+ "smukken",
+ "snackcar",
+ "snavel",
+ "sneaker",
+ "sneu",
+ "snijdbaar",
+ "snit",
+ "snorder",
+ "soapbox",
+ "soetekouw",
+ "soigneren",
+ "sojaboon",
+ "solo",
+ "solvabel",
+ "somber",
+ "sommatie",
+ "soort",
+ "soppen",
+ "sopraan",
+ "soundbar",
+ "spanen",
+ "spawater",
+ "spijgat",
+ "spinaal",
+ "spionage",
+ "spiraal",
+ "spleet",
+ "splijt",
+ "spoed",
+ "sporen",
+ "spul",
+ "spuug",
+ "spuw",
+ "stalen",
+ "standaard",
+ "star",
+ "stefan",
+ "stencil",
+ "stijf",
+ "stil",
+ "stip",
+ "stopdas",
+ "stoten",
+ "stoven",
+ "straat",
+ "strobbe",
+ "strubbel",
+ "stucadoor",
+ "stuif",
+ "stukadoor",
+ "subhoofd",
+ "subregent",
+ "sudoku",
+ "sukade",
+ "sulfaat",
+ "surinaams",
+ "suus",
+ "syfilis",
+ "symboliek",
+ "sympathie",
+ "synagoge",
+ "synchroon",
+ "synergie",
+ "systeem",
+ "taanderij",
+ "tabak",
+ "tachtig",
+ "tackelen",
+ "taiwanees",
+ "talman",
+ "tamheid",
+ "tangaslip",
+ "taps",
+ "tarkan",
+ "tarwe",
+ "tasman",
+ "tatjana",
+ "taxameter",
+ "teil",
+ "teisman",
+ "telbaar",
+ "telco",
+ "telganger",
+ "telstar",
+ "tenant",
+ "tepel",
+ "terzet",
+ "testament",
+ "ticket",
+ "tiesinga",
+ "tijdelijk",
+ "tika",
+ "tiksel",
+ "tilleman",
+ "timbaal",
+ "tinsteen",
+ "tiplijn",
+ "tippelaar",
+ "tjirpen",
+ "toezeggen",
+ "tolbaas",
+ "tolgeld",
+ "tolhek",
+ "tolo",
+ "tolpoort",
+ "toltarief",
+ "tolvrij",
+ "tomaat",
+ "tondeuse",
+ "toog",
+ "tooi",
+ "toonbaar",
+ "toos",
+ "topclub",
+ "toppen",
+ "toptalent",
+ "topvrouw",
+ "toque",
+ "torment",
+ "tornado",
+ "tosti",
+ "totdat",
+ "toucheer",
+ "toulouse",
+ "tournedos",
+ "tout",
+ "trabant",
+ "tragedie",
+ "trailer",
+ "traject",
+ "traktaat",
+ "trauma",
+ "tray",
+ "trechter",
+ "tred",
+ "tref",
+ "treur",
+ "troebel",
+ "tros",
+ "trucage",
+ "truffel",
+ "tsaar",
+ "tucht",
+ "tuenter",
+ "tuitelig",
+ "tukje",
+ "tuktuk",
+ "tulp",
+ "tuma",
+ "tureluurs",
+ "twijfel",
+ "twitteren",
+ "tyfoon",
+ "typograaf",
+ "ugandees",
+ "uiachtig",
+ "uier",
+ "uisnipper",
+ "ultiem",
+ "unitair",
+ "uranium",
+ "urbaan",
+ "urendag",
+ "ursula",
+ "uurcirkel",
+ "uurglas",
+ "uzelf",
+ "vaat",
+ "vakantie",
+ "vakleraar",
+ "valbijl",
+ "valpartij",
+ "valreep",
+ "valuatie",
+ "vanmiddag",
+ "vanonder",
+ "varaan",
+ "varken",
+ "vaten",
+ "veenbes",
+ "veeteler",
+ "velgrem",
+ "vellekoop",
+ "velvet",
+ "veneberg",
+ "venlo",
+ "vent",
+ "venusberg",
+ "venw",
+ "veredeld",
+ "verf",
+ "verhaaf",
+ "vermaak",
+ "vernaaid",
+ "verraad",
+ "vers",
+ "veruit",
+ "verzaagd",
+ "vetachtig",
+ "vetlok",
+ "vetmesten",
+ "veto",
+ "vetrek",
+ "vetstaart",
+ "vetten",
+ "veurink",
+ "viaduct",
+ "vibrafoon",
+ "vicariaat",
+ "vieux",
+ "vieveen",
+ "vijfvoud",
+ "villa",
+ "vilt",
+ "vimmetje",
+ "vindbaar",
+ "vips",
+ "virtueel",
+ "visdieven",
+ "visee",
+ "visie",
+ "vlaag",
+ "vleugel",
+ "vmbo",
+ "vocht",
+ "voesenek",
+ "voicemail",
+ "voip",
+ "volg",
+ "vork",
+ "vorselaar",
+ "voyeur",
+ "vracht",
+ "vrekkig",
+ "vreten",
+ "vrije",
+ "vrozen",
+ "vrucht",
+ "vucht",
+ "vugt",
+ "vulkaan",
+ "vulmiddel",
+ "vulva",
+ "vuren",
+ "waas",
+ "wacht",
+ "wadvogel",
+ "wafel",
+ "waffel",
+ "walhalla",
+ "walnoot",
+ "walraven",
+ "wals",
+ "walvis",
+ "wandaad",
+ "wanen",
+ "wanmolen",
+ "want",
+ "warklomp",
+ "warm",
+ "wasachtig",
+ "wasteil",
+ "watt",
+ "webhandel",
+ "weblog",
+ "webpagina",
+ "webzine",
+ "wedereis",
+ "wedstrijd",
+ "weeda",
+ "weert",
+ "wegmaaien",
+ "wegscheer",
+ "wekelijks",
+ "wekken",
+ "wekroep",
+ "wektoon",
+ "weldaad",
+ "welwater",
+ "wendbaar",
+ "wenkbrauw",
+ "wens",
+ "wentelaar",
+ "wervel",
+ "wesseling",
+ "wetboek",
+ "wetmatig",
+ "whirlpool",
+ "wijbrands",
+ "wijdbeens",
+ "wijk",
+ "wijnbes",
+ "wijting",
+ "wild",
+ "wimpelen",
+ "wingebied",
+ "winplaats",
+ "winter",
+ "winzucht",
+ "wipstaart",
+ "wisgerhof",
+ "withaar",
+ "witmaker",
+ "wokkel",
+ "wolf",
+ "wonenden",
+ "woning",
+ "worden",
+ "worp",
+ "wortel",
+ "wrat",
+ "wrijf",
+ "wringen",
+ "yoghurt",
+ "ypsilon",
+ "zaaijer",
+ "zaak",
+ "zacharias",
+ "zakelijk",
+ "zakkam",
+ "zakwater",
+ "zalf",
+ "zalig",
+ "zaniken",
+ "zebracode",
+ "zeeblauw",
+ "zeef",
+ "zeegaand",
+ "zeeuw",
+ "zege",
+ "zegje",
+ "zeil",
+ "zesbaans",
+ "zesenhalf",
+ "zeskantig",
+ "zesmaal",
+ "zetbaas",
+ "zetpil",
+ "zeulen",
+ "ziezo",
+ "zigzag",
+ "zijaltaar",
+ "zijbeuk",
+ "zijlijn",
+ "zijmuur",
+ "zijn",
+ "zijwaarts",
+ "zijzelf",
+ "zilt",
+ "zimmerman",
+ "zinledig",
+ "zinnelijk",
+ "zionist",
+ "zitdag",
+ "zitruimte",
+ "zitzak",
+ "zoal",
+ "zodoende",
+ "zoekbots",
+ "zoem",
+ "zoiets",
+ "zojuist",
+ "zondaar",
+ "zotskap",
+ "zottebol",
+ "zucht",
+ "zuivel",
+ "zulk",
+ "zult",
+ "zuster",
+ "zuur",
+ "zweedijk",
+ "zwendel",
+ "zwepen",
+ "zwiep",
+ "zwijmel",
+ "zworen"
+ }), 4)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/electrum-words.cpp b/src/mnemonics/electrum-words.cpp
index f0e254ba4..501495f0b 100644
--- a/src/mnemonics/electrum-words.cpp
+++ b/src/mnemonics/electrum-words.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,7 +51,10 @@
#include <boost/crc.hpp>
#include <boost/algorithm/string/join.hpp>
+#include "chinese_simplified.h"
#include "english.h"
+#include "dutch.h"
+#include "french.h"
#include "italian.h"
#include "german.h"
#include "spanish.h"
@@ -82,7 +85,10 @@ namespace
{
// If there's a new language added, add an instance of it here.
std::vector<Language::Base*> language_instances({
+ Language::Singleton<Language::Chinese_Simplified>::instance(),
Language::Singleton<Language::English>::instance(),
+ Language::Singleton<Language::Dutch>::instance(),
+ Language::Singleton<Language::French>::instance(),
Language::Singleton<Language::Spanish>::instance(),
Language::Singleton<Language::German>::instance(),
Language::Singleton<Language::Italian>::instance(),
@@ -236,7 +242,7 @@ namespace crypto
std::vector<std::string> seed;
boost::algorithm::trim(words);
- boost::split(seed, words, boost::is_any_of(" "));
+ boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
// error on non-compliant word list
if (seed.size() != seed_length/2 && seed.size() != seed_length &&
@@ -312,6 +318,14 @@ namespace crypto
{
language = Language::Singleton<Language::English>::instance();
}
+ else if (language_name == "Dutch")
+ {
+ language = Language::Singleton<Language::Dutch>::instance();
+ }
+ else if (language_name == "French")
+ {
+ language = Language::Singleton<Language::French>::instance();
+ }
else if (language_name == "Spanish")
{
language = Language::Singleton<Language::Spanish>::instance();
@@ -336,6 +350,10 @@ namespace crypto
{
language = Language::Singleton<Language::Russian>::instance();
}
+ else if (language_name == "Chinese (Simplified)")
+ {
+ language = Language::Singleton<Language::Chinese_Simplified>::instance();
+ }
else
{
return false;
@@ -381,7 +399,10 @@ namespace crypto
void get_language_list(std::vector<std::string> &languages)
{
std::vector<Language::Base*> language_instances({
+ Language::Singleton<Language::Chinese_Simplified>::instance(),
Language::Singleton<Language::English>::instance(),
+ Language::Singleton<Language::Dutch>::instance(),
+ Language::Singleton<Language::French>::instance(),
Language::Singleton<Language::Spanish>::instance(),
Language::Singleton<Language::German>::instance(),
Language::Singleton<Language::Italian>::instance(),
diff --git a/src/mnemonics/electrum-words.h b/src/mnemonics/electrum-words.h
index 29a9f62f2..d1aa65939 100644
--- a/src/mnemonics/electrum-words.h
+++ b/src/mnemonics/electrum-words.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h
index c42b752b1..e6cfa8951 100644
--- a/src/mnemonics/english.h
+++ b/src/mnemonics/english.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -49,9 +49,7 @@ namespace Language
class English: public Base
{
public:
- English()
- {
- word_list = new std::vector<std::string>({
+ English(): Base("English", std::vector<std::string>({
"abbey",
"abducts",
"ability",
@@ -1678,11 +1676,8 @@ namespace Language
"zombie",
"zones",
"zoom"
- });
- unique_prefix_length = 3;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "English";
+ }), 3)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h
new file mode 100644
index 000000000..6cf44a197
--- /dev/null
+++ b/src/mnemonics/french.h
@@ -0,0 +1,1686 @@
+// 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.
+
+/*!
+ * \file french.h
+ *
+ * \brief French word list and map.
+ */
+
+#ifndef FRENCH_H
+#define FRENCH_H
+
+#include <vector>
+#include <unordered_map>
+#include "language_base.h"
+#include <string>
+
+/*!
+ * \namespace Language
+ * \brief Mnemonic language related namespace.
+ */
+namespace Language
+{
+ class French: public Base
+ {
+ public:
+ French(): Base("French", std::vector<std::string>({
+ "abandon",
+ "abattre",
+ "aboi",
+ "abolir",
+ "aborder",
+ "abri",
+ "absence",
+ "absolu",
+ "abuser",
+ "acacia",
+ "acajou",
+ "accent",
+ "accord",
+ "accrocher",
+ "accuser",
+ "acerbe",
+ "achat",
+ "acheter",
+ "acide",
+ "acier",
+ "acquis",
+ "acte",
+ "action",
+ "adage",
+ "adepte",
+ "adieu",
+ "admettre",
+ "admis",
+ "adorer",
+ "adresser",
+ "aduler",
+ "affaire",
+ "affirmer",
+ "afin",
+ "agacer",
+ "agent",
+ "agir",
+ "agiter",
+ "agonie",
+ "agrafe",
+ "agrume",
+ "aider",
+ "aigle",
+ "aigre",
+ "aile",
+ "ailleurs",
+ "aimant",
+ "aimer",
+ "ainsi",
+ "aise",
+ "ajouter",
+ "alarme",
+ "album",
+ "alcool",
+ "alerte",
+ "algue",
+ "alibi",
+ "aller",
+ "allumer",
+ "alors",
+ "amande",
+ "amener",
+ "amie",
+ "amorcer",
+ "amour",
+ "ample",
+ "amuser",
+ "ananas",
+ "ancien",
+ "anglais",
+ "angoisse",
+ "animal",
+ "anneau",
+ "annoncer",
+ "apercevoir",
+ "apparence",
+ "appel",
+ "apporter",
+ "apprendre",
+ "appuyer",
+ "arbre",
+ "arcade",
+ "arceau",
+ "arche",
+ "ardeur",
+ "argent",
+ "argile",
+ "aride",
+ "arme",
+ "armure",
+ "arracher",
+ "arriver",
+ "article",
+ "asile",
+ "aspect",
+ "assaut",
+ "assez",
+ "assister",
+ "assurer",
+ "astre",
+ "astuce",
+ "atlas",
+ "atroce",
+ "attacher",
+ "attente",
+ "attirer",
+ "aube",
+ "aucun",
+ "audace",
+ "auparavant",
+ "auquel",
+ "aurore",
+ "aussi",
+ "autant",
+ "auteur",
+ "autoroute",
+ "autre",
+ "aval",
+ "avant",
+ "avec",
+ "avenir",
+ "averse",
+ "aveu",
+ "avide",
+ "avion",
+ "avis",
+ "avoir",
+ "avouer",
+ "avril",
+ "azote",
+ "azur",
+ "badge",
+ "bagage",
+ "bague",
+ "bain",
+ "baisser",
+ "balai",
+ "balcon",
+ "balise",
+ "balle",
+ "bambou",
+ "banane",
+ "banc",
+ "bandage",
+ "banjo",
+ "banlieue",
+ "bannir",
+ "banque",
+ "baobab",
+ "barbe",
+ "barque",
+ "barrer",
+ "bassine",
+ "bataille",
+ "bateau",
+ "battre",
+ "baver",
+ "bavoir",
+ "bazar",
+ "beau",
+ "beige",
+ "berger",
+ "besoin",
+ "beurre",
+ "biais",
+ "biceps",
+ "bidule",
+ "bien",
+ "bijou",
+ "bilan",
+ "billet",
+ "blanc",
+ "blason",
+ "bleu",
+ "bloc",
+ "blond",
+ "bocal",
+ "boire",
+ "boiserie",
+ "boiter",
+ "bonbon",
+ "bondir",
+ "bonheur",
+ "bordure",
+ "borgne",
+ "borner",
+ "bosse",
+ "bouche",
+ "bouder",
+ "bouger",
+ "boule",
+ "bourse",
+ "bout",
+ "boxe",
+ "brader",
+ "braise",
+ "branche",
+ "braquer",
+ "bras",
+ "brave",
+ "brebis",
+ "brevet",
+ "brider",
+ "briller",
+ "brin",
+ "brique",
+ "briser",
+ "broche",
+ "broder",
+ "bronze",
+ "brosser",
+ "brouter",
+ "bruit",
+ "brute",
+ "budget",
+ "buffet",
+ "bulle",
+ "bureau",
+ "buriner",
+ "buste",
+ "buter",
+ "butiner",
+ "cabas",
+ "cabinet",
+ "cabri",
+ "cacao",
+ "cacher",
+ "cadeau",
+ "cadre",
+ "cage",
+ "caisse",
+ "caler",
+ "calme",
+ "camarade",
+ "camion",
+ "campagne",
+ "canal",
+ "canif",
+ "capable",
+ "capot",
+ "carat",
+ "caresser",
+ "carie",
+ "carpe",
+ "cartel",
+ "casier",
+ "casque",
+ "casserole",
+ "cause",
+ "cavale",
+ "cave",
+ "ceci",
+ "cela",
+ "celui",
+ "cendre",
+ "cent",
+ "cependant",
+ "cercle",
+ "cerise",
+ "cerner",
+ "certes",
+ "cerveau",
+ "cesser",
+ "chacun",
+ "chair",
+ "chaleur",
+ "chamois",
+ "chanson",
+ "chaque",
+ "charge",
+ "chasse",
+ "chat",
+ "chaud",
+ "chef",
+ "chemin",
+ "cheveu",
+ "chez",
+ "chicane",
+ "chien",
+ "chiffre",
+ "chiner",
+ "chiot",
+ "chlore",
+ "choc",
+ "choix",
+ "chose",
+ "chou",
+ "chute",
+ "cibler",
+ "cidre",
+ "ciel",
+ "cigale",
+ "cinq",
+ "cintre",
+ "cirage",
+ "cirque",
+ "ciseau",
+ "citation",
+ "citer",
+ "citron",
+ "civet",
+ "clairon",
+ "clan",
+ "classe",
+ "clavier",
+ "clef",
+ "climat",
+ "cloche",
+ "cloner",
+ "clore",
+ "clos",
+ "clou",
+ "club",
+ "cobra",
+ "cocon",
+ "coiffer",
+ "coin",
+ "colline",
+ "colon",
+ "combat",
+ "comme",
+ "compte",
+ "conclure",
+ "conduire",
+ "confier",
+ "connu",
+ "conseil",
+ "contre",
+ "convenir",
+ "copier",
+ "cordial",
+ "cornet",
+ "corps",
+ "cosmos",
+ "coton",
+ "couche",
+ "coude",
+ "couler",
+ "coupure",
+ "cour",
+ "couteau",
+ "couvrir",
+ "crabe",
+ "crainte",
+ "crampe",
+ "cran",
+ "creuser",
+ "crever",
+ "crier",
+ "crime",
+ "crin",
+ "crise",
+ "crochet",
+ "croix",
+ "cruel",
+ "cuisine",
+ "cuite",
+ "culot",
+ "culte",
+ "cumul",
+ "cure",
+ "curieux",
+ "cuve",
+ "dame",
+ "danger",
+ "dans",
+ "davantage",
+ "debout",
+ "dedans",
+ "dehors",
+ "delta",
+ "demain",
+ "demeurer",
+ "demi",
+ "dense",
+ "dent",
+ "depuis",
+ "dernier",
+ "descendre",
+ "dessus",
+ "destin",
+ "dette",
+ "deuil",
+ "deux",
+ "devant",
+ "devenir",
+ "devin",
+ "devoir",
+ "dicton",
+ "dieu",
+ "difficile",
+ "digestion",
+ "digue",
+ "diluer",
+ "dimanche",
+ "dinde",
+ "diode",
+ "dire",
+ "diriger",
+ "discours",
+ "disposer",
+ "distance",
+ "divan",
+ "divers",
+ "docile",
+ "docteur",
+ "dodu",
+ "dogme",
+ "doigt",
+ "dominer",
+ "donation",
+ "donjon",
+ "donner",
+ "dopage",
+ "dorer",
+ "dormir",
+ "doseur",
+ "douane",
+ "double",
+ "douche",
+ "douleur",
+ "doute",
+ "doux",
+ "douzaine",
+ "draguer",
+ "drame",
+ "drap",
+ "dresser",
+ "droit",
+ "duel",
+ "dune",
+ "duper",
+ "durant",
+ "durcir",
+ "durer",
+ "eaux",
+ "effacer",
+ "effet",
+ "effort",
+ "effrayant",
+ "elle",
+ "embrasser",
+ "emmener",
+ "emparer",
+ "empire",
+ "employer",
+ "emporter",
+ "enclos",
+ "encore",
+ "endive",
+ "endormir",
+ "endroit",
+ "enduit",
+ "enfant",
+ "enfermer",
+ "enfin",
+ "enfler",
+ "enfoncer",
+ "enfuir",
+ "engager",
+ "engin",
+ "enjeu",
+ "enlever",
+ "ennemi",
+ "ennui",
+ "ensemble",
+ "ensuite",
+ "entamer",
+ "entendre",
+ "entier",
+ "entourer",
+ "entre",
+ "envelopper",
+ "envie",
+ "envoyer",
+ "erreur",
+ "escalier",
+ "espace",
+ "espoir",
+ "esprit",
+ "essai",
+ "essor",
+ "essuyer",
+ "estimer",
+ "exact",
+ "examiner",
+ "excuse",
+ "exemple",
+ "exiger",
+ "exil",
+ "exister",
+ "exode",
+ "expliquer",
+ "exposer",
+ "exprimer",
+ "extase",
+ "fable",
+ "facette",
+ "facile",
+ "fade",
+ "faible",
+ "faim",
+ "faire",
+ "fait",
+ "falloir",
+ "famille",
+ "faner",
+ "farce",
+ "farine",
+ "fatigue",
+ "faucon",
+ "faune",
+ "faute",
+ "faux",
+ "faveur",
+ "favori",
+ "faxer",
+ "feinter",
+ "femme",
+ "fendre",
+ "fente",
+ "ferme",
+ "festin",
+ "feuille",
+ "feutre",
+ "fiable",
+ "fibre",
+ "ficher",
+ "fier",
+ "figer",
+ "figure",
+ "filet",
+ "fille",
+ "filmer",
+ "fils",
+ "filtre",
+ "final",
+ "finesse",
+ "finir",
+ "fiole",
+ "firme",
+ "fixe",
+ "flacon",
+ "flair",
+ "flamme",
+ "flan",
+ "flaque",
+ "fleur",
+ "flocon",
+ "flore",
+ "flot",
+ "flou",
+ "fluide",
+ "fluor",
+ "flux",
+ "focus",
+ "foin",
+ "foire",
+ "foison",
+ "folie",
+ "fonction",
+ "fondre",
+ "fonte",
+ "force",
+ "forer",
+ "forger",
+ "forme",
+ "fort",
+ "fosse",
+ "fouet",
+ "fouine",
+ "foule",
+ "four",
+ "foyer",
+ "frais",
+ "franc",
+ "frapper",
+ "freiner",
+ "frimer",
+ "friser",
+ "frite",
+ "froid",
+ "froncer",
+ "fruit",
+ "fugue",
+ "fuir",
+ "fuite",
+ "fumer",
+ "fureur",
+ "furieux",
+ "fuser",
+ "fusil",
+ "futile",
+ "futur",
+ "gagner",
+ "gain",
+ "gala",
+ "galet",
+ "galop",
+ "gamme",
+ "gant",
+ "garage",
+ "garde",
+ "garer",
+ "gauche",
+ "gaufre",
+ "gaule",
+ "gaver",
+ "gazon",
+ "geler",
+ "genou",
+ "genre",
+ "gens",
+ "gercer",
+ "germer",
+ "geste",
+ "gibier",
+ "gicler",
+ "gilet",
+ "girafe",
+ "givre",
+ "glace",
+ "glisser",
+ "globe",
+ "gloire",
+ "gluant",
+ "gober",
+ "golf",
+ "gommer",
+ "gorge",
+ "gosier",
+ "goutte",
+ "grain",
+ "gramme",
+ "grand",
+ "gras",
+ "grave",
+ "gredin",
+ "griffure",
+ "griller",
+ "gris",
+ "gronder",
+ "gros",
+ "grotte",
+ "groupe",
+ "grue",
+ "guerrier",
+ "guetter",
+ "guider",
+ "guise",
+ "habiter",
+ "hache",
+ "haie",
+ "haine",
+ "halte",
+ "hamac",
+ "hanche",
+ "hangar",
+ "hanter",
+ "haras",
+ "hareng",
+ "harpe",
+ "hasard",
+ "hausse",
+ "haut",
+ "havre",
+ "herbe",
+ "heure",
+ "hibou",
+ "hier",
+ "histoire",
+ "hiver",
+ "hochet",
+ "homme",
+ "honneur",
+ "honte",
+ "horde",
+ "horizon",
+ "hormone",
+ "houle",
+ "housse",
+ "hublot",
+ "huile",
+ "huit",
+ "humain",
+ "humble",
+ "humide",
+ "humour",
+ "hurler",
+ "idole",
+ "igloo",
+ "ignorer",
+ "illusion",
+ "image",
+ "immense",
+ "immobile",
+ "imposer",
+ "impression",
+ "incapable",
+ "inconnu",
+ "index",
+ "indiquer",
+ "infime",
+ "injure",
+ "inox",
+ "inspirer",
+ "instant",
+ "intention",
+ "intime",
+ "inutile",
+ "inventer",
+ "inviter",
+ "iode",
+ "iris",
+ "issue",
+ "ivre",
+ "jade",
+ "jadis",
+ "jamais",
+ "jambe",
+ "janvier",
+ "jardin",
+ "jauge",
+ "jaunisse",
+ "jeter",
+ "jeton",
+ "jeudi",
+ "jeune",
+ "joie",
+ "joindre",
+ "joli",
+ "joueur",
+ "journal",
+ "judo",
+ "juge",
+ "juillet",
+ "juin",
+ "jument",
+ "jungle",
+ "jupe",
+ "jupon",
+ "jurer",
+ "juron",
+ "jury",
+ "jusque",
+ "juste",
+ "kayak",
+ "ketchup",
+ "kilo",
+ "kiwi",
+ "koala",
+ "label",
+ "lacet",
+ "lacune",
+ "laine",
+ "laisse",
+ "lait",
+ "lame",
+ "lancer",
+ "lande",
+ "laque",
+ "lard",
+ "largeur",
+ "larme",
+ "larve",
+ "lasso",
+ "laver",
+ "lendemain",
+ "lentement",
+ "lequel",
+ "lettre",
+ "leur",
+ "lever",
+ "levure",
+ "liane",
+ "libre",
+ "lien",
+ "lier",
+ "lieutenant",
+ "ligne",
+ "ligoter",
+ "liguer",
+ "limace",
+ "limer",
+ "limite",
+ "lingot",
+ "lion",
+ "lire",
+ "lisser",
+ "litre",
+ "livre",
+ "lobe",
+ "local",
+ "logis",
+ "loin",
+ "loisir",
+ "long",
+ "loque",
+ "lors",
+ "lotus",
+ "louer",
+ "loup",
+ "lourd",
+ "louve",
+ "loyer",
+ "lubie",
+ "lucide",
+ "lueur",
+ "luge",
+ "luire",
+ "lundi",
+ "lune",
+ "lustre",
+ "lutin",
+ "lutte",
+ "luxe",
+ "machine",
+ "madame",
+ "magie",
+ "magnifique",
+ "magot",
+ "maigre",
+ "main",
+ "mairie",
+ "maison",
+ "malade",
+ "malheur",
+ "malin",
+ "manche",
+ "manger",
+ "manier",
+ "manoir",
+ "manquer",
+ "marche",
+ "mardi",
+ "marge",
+ "mariage",
+ "marquer",
+ "mars",
+ "masque",
+ "masse",
+ "matin",
+ "mauvais",
+ "meilleur",
+ "melon",
+ "membre",
+ "menacer",
+ "mener",
+ "mensonge",
+ "mentir",
+ "menu",
+ "merci",
+ "merlu",
+ "mesure",
+ "mettre",
+ "meuble",
+ "meunier",
+ "meute",
+ "miche",
+ "micro",
+ "midi",
+ "miel",
+ "miette",
+ "mieux",
+ "milieu",
+ "mille",
+ "mimer",
+ "mince",
+ "mineur",
+ "ministre",
+ "minute",
+ "mirage",
+ "miroir",
+ "miser",
+ "mite",
+ "mixte",
+ "mobile",
+ "mode",
+ "module",
+ "moins",
+ "mois",
+ "moment",
+ "momie",
+ "monde",
+ "monsieur",
+ "monter",
+ "moquer",
+ "moral",
+ "morceau",
+ "mordre",
+ "morose",
+ "morse",
+ "mortier",
+ "morue",
+ "motif",
+ "motte",
+ "moudre",
+ "moule",
+ "mourir",
+ "mousse",
+ "mouton",
+ "mouvement",
+ "moyen",
+ "muer",
+ "muette",
+ "mugir",
+ "muguet",
+ "mulot",
+ "multiple",
+ "munir",
+ "muret",
+ "muse",
+ "musique",
+ "muter",
+ "nacre",
+ "nager",
+ "nain",
+ "naissance",
+ "narine",
+ "narrer",
+ "naseau",
+ "nasse",
+ "nation",
+ "nature",
+ "naval",
+ "navet",
+ "naviguer",
+ "navrer",
+ "neige",
+ "nerf",
+ "nerveux",
+ "neuf",
+ "neutre",
+ "neuve",
+ "neveu",
+ "niche",
+ "nier",
+ "niveau",
+ "noble",
+ "noce",
+ "nocif",
+ "noir",
+ "nomade",
+ "nombre",
+ "nommer",
+ "nord",
+ "norme",
+ "notaire",
+ "notice",
+ "notre",
+ "nouer",
+ "nougat",
+ "nourrir",
+ "nous",
+ "nouveau",
+ "novice",
+ "noyade",
+ "noyer",
+ "nuage",
+ "nuance",
+ "nuire",
+ "nuit",
+ "nulle",
+ "nuque",
+ "oasis",
+ "objet",
+ "obliger",
+ "obscur",
+ "observer",
+ "obtenir",
+ "obus",
+ "occasion",
+ "occuper",
+ "ocre",
+ "octet",
+ "odeur",
+ "odorat",
+ "offense",
+ "officier",
+ "offrir",
+ "ogive",
+ "oiseau",
+ "olive",
+ "ombre",
+ "onctueux",
+ "onduler",
+ "ongle",
+ "onze",
+ "opter",
+ "option",
+ "orageux",
+ "oral",
+ "orange",
+ "orbite",
+ "ordinaire",
+ "ordre",
+ "oreille",
+ "organe",
+ "orgie",
+ "orgueil",
+ "orient",
+ "origan",
+ "orner",
+ "orteil",
+ "ortie",
+ "oser",
+ "osselet",
+ "otage",
+ "otarie",
+ "ouate",
+ "oublier",
+ "ouest",
+ "ours",
+ "outil",
+ "outre",
+ "ouvert",
+ "ouvrir",
+ "ovale",
+ "ozone",
+ "pacte",
+ "page",
+ "paille",
+ "pain",
+ "paire",
+ "paix",
+ "palace",
+ "palissade",
+ "palmier",
+ "palpiter",
+ "panda",
+ "panneau",
+ "papa",
+ "papier",
+ "paquet",
+ "parc",
+ "pardi",
+ "parfois",
+ "parler",
+ "parmi",
+ "parole",
+ "partir",
+ "parvenir",
+ "passer",
+ "pastel",
+ "patin",
+ "patron",
+ "paume",
+ "pause",
+ "pauvre",
+ "paver",
+ "pavot",
+ "payer",
+ "pays",
+ "peau",
+ "peigne",
+ "peinture",
+ "pelage",
+ "pelote",
+ "pencher",
+ "pendre",
+ "penser",
+ "pente",
+ "percer",
+ "perdu",
+ "perle",
+ "permettre",
+ "personne",
+ "perte",
+ "peser",
+ "pesticide",
+ "petit",
+ "peuple",
+ "peur",
+ "phase",
+ "photo",
+ "phrase",
+ "piano",
+ "pied",
+ "pierre",
+ "pieu",
+ "pile",
+ "pilier",
+ "pilote",
+ "pilule",
+ "piment",
+ "pincer",
+ "pinson",
+ "pinte",
+ "pion",
+ "piquer",
+ "pirate",
+ "pire",
+ "piste",
+ "piton",
+ "pitre",
+ "pivot",
+ "pizza",
+ "placer",
+ "plage",
+ "plaire",
+ "plan",
+ "plaque",
+ "plat",
+ "plein",
+ "pleurer",
+ "pliage",
+ "plier",
+ "plonger",
+ "plot",
+ "pluie",
+ "plume",
+ "plus",
+ "pneu",
+ "poche",
+ "podium",
+ "poids",
+ "poil",
+ "point",
+ "poire",
+ "poison",
+ "poitrine",
+ "poivre",
+ "police",
+ "pollen",
+ "pomme",
+ "pompier",
+ "poncer",
+ "pondre",
+ "pont",
+ "portion",
+ "poser",
+ "position",
+ "possible",
+ "poste",
+ "potage",
+ "potin",
+ "pouce",
+ "poudre",
+ "poulet",
+ "poumon",
+ "poupe",
+ "pour",
+ "pousser",
+ "poutre",
+ "pouvoir",
+ "prairie",
+ "premier",
+ "prendre",
+ "presque",
+ "preuve",
+ "prier",
+ "primeur",
+ "prince",
+ "prison",
+ "priver",
+ "prix",
+ "prochain",
+ "produire",
+ "profond",
+ "proie",
+ "projet",
+ "promener",
+ "prononcer",
+ "propre",
+ "prose",
+ "prouver",
+ "prune",
+ "public",
+ "puce",
+ "pudeur",
+ "puiser",
+ "pull",
+ "pulpe",
+ "puma",
+ "punir",
+ "purge",
+ "putois",
+ "quand",
+ "quartier",
+ "quasi",
+ "quatre",
+ "quel",
+ "question",
+ "queue",
+ "quiche",
+ "quille",
+ "quinze",
+ "quitter",
+ "quoi",
+ "rabais",
+ "raboter",
+ "race",
+ "racheter",
+ "racine",
+ "racler",
+ "raconter",
+ "radar",
+ "radio",
+ "rafale",
+ "rage",
+ "ragot",
+ "raideur",
+ "raie",
+ "rail",
+ "raison",
+ "ramasser",
+ "ramener",
+ "rampe",
+ "rance",
+ "rang",
+ "rapace",
+ "rapide",
+ "rapport",
+ "rarement",
+ "rasage",
+ "raser",
+ "rasoir",
+ "rassurer",
+ "rater",
+ "ratio",
+ "rature",
+ "ravage",
+ "ravir",
+ "rayer",
+ "rayon",
+ "rebond",
+ "recevoir",
+ "recherche",
+ "record",
+ "reculer",
+ "redevenir",
+ "refuser",
+ "regard",
+ "regretter",
+ "rein",
+ "rejeter",
+ "rejoindre",
+ "relation",
+ "relever",
+ "religion",
+ "remarquer",
+ "remettre",
+ "remise",
+ "remonter",
+ "remplir",
+ "remuer",
+ "rencontre",
+ "rendre",
+ "renier",
+ "renoncer",
+ "rentrer",
+ "renverser",
+ "repas",
+ "repli",
+ "reposer",
+ "reproche",
+ "requin",
+ "respect",
+ "ressembler",
+ "reste",
+ "retard",
+ "retenir",
+ "retirer",
+ "retour",
+ "retrouver",
+ "revenir",
+ "revoir",
+ "revue",
+ "rhume",
+ "ricaner",
+ "riche",
+ "rideau",
+ "ridicule",
+ "rien",
+ "rigide",
+ "rincer",
+ "rire",
+ "risquer",
+ "rituel",
+ "rivage",
+ "rive",
+ "robe",
+ "robot",
+ "robuste",
+ "rocade",
+ "roche",
+ "rodeur",
+ "rogner",
+ "roman",
+ "rompre",
+ "ronce",
+ "rondeur",
+ "ronger",
+ "roque",
+ "rose",
+ "rosir",
+ "rotation",
+ "rotule",
+ "roue",
+ "rouge",
+ "rouler",
+ "route",
+ "ruban",
+ "rubis",
+ "ruche",
+ "rude",
+ "ruelle",
+ "ruer",
+ "rugby",
+ "rugir",
+ "ruine",
+ "rumeur",
+ "rural",
+ "ruse",
+ "rustre",
+ "sable",
+ "sabot",
+ "sabre",
+ "sacre",
+ "sage",
+ "saint",
+ "saisir",
+ "salade",
+ "salive",
+ "salle",
+ "salon",
+ "salto",
+ "salut",
+ "salve",
+ "samba",
+ "sandale",
+ "sanguin",
+ "sapin",
+ "sarcasme",
+ "satisfaire",
+ "sauce",
+ "sauf",
+ "sauge",
+ "saule",
+ "sauna",
+ "sauter",
+ "sauver",
+ "savoir",
+ "science",
+ "scoop",
+ "score",
+ "second",
+ "secret",
+ "secte",
+ "seigneur",
+ "sein",
+ "seize",
+ "selle",
+ "selon",
+ "semaine",
+ "sembler",
+ "semer",
+ "semis",
+ "sensuel",
+ "sentir",
+ "sept",
+ "serpe",
+ "serrer",
+ "sertir",
+ "service",
+ "seuil",
+ "seulement",
+ "short",
+ "sien",
+ "sigle",
+ "signal",
+ "silence",
+ "silo",
+ "simple",
+ "singe",
+ "sinon",
+ "sinus",
+ "sioux",
+ "sirop",
+ "site",
+ "situation",
+ "skier",
+ "snob",
+ "sobre",
+ "social",
+ "socle",
+ "sodium",
+ "soigner",
+ "soir",
+ "soixante",
+ "soja",
+ "solaire",
+ "soldat",
+ "soleil",
+ "solide",
+ "solo",
+ "solvant",
+ "sombre",
+ "somme",
+ "somnoler",
+ "sondage",
+ "songeur",
+ "sonner",
+ "sorte",
+ "sosie",
+ "sottise",
+ "souci",
+ "soudain",
+ "souffrir",
+ "souhaiter",
+ "soulever",
+ "soumettre",
+ "soupe",
+ "sourd",
+ "soustraire",
+ "soutenir",
+ "souvent",
+ "soyeux",
+ "spectacle",
+ "sport",
+ "stade",
+ "stagiaire",
+ "stand",
+ "star",
+ "statue",
+ "stock",
+ "stop",
+ "store",
+ "style",
+ "suave",
+ "subir",
+ "sucre",
+ "suer",
+ "suffire",
+ "suie",
+ "suite",
+ "suivre",
+ "sujet",
+ "sulfite",
+ "supposer",
+ "surf",
+ "surprendre",
+ "surtout",
+ "surveiller",
+ "tabac",
+ "table",
+ "tabou",
+ "tache",
+ "tacler",
+ "tacot",
+ "tact",
+ "taie",
+ "taille",
+ "taire",
+ "talon",
+ "talus",
+ "tandis",
+ "tango",
+ "tanin",
+ "tant",
+ "taper",
+ "tapis",
+ "tard",
+ "tarif",
+ "tarot",
+ "tarte",
+ "tasse",
+ "taureau",
+ "taux",
+ "taverne",
+ "taxer",
+ "taxi",
+ "tellement",
+ "temple",
+ "tendre",
+ "tenir",
+ "tenter",
+ "tenu",
+ "terme",
+ "ternir",
+ "terre",
+ "test",
+ "texte",
+ "thym",
+ "tibia",
+ "tiers",
+ "tige",
+ "tipi",
+ "tique",
+ "tirer",
+ "tissu",
+ "titre",
+ "toast",
+ "toge",
+ "toile",
+ "toiser",
+ "toiture",
+ "tomber",
+ "tome",
+ "tonne",
+ "tonte",
+ "toque",
+ "torse",
+ "tortue",
+ "totem",
+ "toucher",
+ "toujours",
+ "tour",
+ "tousser",
+ "tout",
+ "toux",
+ "trace",
+ "train",
+ "trame",
+ "tranquille",
+ "travail",
+ "trembler",
+ "trente",
+ "tribu",
+ "trier",
+ "trio",
+ "tripe",
+ "triste",
+ "troc",
+ "trois",
+ "tromper",
+ "tronc",
+ "trop",
+ "trotter",
+ "trouer",
+ "truc",
+ "truite",
+ "tuba",
+ "tuer",
+ "tuile",
+ "turbo",
+ "tutu",
+ "tuyau",
+ "type",
+ "union",
+ "unique",
+ "unir",
+ "unisson",
+ "untel",
+ "urne",
+ "usage",
+ "user",
+ "usiner",
+ "usure",
+ "utile",
+ "vache",
+ "vague",
+ "vaincre",
+ "valeur",
+ "valoir",
+ "valser",
+ "valve",
+ "vampire",
+ "vaseux",
+ "vaste",
+ "veau",
+ "veille",
+ "veine",
+ "velours",
+ "velu",
+ "vendre",
+ "venir",
+ "vent",
+ "venue",
+ "verbe",
+ "verdict",
+ "version",
+ "vertige",
+ "verve",
+ "veste",
+ "veto",
+ "vexer",
+ "vice",
+ "victime",
+ "vide",
+ "vieil",
+ "vieux",
+ "vigie",
+ "vigne",
+ "ville",
+ "vingt",
+ "violent",
+ "virer",
+ "virus",
+ "visage",
+ "viser",
+ "visite",
+ "visuel",
+ "vitamine",
+ "vitrine",
+ "vivant",
+ "vivre",
+ "vocal",
+ "vodka",
+ "vogue",
+ "voici",
+ "voile",
+ "voir",
+ "voisin",
+ "voiture",
+ "volaille",
+ "volcan",
+ "voler",
+ "volt",
+ "votant",
+ "votre",
+ "vouer",
+ "vouloir",
+ "vous",
+ "voyage",
+ "voyou",
+ "vrac",
+ "vrai",
+ "yacht",
+ "yeti",
+ "yeux",
+ "yoga",
+ "zeste",
+ "zinc",
+ "zone",
+ "zoom"
+ }), 4)
+ {
+ populate_maps();
+ }
+ };
+}
+
+#endif
diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h
index 675531453..1a1e6b467 100644
--- a/src/mnemonics/german.h
+++ b/src/mnemonics/german.h
@@ -1,6 +1,6 @@
// Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language
class German: public Base
{
public:
- German()
- {
- word_list = new std::vector<std::string>({
+ German(): Base("German", std::vector<std::string>({
"Abakus",
"Abart",
"abbilden",
@@ -1680,11 +1678,8 @@ namespace Language
"Zündung",
"Zweck",
"Zyklop"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "German";
+ }), 4)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h
index 08852a97b..28cee9d9a 100644
--- a/src/mnemonics/italian.h
+++ b/src/mnemonics/italian.h
@@ -1,6 +1,6 @@
// Word list created by Monero contributor Shrikez
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language
class Italian: public Base
{
public:
- Italian()
- {
- word_list = new std::vector<std::string>({
+ Italian(): Base("Italian", std::vector<std::string>({
"abbinare",
"abbonato",
"abisso",
@@ -1680,11 +1678,8 @@ namespace Language
"zolfo",
"zombie",
"zucchero"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Italian";
+ }), 4)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h
index 421188893..d5ab4bc84 100644
--- a/src/mnemonics/japanese.h
+++ b/src/mnemonics/japanese.h
@@ -1,23 +1,43 @@
-// Word list originally created by dabura667
-//
-// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is 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
@@ -51,9 +71,7 @@ namespace Language
class Japanese: public Base
{
public:
- Japanese()
- {
- word_list = new std::vector<std::string>({
+ Japanese(): Base("Japanese", std::vector<std::string>({
"あいこくしん",
"あいさつ",
"あいだ",
@@ -1680,11 +1698,8 @@ namespace Language
"ひさん",
"びじゅつかん",
"ひしょ"
- });
- unique_prefix_length = 3;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Japanese";
+ }), 3)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h
index e0297c847..8f0a7a9d3 100644
--- a/src/mnemonics/language_base.h
+++ b/src/mnemonics/language_base.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -38,6 +38,7 @@
#include <vector>
#include <unordered_map>
#include <string>
+#include "misc_log_ex.h"
/*!
* \namespace Language
@@ -73,44 +74,62 @@ namespace Language
class Base
{
protected:
- std::vector<std::string> *word_list; /*!< A pointer to the array of words */
- std::unordered_map<std::string, uint32_t> *word_map; /*!< hash table to find word's index */
- std::unordered_map<std::string, uint32_t> *trimmed_word_map; /*!< hash table to find word's trimmed index */
+ enum {
+ ALLOW_SHORT_WORDS = 1<<0,
+ ALLOW_DUPLICATE_PREFIXES = 1<<1,
+ };
+ const std::vector<std::string> word_list; /*!< A pointer to the array of words */
+ std::unordered_map<std::string, uint32_t> word_map; /*!< hash table to find word's index */
+ std::unordered_map<std::string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */
uint32_t unique_prefix_length; /*!< Number of unique starting characters to trim the wordlist to when matching */
/*!
* \brief Populates the word maps after the list is ready.
*/
- void populate_maps()
+ void populate_maps(uint32_t flags = 0)
{
int ii;
- std::vector<std::string>::iterator it;
- for (it = word_list->begin(), ii = 0; it != word_list->end(); it++, ii++)
+ std::vector<std::string>::const_iterator it;
+ if (word_list.size () != 1626)
+ throw std::runtime_error("Wrong word list length for " + language_name);
+ for (it = word_list.begin(), ii = 0; it != word_list.end(); it++, ii++)
{
- (*word_map)[*it] = ii;
+ word_map[*it] = ii;
+ if ((*it).size() < unique_prefix_length)
+ {
+ if (flags & ALLOW_SHORT_WORDS)
+ MWARNING(language_name << " word '" << *it << "' is shorter than its prefix length, " << unique_prefix_length);
+ else
+ throw std::runtime_error("Too short word in " + language_name + " word list: " + *it);
+ }
+ std::string trimmed;
if (it->length() > unique_prefix_length)
{
- (*trimmed_word_map)[utf8prefix(*it, unique_prefix_length)] = ii;
+ trimmed = utf8prefix(*it, unique_prefix_length);
}
else
{
- (*trimmed_word_map)[*it] = ii;
+ trimmed = *it;
+ }
+ if (trimmed_word_map.find(trimmed) != trimmed_word_map.end())
+ {
+ if (flags & ALLOW_DUPLICATE_PREFIXES)
+ MWARNING("Duplicate prefix in " << language_name << " word list: " << trimmed);
+ else
+ throw std::runtime_error("Duplicate prefix in " + language_name + " word list: " + trimmed);
}
+ trimmed_word_map[trimmed] = ii;
}
}
public:
- Base()
+ Base(const char *language_name, const std::vector<std::string> &words, uint32_t prefix_length):
+ word_list(words),
+ unique_prefix_length(prefix_length),
+ language_name(language_name)
{
- word_list = new std::vector<std::string>;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- unique_prefix_length = 4;
}
virtual ~Base()
{
- delete word_list;
- delete word_map;
- delete trimmed_word_map;
}
/*!
* \brief Returns a pointer to the word list.
@@ -118,7 +137,7 @@ namespace Language
*/
const std::vector<std::string>& get_word_list() const
{
- return *word_list;
+ return word_list;
}
/*!
* \brief Returns a pointer to the word map.
@@ -126,7 +145,7 @@ namespace Language
*/
const std::unordered_map<std::string, uint32_t>& get_word_map() const
{
- return *word_map;
+ return word_map;
}
/*!
* \brief Returns a pointer to the trimmed word map.
@@ -134,13 +153,13 @@ namespace Language
*/
const std::unordered_map<std::string, uint32_t>& get_trimmed_word_map() const
{
- return *trimmed_word_map;
+ return trimmed_word_map;
}
/*!
* \brief Returns the name of the language.
* \return Name of the language.
*/
- std::string get_language_name() const
+ const std::string &get_language_name() const
{
return language_name;
}
diff --git a/src/mnemonics/old_english.h b/src/mnemonics/old_english.h
index cd4aa7629..21ac95de3 100644
--- a/src/mnemonics/old_english.h
+++ b/src/mnemonics/old_english.h
@@ -1,6 +1,6 @@
// Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language
class OldEnglish: public Base
{
public:
- OldEnglish()
- {
- word_list = new std::vector<std::string>({
+ OldEnglish(): Base("OldEnglish", std::vector<std::string>({
"like",
"just",
"love",
@@ -1680,12 +1678,9 @@ namespace Language
"unseen",
"weapon",
"weary"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "OldEnglish";
- populate_maps();
+ }), 4)
+ {
+ populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS);
}
};
}
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h
index 782a2ad5d..f9d66afc4 100644
--- a/src/mnemonics/portuguese.h
+++ b/src/mnemonics/portuguese.h
@@ -1,21 +1,44 @@
-// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is 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
@@ -49,9 +72,7 @@ namespace Language
class Portuguese: public Base
{
public:
- Portuguese()
- {
- word_list = new std::vector<std::string>({
+ Portuguese(): Base("Portuguese", std::vector<std::string>({
"abaular",
"abdominal",
"abeto",
@@ -1678,11 +1699,8 @@ namespace Language
"zeloso",
"zenite",
"zumbi"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Portuguese";
+ }), 4)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h
index 6d98ca4b8..b3db4aa4c 100644
--- a/src/mnemonics/russian.h
+++ b/src/mnemonics/russian.h
@@ -1,6 +1,6 @@
// Word list created by Monero contributor sammy007
//
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -51,9 +51,7 @@ namespace Language
class Russian: public Base
{
public:
- Russian()
- {
- word_list = new std::vector<std::string>({
+ Russian(): Base("Russian", std::vector<std::string>({
"абажур",
"абзац",
"абонент",
@@ -1680,11 +1678,8 @@ namespace Language
"яхта",
"ячейка",
"ящик"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Russian";
+ }), 4)
+ {
populate_maps();
}
};
diff --git a/src/mnemonics/singleton.h b/src/mnemonics/singleton.h
index 48b217a0c..5ba9269b1 100644
--- a/src/mnemonics/singleton.h
+++ b/src/mnemonics/singleton.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h
index 7275f4c42..117890ada 100644
--- a/src/mnemonics/spanish.h
+++ b/src/mnemonics/spanish.h
@@ -1,23 +1,44 @@
-// Word list originally created as part of the Electrum project, Copyright (C) 2014 Thomas Voegtlin
-//
-// Copyright (c) 2014-2016, The Monero Project
-//
+// Word list originally created by dabura667 and released under The MIT License (MIT)
+//
+// The MIT License (MIT)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Code surrounding the word list is 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
@@ -51,9 +72,7 @@ namespace Language
class Spanish: public Base
{
public:
- Spanish()
- {
- word_list = new std::vector<std::string>({
+ Spanish(): Base("Spanish", std::vector<std::string>({
"ábaco",
"abdomen",
"abeja",
@@ -1680,14 +1699,11 @@ namespace Language
"risa",
"ritmo",
"rito"
- });
- unique_prefix_length = 4;
- word_map = new std::unordered_map<std::string, uint32_t>;
- trimmed_word_map = new std::unordered_map<std::string, uint32_t>;
- language_name = "Spanish";
- populate_maps();
+ }), 4)
+ {
+ populate_maps(ALLOW_SHORT_WORDS);
}
};
}
-#endif
+#endif
diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt
index b5b7d87ff..0704940b5 100644
--- a/src/p2p/CMakeLists.txt
+++ b/src/p2p/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -38,6 +38,7 @@ source_group(p2p FILES ${P2P})
monero_add_library(p2p ${P2P})
target_link_libraries(p2p
PUBLIC
+ epee
${UPNP_LIBRARIES}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
diff --git a/src/p2p/connection_basic.cpp b/src/p2p/connection_basic.cpp
index bdb239ca8..df34379e2 100644
--- a/src/p2p/connection_basic.cpp
+++ b/src/p2p/connection_basic.cpp
@@ -2,7 +2,7 @@
/// @author rfree (current maintainer in monero.cc project)
/// @brief base for connection, contains e.g. the ratelimit hooks
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/connection_basic.hpp b/src/p2p/connection_basic.hpp
index 5452fa6fb..bea2df1cd 100644
--- a/src/p2p/connection_basic.hpp
+++ b/src/p2p/connection_basic.hpp
@@ -8,7 +8,7 @@
// ! (how ever if in some wonderful juristdictions that is not the case, then why not make another sub-class withat that members and licence it as epee part)
// ! Working on above premise, IF this is valid in your juristdictions, then consider this code as released as:
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 51ab9b3f2..13cd3f5b0 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -112,7 +112,7 @@ namespace nodetool
if (ver == 0)
{
// from v1, we do not store the peer id anymore
- peerid_type peer_id;
+ peerid_type peer_id = AUTO_VAL_INIT (peer_id);
a & peer_id;
}
}
@@ -193,7 +193,6 @@ namespace nodetool
bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr);
bool handle_command_line(
const boost::program_options::variables_map& vm
- , bool testnet
);
bool idle_worker();
bool handle_remote_peerlist(const std::list<peerlist_entry>& peerlist, time_t local_time, const epee::net_utils::connection_context_base& context);
@@ -222,6 +221,7 @@ namespace nodetool
void cache_connect_fail_info(const net_address& addr);
bool is_addr_recently_failed(const net_address& addr);
bool is_priority_node(const net_address& na);
+ std::set<std::string> get_seed_nodes(bool testnet) const;
template <class Container>
bool connect_to_peerlist(const Container& peers);
@@ -325,6 +325,8 @@ namespace nodetool
epee::critical_section m_ip_fails_score_lock;
std::map<uint32_t, uint64_t> m_ip_fails_score;
+
+ bool m_testnet;
};
}
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 568b8208e..5c903b1f5 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -65,6 +65,7 @@
#define NET_MAKE_IP(b1,b2,b3,b4) ((LPARAM)(((DWORD)(b1)<<24)+((DWORD)(b2)<<16)+((DWORD)(b3)<<8)+((DWORD)(b4))))
+#define MIN_WANTED_SEED_NODES 12
namespace nodetool
{
@@ -108,8 +109,8 @@ namespace nodetool
void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_p2p_bind_ip);
- command_line::add_arg(desc, arg_p2p_bind_port);
- command_line::add_arg(desc, arg_testnet_p2p_bind_port);
+ command_line::add_arg(desc, arg_p2p_bind_port, false);
+ command_line::add_arg(desc, arg_testnet_p2p_bind_port, false);
command_line::add_arg(desc, arg_p2p_external_port);
command_line::add_arg(desc, arg_p2p_allow_local_ip);
command_line::add_arg(desc, arg_p2p_add_peer);
@@ -287,10 +288,9 @@ namespace nodetool
template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::handle_command_line(
const boost::program_options::variables_map& vm
- , bool testnet
)
{
- auto p2p_bind_arg = testnet ? arg_testnet_p2p_bind_port : arg_p2p_bind_port;
+ auto p2p_bind_arg = m_testnet ? arg_testnet_p2p_bind_port : arg_p2p_bind_port;
m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip);
m_port = command_line::get_arg(vm, p2p_bind_arg);
@@ -309,7 +309,7 @@ namespace nodetool
bool r = parse_peer_from_string(pe.adr, pr_str);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str);
if (pe.adr.port == 0)
- pe.adr.port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT;
+ pe.adr.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT;
m_command_line_peers.push_back(pe);
}
}
@@ -398,17 +398,42 @@ namespace nodetool
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
+ std::set<std::string> node_server<t_payload_net_handler>::get_seed_nodes(bool testnet) const
+ {
+ std::set<std::string> full_addrs;
+ if (testnet)
+ {
+ full_addrs.insert("212.83.175.67:28080");
+ full_addrs.insert("5.9.100.248:28080");
+ full_addrs.insert("163.172.182.165:28080");
+ full_addrs.insert("195.154.123.123:28080");
+ full_addrs.insert("212.83.172.165:28080");
+ }
+ else
+ {
+ full_addrs.insert("107.152.130.98:18080");
+ full_addrs.insert("212.83.175.67:18080");
+ full_addrs.insert("5.9.100.248:18080");
+ full_addrs.insert("163.172.182.165:18080");
+ full_addrs.insert("161.67.132.39:18080");
+ full_addrs.insert("198.74.231.92:18080");
+ full_addrs.insert("195.154.123.123:28080");
+ full_addrs.insert("212.83.172.165:28080");
+ }
+ return full_addrs;
+ }
+
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::init(const boost::program_options::variables_map& vm)
{
std::set<std::string> full_addrs;
- bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+ m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
- if (testnet)
+ if (m_testnet)
{
memcpy(&m_network_id, &::config::testnet::NETWORK_ID, 16);
- full_addrs.insert("163.172.182.165:28080");
- full_addrs.insert("204.12.248.66:28080");
- full_addrs.insert("5.9.100.248:28080");
+ full_addrs = get_seed_nodes(true);
}
else
{
@@ -481,14 +506,16 @@ namespace nodetool
++i;
}
- if (!full_addrs.size())
+ // append the fallback nodes if we have too few seed nodes to start with
+ if (full_addrs.size() < MIN_WANTED_SEED_NODES)
{
- MINFO("DNS seed node lookup either timed out or failed, falling back to defaults");
- full_addrs.insert("198.74.231.92:18080");
- full_addrs.insert("161.67.132.39:18080");
- full_addrs.insert("163.172.182.165:18080");
- full_addrs.insert("204.12.248.66:18080");
- full_addrs.insert("5.9.100.248:18080");
+ if (full_addrs.empty())
+ MINFO("DNS seed node lookup either timed out or failed, falling back to defaults");
+ else
+ MINFO("Not enough DNS seed nodes found, using fallback defaults too");
+
+ for (const auto &peer: get_seed_nodes(false))
+ full_addrs.insert(peer);
}
}
@@ -499,14 +526,14 @@ namespace nodetool
}
MDEBUG("Number of seed nodes: " << m_seed_nodes.size());
- bool res = handle_command_line(vm, testnet);
+ bool res = handle_command_line(vm);
CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line");
- auto config_arg = testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
+ auto config_arg = m_testnet ? command_line::arg_testnet_data_dir : command_line::arg_data_dir;
m_config_folder = command_line::get_arg(vm, config_arg);
- if ((!testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT))
- || (testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) {
+ if ((!m_testnet && m_port != std::to_string(::config::P2P_DEFAULT_PORT))
+ || (m_testnet && m_port != std::to_string(::config::testnet::P2P_DEFAULT_PORT))) {
m_config_folder = m_config_folder + "/" + m_port;
}
@@ -1083,7 +1110,14 @@ namespace nodetool
while(rand_count < (max_random_index+1)*3 && try_count < 10 && !m_net_server.is_stop_signal_sent())
{
++rand_count;
- size_t random_index = get_random_index_with_fixed_probability(max_random_index);
+ size_t random_index;
+
+ if (use_white_list) {
+ random_index = get_random_index_with_fixed_probability(max_random_index);
+ } else {
+ random_index = crypto::rand<size_t>() % m_peerlist.get_gray_peers_count();
+ }
+
CHECK_AND_ASSERT_MES(random_index < local_peers_count, false, "random_starter_index < peers_local.size() failed!!");
if(tried_peers.count(random_index))
@@ -1135,6 +1169,7 @@ namespace nodetool
{
size_t try_count = 0;
size_t current_index = crypto::rand<size_t>()%m_seed_nodes.size();
+ bool fallback_nodes_added = false;
while(true)
{
if(m_net_server.is_stop_signal_sent())
@@ -1144,8 +1179,22 @@ namespace nodetool
break;
if(++try_count > m_seed_nodes.size())
{
- MWARNING("Failed to connect to any of seed peers, continuing without seeds");
- break;
+ if (!fallback_nodes_added)
+ {
+ MWARNING("Failed to connect to any of seed peers, trying fallback seeds");
+ for (const auto &peer: get_seed_nodes(m_testnet))
+ {
+ MDEBUG("Fallback seed node: " << peer);
+ append_net_address(m_seed_nodes, peer);
+ }
+ fallback_nodes_added = true;
+ // continue for another few cycles
+ }
+ else
+ {
+ MWARNING("Failed to connect to any of seed peers, continuing without seeds");
+ break;
+ }
}
if(++current_index >= m_seed_nodes.size())
current_index = 0;
@@ -1734,7 +1783,6 @@ namespace nodetool
bool node_server<t_payload_net_handler>::parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container)
{
std::vector<std::string> perrs = command_line::get_arg(vm, arg);
- bool testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
for(const std::string& pr_str: perrs)
{
@@ -1742,7 +1790,7 @@ namespace nodetool
bool r = parse_peer_from_string(na, pr_str);
CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str);
if (na.port == 0)
- na.port = testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT;
+ na.port = m_testnet ? ::config::testnet::P2P_DEFAULT_PORT : ::config::P2P_DEFAULT_PORT;
container.push_back(na);
}
diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h
index 69bee890c..6aba7aa24 100644
--- a/src/p2p/net_node_common.h
+++ b/src/p2p/net_node_common.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index 9af3694b6..de0f51cc3 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -446,13 +446,10 @@ namespace nodetool
return false;
}
- size_t x = crypto::rand<size_t>() % (m_peers_gray.size() + 1);
- size_t res = (x * x * x) / (m_peers_gray.size() * m_peers_gray.size()); //parabola \/
-
- LOG_PRINT_L3("Random gray peer index=" << res << "(x="<< x << ", max_index=" << m_peers_gray.size() << ")");
+ size_t random_index = crypto::rand<size_t>() % m_peers_gray.size();
peers_indexed::index<by_time>::type& by_time_index = m_peers_gray.get<by_time>();
- pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), res);
+ pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), random_index);
return true;
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 9a0d12c2e..6891ac80c 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/network_throttle-detail.cpp b/src/p2p/network_throttle-detail.cpp
index d4fe356a9..0747b6f36 100644
--- a/src/p2p/network_throttle-detail.cpp
+++ b/src/p2p/network_throttle-detail.cpp
@@ -2,7 +2,7 @@
/// @author rfree (current maintainer in monero.cc project)
/// @brief implementaion for throttling of connection (count and rate-limit speed etc)
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -216,7 +216,7 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
std::string history_str = oss.str();
- MDEBUG("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)"
+ MTRACE("Throttle " << m_name << ": packet of ~"<<packet_size<<"b " << " (from "<<orginal_size<<" b)"
<< " Speed AVG=" << std::setw(4) << ((long int)(cts .average/1024)) <<"[w="<<cts .window<<"]"
<< " " << std::setw(4) << ((long int)(cts2.average/1024)) <<"[w="<<cts2.window<<"]"
<<" / " << " Limit="<< ((long int)(m_target_speed/1024)) <<" KiB/sec "
@@ -306,7 +306,7 @@ void network_throttle::calculate_times(size_t packet_size, calculate_times_struc
if (dbg) {
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
std::string history_str = oss.str();
- MDEBUG((cts.delay > 0 ? "SLEEP" : "")
+ MTRACE((cts.delay > 0 ? "SLEEP" : "")
<< "dbg " << m_name << ": "
<< "speed is A=" << std::setw(8) <<cts.average<<" vs "
<< "Max=" << std::setw(8) <<M<<" "
diff --git a/src/p2p/network_throttle-detail.hpp b/src/p2p/network_throttle-detail.hpp
index ef7227eb9..c514db450 100644
--- a/src/p2p/network_throttle-detail.hpp
+++ b/src/p2p/network_throttle-detail.hpp
@@ -2,7 +2,7 @@
/// @author rfree (current maintainer in monero.cc project)
/// @brief implementaion for throttling of connection (count and rate-limit speed etc)
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/network_throttle.cpp b/src/p2p/network_throttle.cpp
index 30538bb3c..6d68f3286 100644
--- a/src/p2p/network_throttle.cpp
+++ b/src/p2p/network_throttle.cpp
@@ -26,7 +26,7 @@ Throttling work by:
*/
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/network_throttle.hpp b/src/p2p/network_throttle.hpp
index 4f6cbe9cf..a747cdd71 100644
--- a/src/p2p/network_throttle.hpp
+++ b/src/p2p/network_throttle.hpp
@@ -2,7 +2,7 @@
/// @author rfree (current maintainer in monero.cc project)
/// @brief interface for throttling of connection (count and rate-limit speed etc)
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h
index dc738d92c..5ec012714 100644
--- a/src/p2p/p2p_protocol_defs.h
+++ b/src/p2p/p2p_protocol_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h
index 77daa484d..5e9baa895 100644
--- a/src/p2p/stdafx.h
+++ b/src/p2p/stdafx.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h
index 131ad3487..f850d1cdf 100644
--- a/src/platform/mingw/alloca.h
+++ b/src/platform/mingw/alloca.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h
index 0007d32db..2f3f79a3f 100644
--- a/src/platform/msc/alloca.h
+++ b/src/platform/msc/alloca.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h
index f28d70733..030604ef8 100644
--- a/src/platform/msc/inline_c.h
+++ b/src/platform/msc/inline_c.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h
index 22e34c24a..05d2e7a38 100644
--- a/src/platform/msc/stdbool.h
+++ b/src/platform/msc/stdbool.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h
index 4201c8784..db6eca28b 100644
--- a/src/platform/msc/sys/param.h
+++ b/src/platform/msc/sys/param.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt
index 334a7f350..e15af205a 100644
--- a/src/ringct/CMakeLists.txt
+++ b/src/ringct/CMakeLists.txt
@@ -49,5 +49,6 @@ target_link_libraries(ringct
PUBLIC
common
crypto
+ cryptonote_basic
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/ringct/rctCryptoOps.c b/src/ringct/rctCryptoOps.c
index 9bb9a6891..f69d692f6 100644
--- a/src/ringct/rctCryptoOps.c
+++ b/src/ringct/rctCryptoOps.c
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/ringct/rctCryptoOps.h b/src/ringct/rctCryptoOps.h
index 58c6964d8..2674c2243 100644
--- a/src/ringct/rctCryptoOps.h
+++ b/src/ringct/rctCryptoOps.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index 0e1715072..d0e0964b6 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -36,6 +36,8 @@ using namespace std;
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "ringct"
+#define CHECK_AND_ASSERT_THROW_MES_L1(expr, message) {if(!(expr)) {MWARNING(message); throw std::runtime_error(message);}}
+
namespace rct {
//Various key initialization functions
@@ -175,7 +177,7 @@ namespace rct {
void scalarmultKey(key & aP, const key &P, const key &a) {
ge_p3 A;
ge_p2 R;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_scalarmult(&R, a.bytes, &A);
ge_tobytes(aP.bytes, &R);
}
@@ -184,7 +186,7 @@ namespace rct {
key scalarmultKey(const key & P, const key & a) {
ge_p3 A;
ge_p2 R;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, P.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_scalarmult(&R, a.bytes, &A);
key aP;
ge_tobytes(aP.bytes, &R);
@@ -196,7 +198,7 @@ namespace rct {
key scalarmultH(const key & a) {
ge_p3 A;
ge_p2 R;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A, H.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_scalarmult(&R, a.bytes, &A);
key aP;
ge_tobytes(aP.bytes, &R);
@@ -208,8 +210,8 @@ namespace rct {
//for curve points: AB = A + B
void addKeys(key &AB, const key &A, const key &B) {
ge_p3 B2, A2;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_cached tmp2;
ge_p3_to_cached(&tmp2, &B2);
ge_p1p1 tmp3;
@@ -231,7 +233,7 @@ namespace rct {
void addKeys2(key &aGbB, const key &a, const key &b, const key & B) {
ge_p2 rv;
ge_p3 B2;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_double_scalarmult_base_vartime(&rv, b.bytes, &B2, a.bytes);
ge_tobytes(aGbB.bytes, &rv);
}
@@ -240,7 +242,7 @@ namespace rct {
// input B a curve point and output a ge_dsmp which has precomputation applied
void precomp(ge_dsmp rv, const key & B) {
ge_p3 B2;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_dsm_precomp(rv, &B2);
}
@@ -250,7 +252,7 @@ namespace rct {
void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B) {
ge_p2 rv;
ge_p3 A2;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_double_scalarmult_precomp_vartime(&rv, a.bytes, &A2, b.bytes, B);
ge_tobytes(aAbB.bytes, &rv);
}
@@ -260,8 +262,8 @@ namespace rct {
//AB = A - B where A, B are curve points
void subKeys(key & AB, const key &A, const key &B) {
ge_p3 B2, A2;
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&B2, B.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&A2, A.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_cached tmp2;
ge_p3_to_cached(&tmp2, &B2);
ge_p1p1 tmp3;
@@ -381,7 +383,7 @@ namespace rct {
ge_p2 point;
ge_p3 res;
key h = cn_fast_hash(hh);
- CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
+ CHECK_AND_ASSERT_THROW_MES_L1(ge_frombytes_vartime(&res, h.bytes) == 0, "ge_frombytes_vartime failed at "+boost::lexical_cast<std::string>(__LINE__));
ge_p3_to_p2(&point, &res);
ge_mul8(&point2, &point);
ge_p1p1_to_p3(&res, &point2);
diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h
index 90f54b050..cb19bbbd6 100644
--- a/src/ringct/rctOps.h
+++ b/src/ringct/rctOps.h
@@ -66,6 +66,7 @@ namespace rct {
static const key Z = { {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, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
static const key I = { {0x01, 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, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
+ static const key L = { {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 } };
//Creates a zero scalar
inline key zero() { return Z; }
@@ -73,6 +74,9 @@ namespace rct {
//Creates a zero elliptic curve point
inline key identity() { return I; }
inline void identity(key &Id) { memcpy(&Id, &I, 32); }
+ //Creates a key equal to the curve order
+ inline key curveOrder() { return L; }
+ inline void curveOrder(key &l) { l = L; }
//copies a scalar or point
inline void copy(key &AA, const key &A) { memcpy(&AA, &A, 32); }
inline key copy(const key & A) { key AA; memcpy(&AA, &A, 32); return AA; }
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index b8f3596e8..8efd6a07c 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -34,7 +34,7 @@
#include "common/thread_group.h"
#include "common/util.h"
#include "rctSigs.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
using namespace crypto;
using namespace std;
@@ -873,8 +873,7 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig");
- CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs");
- CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs");
+ CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
//mask amount and mask
@@ -902,8 +901,7 @@ namespace rct {
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) {
CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig");
- CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs");
- CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs");
+ CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
//mask amount and mask
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 0c27745e1..c820fb297 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -47,6 +47,8 @@ extern "C" {
#include "crypto/generic-ops.h"
#include "crypto/crypto.h"
+#include "hex.h"
+#include "span.h"
#include "serialization/serialization.h"
#include "serialization/debug_archive.h"
#include "serialization/binary_archive.h"
@@ -443,8 +445,9 @@ namespace cryptonote {
static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); }
}
-template<typename T> std::ostream &print256(std::ostream &o, const T &v);
-inline std::ostream &operator <<(std::ostream &o, const rct::key &v) { return print256(o, v); }
+inline std::ostream &operator <<(std::ostream &o, const rct::key &v) {
+ epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
+}
BLOB_SERIALIZER(rct::key);
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index 6df93cde1..f6037d7e3 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -27,9 +27,11 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(rpc_sources
- core_rpc_server.cpp)
+ core_rpc_server.cpp
+ rpc_args.cpp)
-set(rpc_headers)
+set(rpc_headers
+ rpc_args.h)
set(rpc_private_headers
core_rpc_server.h
@@ -44,9 +46,11 @@ monero_add_library(rpc
${rpc_private_headers})
target_link_libraries(rpc
PUBLIC
+ common
cryptonote_core
cryptonote_protocol
epee
+ ${Boost_REGEX_LIBRARY}
${Boost_THREAD_LIBRARY}
PRIVATE
${EXTRA_LIBRARIES})
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index b2e8e6716..76a69fbf6 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -33,13 +33,20 @@ using namespace epee;
#include "core_rpc_server.h"
#include "common/command_line.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "cryptonote_core/account.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "common/updates.h"
+#include "common/download.h"
+#include "common/util.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_basic/account.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "misc_language.h"
#include "crypto/hash.h"
+#include "rpc/rpc_args.h"
#include "core_rpc_server_error_codes.h"
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc"
+
#define MAX_RESTRICTED_FAKE_OUTS_COUNT 40
#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500
@@ -49,11 +56,10 @@ namespace cryptonote
//-----------------------------------------------------------------------------------
void core_rpc_server::init_options(boost::program_options::options_description& desc)
{
- command_line::add_arg(desc, arg_rpc_bind_ip);
command_line::add_arg(desc, arg_rpc_bind_port);
command_line::add_arg(desc, arg_testnet_rpc_bind_port);
command_line::add_arg(desc, arg_restricted_rpc);
- command_line::add_arg(desc, arg_user_agent);
+ cryptonote::rpc_args::init_options(desc);
}
//------------------------------------------------------------------------------------------------------------------------------
core_rpc_server::core_rpc_server(
@@ -64,29 +70,29 @@ namespace cryptonote
, m_p2p(p2p)
{}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::handle_command_line(
+ bool core_rpc_server::init(
const boost::program_options::variables_map& vm
)
{
+ m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
+ m_net_server.set_threads_prefix("RPC");
+
auto p2p_bind_arg = m_testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port;
- m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip);
- m_port = command_line::get_arg(vm, p2p_bind_arg);
+ auto rpc_config = cryptonote::rpc_args::process(vm);
+ if (!rpc_config)
+ return false;
+
m_restricted = command_line::get_arg(vm, arg_restricted_rpc);
- return true;
- }
- //------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::init(
- const boost::program_options::variables_map& vm
- )
- {
- m_testnet = command_line::get_arg(vm, command_line::arg_testnet_on);
- std::string m_user_agent = command_line::get_arg(vm, command_line::arg_user_agent);
- m_net_server.set_threads_prefix("RPC");
- bool r = handle_command_line(vm);
- CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server");
- return epee::http_server_impl_base<core_rpc_server, connection_context>::init(m_port, m_bind_ip, m_user_agent);
+ boost::optional<epee::net_utils::http::login> http_login{};
+ std::string port = command_line::get_arg(vm, p2p_bind_arg);
+ if (rpc_config->login)
+ http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
+
+ return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
+ std::move(port), std::move(rpc_config->bind_ip), std::move(http_login)
+ );
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::check_core_busy()
@@ -148,10 +154,27 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ static cryptonote::blobdata get_pruned_tx_blob(const cryptonote::blobdata &blobdata)
+ {
+ cryptonote::transaction tx;
+
+ if (!cryptonote::parse_and_validate_tx_from_blob(blobdata, tx))
+ {
+ MERROR("Failed to parse and validate tx from blob");
+ return blobdata;
+ }
+
+ std::stringstream ss;
+ binary_archive<true> ba(ss);
+ bool r = tx.serialize_base(ba);
+ CHECK_AND_ASSERT_MES(r, blobdata, "Failed to serialize rct signatures base");
+ return ss.str();
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res)
{
CHECK_CORE_BUSY();
- std::list<std::pair<block, std::list<transaction> > > bs;
+ std::list<std::pair<cryptonote::blobdata, std::list<cryptonote::blobdata> > > bs;
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
@@ -159,24 +182,39 @@ namespace cryptonote
return false;
}
- for(auto& b: bs)
+ size_t pruned_size = 0, unpruned_size = 0, ntxes = 0;
+ for(auto& bd: bs)
{
res.blocks.resize(res.blocks.size()+1);
- res.blocks.back().block = block_to_blob(b.first);
+ res.blocks.back().block = bd.first;
+ pruned_size += bd.first.size();
+ unpruned_size += bd.first.size();
res.output_indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices());
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
- bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.first.miner_tx), res.output_indices.back().indices.back().indices);
+ block b;
+ if (!parse_and_validate_block_from_blob(bd.first, b))
+ {
+ res.status = "Invalid block";
+ return false;
+ }
+ bool r = m_core.get_tx_outputs_gindexs(get_transaction_hash(b.miner_tx), res.output_indices.back().indices.back().indices);
if (!r)
{
res.status = "Failed";
return false;
}
size_t txidx = 0;
- for(auto& t: b.second)
+ ntxes += bd.second.size();
+ for(const auto& t: bd.second)
{
- res.blocks.back().txs.push_back(tx_to_blob(t));
+ if (req.prune)
+ res.blocks.back().txs.push_back(get_pruned_tx_blob(t));
+ else
+ res.blocks.back().txs.push_back(t);
+ pruned_size += res.blocks.back().txs.back().size();
+ unpruned_size += t.size();
res.output_indices.back().indices.push_back(COMMAND_RPC_GET_BLOCKS_FAST::tx_output_indices());
- bool r = m_core.get_tx_outputs_gindexs(b.first.tx_hashes[txidx++], res.output_indices.back().indices.back().indices);
+ bool r = m_core.get_tx_outputs_gindexs(b.tx_hashes[txidx++], res.output_indices.back().indices.back().indices);
if (!r)
{
res.status = "Failed";
@@ -185,6 +223,7 @@ namespace cryptonote
}
}
+ MDEBUG("on_get_blocks: " << bs.size() << " blocks, " << ntxes << " txes, pruned size " << pruned_size << ", unpruned size " << unpruned_size);
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -635,7 +674,7 @@ namespace cryptonote
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
- if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs))
+ if(!m_core.get_miner().start(adr, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.status = "Failed, mining not started";
LOG_PRINT_L0(res.status);
@@ -659,10 +698,11 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res)
{
- CHECK_CORE_READY();
+ CHECK_CORE_BUSY();
const miner& lMiner = m_core.get_miner();
res.active = lMiner.is_mining();
+ res.is_background_mining_enabled = lMiner.get_is_background_mining_enabled();
if ( lMiner.is_mining() ) {
res.speed = lMiner.get_speed();
@@ -736,7 +776,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res)
{
- mlog_set_categories(req.categories.c_str());
+ mlog_set_log(req.categories.c_str());
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -749,6 +789,14 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res)
+ {
+ CHECK_CORE_BUSY();
+ m_core.get_pool_transaction_hashes(res.tx_hashes);
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res)
{
// FIXME: replace back to original m_p2p.send_stop_signal() after
@@ -835,7 +883,7 @@ namespace cryptonote
block b = AUTO_VAL_INIT(b);
cryptonote::blobdata blob_reserve;
blob_reserve.resize(req.reserve_size, 0);
- if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve))
+ if(!m_core.get_block_template(b, acc, res.difficulty, res.height, res.expected_reward, blob_reserve))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
@@ -1386,6 +1434,7 @@ namespace cryptonote
std::pair<uint64_t, uint64_t> amounts = m_core.get_coinbase_tx_sum(req.height, req.count);
res.emission_amount = amounts.first;
res.fee_amount = amounts.second;
+ res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1436,21 +1485,155 @@ namespace cryptonote
bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res)
{
m_p2p.set_save_graph(true);
+ res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res)
{
m_p2p.set_save_graph(false);
+ res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res)
+ {
+ static const char software[] = "monero";
+#ifdef BUILD_TAG
+ static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG);
+#else
+ static const char buildtag[] = "source";
+#endif
- const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_bind_ip = {
- "rpc-bind-ip"
- , "IP for RPC server"
- , "127.0.0.1"
- };
+ if (req.command != "check" && req.command != "download" && req.command != "update")
+ {
+ res.status = std::string("unknown command: '") + req.command + "'";
+ return true;
+ }
+
+ std::string version, hash;
+ if (!tools::check_updates(software, buildtag, version, hash))
+ {
+ res.status = "Error checking for updates";
+ return true;
+ }
+ if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0)
+ {
+ res.update = false;
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ res.update = true;
+ res.version = version;
+ res.user_uri = tools::get_update_url(software, "cli", buildtag, version, true);
+ res.auto_uri = tools::get_update_url(software, "cli", buildtag, version, false);
+ res.hash = hash;
+ if (req.command == "check")
+ {
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+
+ boost::filesystem::path path;
+ if (req.path.empty())
+ {
+ std::string filename;
+ const char *slash = strrchr(res.auto_uri.c_str(), '/');
+ if (slash)
+ filename = slash + 1;
+ else
+ filename = std::string(software) + "-update-" + version;
+ path = epee::string_tools::get_current_module_folder();
+ path /= filename;
+ }
+ else
+ {
+ path = req.path;
+ }
+
+ crypto::hash file_hash;
+ if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash)))
+ {
+ MDEBUG("We don't have that file already, downloading");
+ if (!tools::download(path.string(), res.auto_uri))
+ {
+ MERROR("Failed to download " << res.auto_uri);
+ return false;
+ }
+ if (!tools::sha256sum(path.string(), file_hash))
+ {
+ MERROR("Failed to hash " << path);
+ return false;
+ }
+ if (hash != epee::string_tools::pod_to_hex(file_hash))
+ {
+ MERROR("Download from " << res.auto_uri << " does not match the expected hash");
+ return false;
+ }
+ MINFO("New version downloaded to " << path);
+ }
+ else
+ {
+ MDEBUG("We already have " << path << " with expected hash");
+ }
+ res.path = path.string();
+
+ if (req.command == "download")
+ {
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+
+ res.status = "'update' not implemented yet";
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp)
+ {
+ if(!check_core_busy())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY;
+ error_resp.message = "Core is busy.";
+ return false;
+ }
+
+ bool failed = false;
+ for (const auto &str: req.txids)
+ {
+ cryptonote::blobdata txid_data;
+ if(!epee::string_tools::parse_hexstr_to_binbuff(str, txid_data))
+ {
+ res.status = std::string("Invalid transaction id: ") + str;
+ failed = true;
+ }
+ crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
+
+ cryptonote::transaction tx;
+ bool r = m_core.get_pool_transaction(txid, tx);
+ if (r)
+ {
+ cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
+ NOTIFY_NEW_TRANSACTIONS::request r;
+ r.txs.push_back(cryptonote::tx_to_blob(tx));
+ m_core.get_protocol()->relay_transactions(r, fake_context);
+ //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
+ }
+ else
+ {
+ res.status = std::string("Transaction not found in pool: ") + str;
+ failed = true;
+ }
+ }
+
+ if (failed)
+ {
+ return false;
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_bind_port = {
"rpc-bind-port"
@@ -1469,11 +1652,4 @@ namespace cryptonote
, "Restrict RPC to view only commands"
, false
};
-
- const command_line::arg_descriptor<std::string> core_rpc_server::arg_user_agent = {
- "user-agent"
- , "Restrict RPC to clients using this user agent"
- , ""
- };
-
} // namespace cryptonote
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 767bcc715..0aa222c87 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -52,11 +52,9 @@ namespace cryptonote
{
public:
- static const command_line::arg_descriptor<std::string> arg_rpc_bind_ip;
static const command_line::arg_descriptor<std::string> arg_rpc_bind_port;
static const command_line::arg_descriptor<std::string> arg_testnet_rpc_bind_port;
static const command_line::arg_descriptor<bool> arg_restricted_rpc;
- static const command_line::arg_descriptor<std::string> arg_user_agent;
typedef epee::net_utils::connection_context_base connection_context;
@@ -94,12 +92,14 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted)
MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL)
+ MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES)
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
+ MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT)
MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH)
@@ -121,6 +121,7 @@ namespace cryptonote
MAP_JON_RPC_WE("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM)
MAP_JON_RPC_WE("get_fee_estimate", on_get_per_kb_fee_estimate, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE)
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
+ MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -146,10 +147,12 @@ namespace cryptonote
bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res);
bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res);
bool on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res);
+ bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res);
bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res);
bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res);
bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res);
bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res);
+ bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res);
//json_rpc
bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res);
@@ -172,13 +175,10 @@ namespace cryptonote
bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp);
bool on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp);
bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp);
+ bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp);
//-----------------------
private:
-
- bool handle_command_line(
- const boost::program_options::variables_map& vm
- );
bool check_core_busy();
bool check_core_ready();
@@ -188,8 +188,6 @@ private:
core& m_core;
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p;
- std::string m_port;
- std::string m_bind_ip;
bool m_testnet;
bool m_restricted;
};
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 0fc230d11..9bdadf0d1 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,8 +30,8 @@
#pragma once
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
-#include "cryptonote_core/cryptonote_basic.h"
-#include "cryptonote_core/difficulty.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+#include "cryptonote_basic/difficulty.h"
#include "crypto/hash.h"
namespace cryptonote
@@ -49,7 +49,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 1
-#define CORE_RPC_VERSION_MINOR 6
+#define CORE_RPC_VERSION_MINOR 10
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -80,9 +80,11 @@ namespace cryptonote
{
std::list<crypto::hash> block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */
uint64_t start_height;
+ bool prune;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE(start_height)
+ KV_SERIALIZE(prune)
END_KV_SERIALIZE_MAP()
};
@@ -500,10 +502,14 @@ namespace cryptonote
{
std::string miner_address;
uint64_t threads_count;
+ bool do_background_mining;
+ bool ignore_battery;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(miner_address)
KV_SERIALIZE(threads_count)
+ KV_SERIALIZE(do_background_mining)
+ KV_SERIALIZE(ignore_battery)
END_KV_SERIALIZE_MAP()
};
@@ -608,6 +614,7 @@ namespace cryptonote
uint64_t speed;
uint32_t threads_count;
std::string address;
+ bool is_background_mining_enabled;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
@@ -615,6 +622,7 @@ namespace cryptonote
KV_SERIALIZE(speed)
KV_SERIALIZE(threads_count)
KV_SERIALIZE(address)
+ KV_SERIALIZE(is_background_mining_enabled)
END_KV_SERIALIZE_MAP()
};
};
@@ -684,6 +692,7 @@ namespace cryptonote
uint64_t difficulty;
uint64_t height;
uint64_t reserved_offset;
+ uint64_t expected_reward;
std::string prev_hash;
blobdata blocktemplate_blob;
blobdata blockhashing_blob;
@@ -693,6 +702,7 @@ namespace cryptonote
KV_SERIALIZE(difficulty)
KV_SERIALIZE(height)
KV_SERIALIZE(reserved_offset)
+ KV_SERIALIZE(expected_reward)
KV_SERIALIZE(prev_hash)
KV_SERIALIZE(blocktemplate_blob)
KV_SERIALIZE(blockhashing_blob)
@@ -1017,6 +1027,26 @@ namespace cryptonote
};
};
+ struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string status;
+ std::vector<crypto::hash> tx_hashes;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
struct COMMAND_RPC_GET_CONNECTIONS
{
struct request
@@ -1432,4 +1462,60 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
};
+
+ struct COMMAND_RPC_UPDATE
+ {
+ struct request
+ {
+ std::string command;
+ std::string path;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(command);
+ KV_SERIALIZE(path);
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string status;
+ bool update;
+ std::string version;
+ std::string user_uri;
+ std::string auto_uri;
+ std::string hash;
+ std::string path;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(update)
+ KV_SERIALIZE(version)
+ KV_SERIALIZE(user_uri)
+ KV_SERIALIZE(auto_uri)
+ KV_SERIALIZE(hash)
+ KV_SERIALIZE(path)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_RELAY_TX
+ {
+ struct request
+ {
+ std::list<std::string> txids;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(txids)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
}
diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h
index 4ac48a1c1..269cce2b1 100644
--- a/src/rpc/core_rpc_server_error_codes.h
+++ b/src/rpc/core_rpc_server_error_codes.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
new file mode 100644
index 000000000..4435f74d1
--- /dev/null
+++ b/src/rpc/rpc_args.cpp
@@ -0,0 +1,96 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#include "rpc_args.h"
+
+#include <boost/asio/ip/address.hpp>
+#include "common/command_line.h"
+#include "common/i18n.h"
+
+namespace cryptonote
+{
+ rpc_args::descriptors::descriptors()
+ : rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify ip to bind rpc server"), "127.0.0.1"})
+ , rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
+ , confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
+ {}
+
+ const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); }
+
+ void rpc_args::init_options(boost::program_options::options_description& desc)
+ {
+ const descriptors arg{};
+ command_line::add_arg(desc, arg.rpc_bind_ip);
+ command_line::add_arg(desc, arg.rpc_login);
+ command_line::add_arg(desc, arg.confirm_external_bind);
+ }
+
+ boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm)
+ {
+ const descriptors arg{};
+ rpc_args config{};
+
+ config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip);
+ if (!config.bind_ip.empty())
+ {
+ // always parse IP here for error consistency
+ boost::system::error_code ec{};
+ const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ip, ec);
+ if (ec)
+ {
+ LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ip.name);
+ return boost::none;
+ }
+
+ if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind))
+ {
+ LOG_ERROR(
+ "--" << arg.rpc_bind_ip.name <<
+ tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") <<
+ arg.confirm_external_bind.name
+ );
+ return boost::none;
+ }
+ }
+
+ if (command_line::has_arg(vm, arg.rpc_login))
+ {
+ config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, "RPC server password");
+ if (!config.login)
+ return boost::none;
+
+ if (config.login->username.empty())
+ {
+ LOG_ERROR(tr("Username specified with --") << arg.rpc_login.name << tr(" cannot be empty"));
+ return boost::none;
+ }
+ }
+
+ return {std::move(config)};
+ }
+}
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
new file mode 100644
index 000000000..d6e7bab07
--- /dev/null
+++ b/src/rpc/rpc_args.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2014-2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#pragma once
+
+#include <boost/optional/optional.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <string>
+
+#include "common/command_line.h"
+#include "common/password.h"
+
+namespace cryptonote
+{
+ //! Processes command line arguments related to server-side RPC
+ struct rpc_args
+ {
+ // non-static construction prevents initialization order issues
+ struct descriptors
+ {
+ descriptors();
+ descriptors(const descriptors&) = delete;
+ descriptors(descriptors&&) = delete;
+ descriptors& operator=(const descriptors&) = delete;
+ descriptors& operator=(descriptors&&) = delete;
+
+ const command_line::arg_descriptor<std::string> rpc_bind_ip;
+ const command_line::arg_descriptor<std::string> rpc_login;
+ const command_line::arg_descriptor<bool> confirm_external_bind;
+ };
+
+ static const char* tr(const char* str);
+ static void init_options(boost::program_options::options_description& desc);
+
+ //! \return Arguments specified by user, or `boost::none` if error
+ static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm);
+
+ std::string bind_ip;
+ boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
+ };
+}
diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h
index 3a5343c9c..0a267b081 100644
--- a/src/serialization/binary_archive.h
+++ b/src/serialization/binary_archive.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h
index ab4a86c68..08eba41da 100644
--- a/src/serialization/binary_utils.h
+++ b/src/serialization/binary_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h
index 9a7e89c49..4213f2e58 100644
--- a/src/serialization/crypto.h
+++ b/src/serialization/crypto.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/debug_archive.h b/src/serialization/debug_archive.h
index f46a7ee2b..c5365aab7 100644
--- a/src/serialization/debug_archive.h
+++ b/src/serialization/debug_archive.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/json_archive.h b/src/serialization/json_archive.h
index 629a37311..8f74e26b1 100644
--- a/src/serialization/json_archive.h
+++ b/src/serialization/json_archive.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/json_utils.h b/src/serialization/json_utils.h
index 5b7871072..32e7b69cf 100644
--- a/src/serialization/json_utils.h
+++ b/src/serialization/json_utils.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index dac43720b..639240820 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/string.h b/src/serialization/string.h
index 2a5228a3f..b94f43dd8 100644
--- a/src/serialization/string.h
+++ b/src/serialization/string.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/variant.h b/src/serialization/variant.h
index a2cce9fa1..9048e2963 100644
--- a/src/serialization/variant.h
+++ b/src/serialization/variant.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/serialization/vector.h b/src/serialization/vector.h
index 7f2bc78ba..598cfb92e 100644
--- a/src/serialization/vector.h
+++ b/src/serialization/vector.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt
index 259008ac5..18a8bac68 100644
--- a/src/simplewallet/CMakeLists.txt
+++ b/src/simplewallet/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index a8f1d177f..6c2df4b22 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -50,7 +50,7 @@
#include "p2p/net_node.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
#include "simplewallet.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "storages/http_abstract_invoke.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "crypto/crypto.h" // for crypto::secret_key definition
@@ -101,6 +101,7 @@ enum TransferType {
namespace
{
+ const auto allowed_priority_strings = {"default", "unimportant", "normal", "elevated", "priority"};
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
@@ -377,12 +378,12 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
if(orig_pwd_container == boost::none)
{
fail_msg_writer() << tr("Your original password was incorrect.");
- return false;
+ return true;
}
- // prompts for a new password, this is not a new wallet so pass in false.
- const auto pwd_container = tools::wallet2::password_prompt(false);
-
+ // prompts for a new password, pass true to verify the password
+ const auto pwd_container = tools::wallet2::password_prompt(true);
+
try
{
m_wallet->rewrite(m_wallet_file, pwd_container->password());
@@ -391,7 +392,7 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
catch (const tools::error::wallet_logic_error& e)
{
fail_msg_writer() << tr("Error with wallet rewrite: ") << e.what();
- return false;
+ return true;
}
return true;
@@ -482,16 +483,11 @@ bool simple_wallet::set_default_mixin(const std::vector<std::string> &args/* = s
bool simple_wallet::set_default_priority(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
int priority = 0;
- if (m_wallet->watch_only())
- {
- fail_msg_writer() << tr("wallet is watch-only and cannot transfer");
- return true;
- }
try
{
if (strchr(args[1].c_str(), '-'))
{
- fail_msg_writer() << tr("priority must be 0, 1, 2, or 3 ");
+ fail_msg_writer() << tr("priority must be 0, 1, 2, 3, or 4 ");
return true;
}
if (args[1] == "0")
@@ -501,9 +497,9 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/*
else
{
priority = boost::lexical_cast<int>(args[1]);
- if (priority != 1 && priority != 2 && priority != 3)
+ if (priority < 1 || priority > 4)
{
- fail_msg_writer() << tr("priority must be 0, 1, 2, or 3");
+ fail_msg_writer() << tr("priority must be 0, 1, 2, 3,or 4");
return true;
}
}
@@ -518,7 +514,7 @@ bool simple_wallet::set_default_priority(const std::vector<std::string> &args/*
}
catch(const boost::bad_lexical_cast &)
{
- fail_msg_writer() << tr("priority must be 0, 1, 2 or 3");
+ fail_msg_writer() << tr("priority must be 0, 1, 2 3,or 4");
return true;
}
catch(...)
@@ -564,27 +560,98 @@ bool simple_wallet::set_refresh_type(const std::vector<std::string> &args/* = st
bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
- if (m_wallet->watch_only())
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
{
- fail_msg_writer() << tr("wallet is watch-only and cannot transfer");
+ m_wallet->confirm_missing_payment_id(is_it_true(args[1]));
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
+bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ m_wallet->ask_password(is_it_true(args[1]));
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
+bool simple_wallet::set_unit(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const std::string &unit = args[1];
+ unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT;
+
+ if (unit == "monero")
+ decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT;
+ else if (unit == "millinero")
+ decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 3;
+ else if (unit == "micronero")
+ decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 6;
+ else if (unit == "nanonero")
+ decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 9;
+ else if (unit == "piconero")
+ decimal_point = 0;
+ else
+ {
+ fail_msg_writer() << tr("invalid unit");
return true;
}
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->confirm_missing_payment_id(is_it_true(args[1]));
+ m_wallet->set_default_decimal_point(decimal_point);
m_wallet->rewrite(m_wallet_file, pwd_container->password());
}
return true;
}
-bool simple_wallet::set_ask_password(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+bool simple_wallet::set_min_output_count(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
+ uint32_t count;
+ if (!string_tools::get_xtype_from_string(count, args[1]))
+ {
+ fail_msg_writer() << tr("invalid count: must be an unsigned integer");
+ return true;
+ }
+
const auto pwd_container = get_and_verify_password();
if (pwd_container)
{
- m_wallet->ask_password(is_it_true(args[1]));
+ m_wallet->set_min_output_count(count);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
+bool simple_wallet::set_min_output_value(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ uint64_t value;
+ if (!cryptonote::parse_amount(value, args[1]))
+ {
+ fail_msg_writer() << tr("invalid value");
+ return true;
+ }
+
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ m_wallet->set_min_output_value(value);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
+bool simple_wallet::set_merge_destinations(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ m_wallet->merge_destinations(is_it_true(args[1]));
m_wallet->rewrite(m_wallet_file, pwd_container->password());
}
return true;
@@ -612,11 +679,12 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] - Show incoming transfers, all or filtered by availability"));
m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments <PID_1> [<PID_2> ... <PID_N>] - Show payments for given payment ID[s]"));
m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height"));
- m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of extra inputs to include for untraceability (from 2 to maximum available)"));
- m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("Same as transfer_original, but using a new transaction building algorithm"));
+ m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("Same as transfer, but using an older transaction building algorithm"));
+ m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("transfer [<priority>] [<mixin_count>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <mixin_count> is the number of extra inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [<mixin_count>] <addr> <amount> <lockblocks>(Number of blocks to lock the transaction for, max 1000000) [<payment_id>]"));
m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with mixin 0"));
- m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance an address"));
+ m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [mixin] address [payment_id] - Send all unlocked balance to an address"));
+ m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [mixin] address [payment_id] - Send all unlocked outputs below the threshold to an address"));
m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [<mixin_count>] <amount> [payment_id] - Donate <amount> to the development team (donate.getmonero.org)"));
m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file"));
m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file"));
@@ -629,7 +697,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("viewkey", boost::bind(&simple_wallet::viewkey, this, _1), tr("Display private view key"));
m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Display private spend key"));
m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display Electrum-style mnemonic seed"));
- m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [1|2|3] - normal/elevated/priority fee; confirm-missing-payment-id <1|0>"));
+ m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-mixin <n> - set default mixin (default is 4); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-output-count [n] - try to keep at least that many outputs of value at least min-output-value; min-output-value [n] - try to keep at least min-output-count outputs of at least that value - merge-destinations <1|0> - whether to merge multiple payments to the same destination address"));
m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>"));
m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>"));
@@ -646,6 +714,7 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("Export a set of outputs owned by this wallet"));
m_cmd_binder.set_handler("import_outputs", boost::bind(&simple_wallet::import_outputs, this, _1), tr("Import set of outputs owned by this wallet"));
m_cmd_binder.set_handler("show_transfer", boost::bind(&simple_wallet::show_transfer, this, _1), tr("Show information about a transfer to/from this address"));
+ m_cmd_binder.set_handler("password", boost::bind(&simple_wallet::change_password, this, _1), tr("Change wallet password"));
m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
}
//----------------------------------------------------------------------------------------------------
@@ -663,10 +732,29 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "priority = " << m_wallet->get_default_priority();
success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id();
success_msg_writer() << "ask-password = " << m_wallet->ask_password();
+ success_msg_writer() << "unit = " << cryptonote::get_unit(m_wallet->get_default_decimal_point());
+ success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count();
+ success_msg_writer() << "min-outputs-value = " << cryptonote::print_money(m_wallet->get_min_output_value());
+ success_msg_writer() << "merge-destinations = " << m_wallet->merge_destinations();
return true;
}
else
{
+
+#define CHECK_SIMPLE_VARIABLE(name, f, help) do \
+ if (args[0] == name) { \
+ if (args.size() <= 1) \
+ { \
+ fail_msg_writer() << "set " << #name << ": " << tr("needs an argument") << " (" << help << ")"; \
+ return true; \
+ } \
+ else \
+ { \
+ f(args); \
+ return true; \
+ } \
+ } while(0)
+
if (args[0] == "seed")
{
if (args.size() == 1)
@@ -680,124 +768,19 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
return true;
}
}
- else if (args[0] == "always-confirm-transfers")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set always-confirm-transfers: needs an argument (0 or 1)");
- return true;
- }
- else
- {
- set_always_confirm_transfers(args);
- return true;
- }
- }
- else if (args[0] == "print-ring-members")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set print-ring-members: needs an argument (0 or 1)");
- return true;
- }
- else
- {
- set_print_ring_members(args);
- return true;
- }
- }
- else if (args[0] == "store-tx-info")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set store-tx-info: needs an argument (0 or 1)");
- return true;
- }
- else
- {
- set_store_tx_info(args);
- return true;
- }
- }
- else if (args[0] == "default-mixin")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set default-mixin: needs an argument (integer >= 2)");
- return true;
- }
- else
- {
- set_default_mixin(args);
- return true;
- }
- }
- else if (args[0] == "auto-refresh")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set auto-refresh: needs an argument (0 or 1)");
- return true;
- }
- else
- {
- set_auto_refresh(args);
- return true;
- }
- }
- else if (args[0] == "refresh-type")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set refresh-type: needs an argument:") <<
- tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)");
- return true;
- }
- else
- {
- set_refresh_type(args);
- return true;
- }
- }
- else if (args[0] == "priority")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set priority: needs an argument: 0, 1, 2, or 3");
- return true;
- }
- else
- {
- set_default_priority(args);
- return true;
- }
- }
- else if (args[0] == "confirm-missing-payment-id")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set confirm-missing-payment-id: needs an argument (0 or 1)");
- return true;
- }
- else
- {
- set_confirm_missing_payment_id(args);
- return true;
- }
- }
- else if (args[0] == "ask-password")
- {
- if (args.size() <= 1)
- {
- fail_msg_writer() << tr("set ask-password: needs an argument (0 or 1)");
- return true;
- }
- else
- {
- set_ask_password(args);
- return true;
- }
- }
+ CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("default-mixin", set_default_mixin, tr("integer >= 2"));
+ CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)"));
+ CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4"));
+ CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanop, piconero"));
+ CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer"));
+ CHECK_SIMPLE_VARIABLE("min-outputs-value", set_min_output_value, tr("amount"));
+ CHECK_SIMPLE_VARIABLE("merge-destinations", set_merge_destinations, tr("0 or 1"));
}
fail_msg_writer() << tr("set: unrecognized argument(s)");
return true;
@@ -811,21 +794,7 @@ bool simple_wallet::set_log(const std::vector<std::string> &args)
fail_msg_writer() << tr("usage: set_log <log_level_number_0-4> | <categories>");
return true;
}
- uint16_t l = 0;
- if(epee::string_tools::get_xtype_from_string(l, args[0]))
- {
- if(4 < l)
- {
- fail_msg_writer() << tr("wrong number range, use: set_log <log_level_number_0-4>");
- return true;
- }
-
- mlog_set_log_level(l);
- }
- else
- {
- mlog_set_categories(args.front().c_str());
- }
+ mlog_set_log(args[0].c_str());
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -923,6 +892,15 @@ void simple_wallet::print_seed(std::string seed)
std::cout << seed << std::endl;
}
//----------------------------------------------------------------------------------------------------
+static bool might_be_partial_seed(std::string words)
+{
+ std::vector<std::string> seed;
+
+ boost::algorithm::trim(words);
+ boost::split(seed, words, boost::is_any_of(" "), boost::token_compress_on);
+ return seed.size() < 24;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
if (!handle_command_line(vm))
@@ -952,14 +930,20 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (m_electrum_seed.empty())
{
- m_electrum_seed = command_line::input_line("Specify Electrum seed: ");
- if (std::cin.eof())
- return false;
- if (m_electrum_seed.empty())
+ m_electrum_seed = "";
+ do
{
- fail_msg_writer() << tr("specify a recovery parameter with the --electrum-seed=\"words list here\"");
- return false;
- }
+ const char *prompt = m_electrum_seed.empty() ? "Specify Electrum seed: " : "Electrum seed continued: ";
+ std::string electrum_seed = command_line::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))
@@ -997,7 +981,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
cryptonote::blobdata viewkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
{
fail_msg_writer() << tr("failed to parse view key secret key");
return false;
@@ -1049,7 +1033,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
cryptonote::blobdata spendkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
{
fail_msg_writer() << tr("failed to parse spend key secret key");
return false;
@@ -1065,7 +1049,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
cryptonote::blobdata viewkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
{
fail_msg_writer() << tr("failed to parse view key secret key");
return false;
@@ -1192,7 +1176,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
catch (const std::exception &e) { }
- m_http_client.set_server(m_wallet->get_daemon_address());
+ m_http_client.set_server(m_wallet->get_daemon_address(), m_wallet->get_daemon_login());
m_wallet->callback(this);
return true;
}
@@ -1282,7 +1266,7 @@ std::string simple_wallet::get_mnemonic_language()
fail_msg_writer() << tr("invalid language choice passed. Please try again.\n");
}
}
- catch (std::exception &e)
+ catch (const std::exception &e)
{
fail_msg_writer() << tr("invalid language choice passed. Please try again.\n");
}
@@ -1561,16 +1545,15 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
return true;
assert(m_wallet);
- COMMAND_RPC_START_MINING::request req;
+ COMMAND_RPC_START_MINING::request req = AUTO_VAL_INIT(req);
req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
bool ok = true;
size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2));
- if (0 == args.size())
- {
- req.threads_count = 1;
- }
- else if (1 == args.size())
+ size_t arg_size = args.size();
+ if(arg_size >= 3) req.ignore_battery = args[2] == "true";
+ if(arg_size >= 2) req.do_background_mining = args[1] == "true";
+ if(arg_size >= 1)
{
uint16_t num = 1;
ok = string_tools::get_xtype_from_string(num, args[0]);
@@ -1579,12 +1562,12 @@ bool simple_wallet::start_mining(const std::vector<std::string>& args)
}
else
{
- ok = false;
+ req.threads_count = 1;
}
if (!ok)
{
- fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>], "
+ fail_msg_writer() << tr("invalid arguments. Please use start_mining [<number_of_threads>] [do_bg_mining] [ignore_battery], "
"<number_of_threads> should be from 1 to ") << max_mining_threads_count;
return true;
}
@@ -1639,11 +1622,11 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block
m_refresh_progress_reporter.update(height, false);
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount)
+void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount)
{
message_writer(console_color_green, false) << "\r" <<
tr("Height ") << height << ", " <<
- tr("transaction ") << get_transaction_hash(tx) << ", " <<
+ tr("transaction ") << txid << ", " <<
tr("received ") << print_money(amount);
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
@@ -1651,16 +1634,16 @@ void simple_wallet::on_money_received(uint64_t height, const cryptonote::transac
m_refresh_progress_reporter.update(height, true);
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount)
+void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount)
{
// Not implemented in CLI wallet
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx)
+void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx)
{
message_writer(console_color_magenta, false) << "\r" <<
tr("Height ") << height << ", " <<
- tr("transaction ") << get_transaction_hash(spend_tx) << ", " <<
+ tr("transaction ") << txid << ", " <<
tr("spent ") << print_money(amount);
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
@@ -1668,11 +1651,11 @@ void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transactio
m_refresh_progress_reporter.update(height, true);
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::transaction& tx)
+void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx)
{
message_writer(console_color_red, true) << "\r" <<
tr("Height ") << height << ", " <<
- tr("transaction ") << get_transaction_hash(tx) << ", " <<
+ tr("transaction ") << txid << ", " <<
tr("unsupported transaction format");
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
@@ -2091,6 +2074,18 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
std::vector<std::string> local_args = args_;
+ int priority = 0;
+ if(local_args.size() > 0) {
+ auto priority_pos = std::find(
+ allowed_priority_strings.begin(),
+ allowed_priority_strings.end(),
+ local_args[0]);
+ if(priority_pos != allowed_priority_strings.end()) {
+ local_args.erase(local_args.begin());
+ priority = std::distance(allowed_priority_strings.begin(), priority_pos);
+ }
+ }
+
size_t fake_outs_count;
if(local_args.size() > 0) {
if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0]))
@@ -2175,7 +2170,10 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
bool has_payment_id;
crypto::hash8 new_payment_id;
if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i]))
+ {
+ fail_msg_writer() << tr("failed to parse address");
return true;
+ }
if (has_payment_id)
{
@@ -2217,8 +2215,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
{
fail_msg_writer() << tr("transaction cancelled.");
- // would like to return false, because no tx made, but everything else returns true
- // and I don't know what returning false might adversely affect. *sigh*
return true;
}
}
@@ -2239,18 +2235,24 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
return true;
}
unlock_block = bc_height + locked_blocks;
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_trusted_daemon);
break;
case TransferNew:
- ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon);
break;
default:
LOG_ERROR("Unknown transfer method, using original");
case TransferOriginal:
- ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, priority, extra, m_trusted_daemon);
break;
}
+ if (ptx_vector.empty())
+ {
+ fail_msg_writer() << tr("No outputs found, or daemon is not ready");
+ return true;
+ }
+
// if more than one tx necessary, prompt user to confirm
if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
{
@@ -2306,8 +2308,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
{
fail_msg_writer() << tr("transaction cancelled.");
- // would like to return false, because no tx made, but everything else returns true
- // and I don't know what returning false might adversely affect. *sigh*
return true;
}
}
@@ -2486,8 +2486,6 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
{
fail_msg_writer() << tr("transaction cancelled.");
- // would like to return false, because no tx made, but everything else returns true
- // and I don't know what returning false might adversely affect. *sigh*
return true;
}
@@ -2603,7 +2601,7 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
+bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &args_)
{
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
if (!try_connect_to_daemon())
@@ -2670,7 +2668,10 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
crypto::hash8 new_payment_id;
cryptonote::account_public_address address;
if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0]))
+ {
+ fail_msg_writer() << tr("failed to parse address");
return true;
+ }
if (has_payment_id)
{
@@ -2701,8 +2702,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
{
fail_msg_writer() << tr("transaction cancelled.");
- // would like to return false, because no tx made, but everything else returns true
- // and I don't know what returning false might adversely affect. *sigh*
return true;
}
}
@@ -2710,11 +2709,11 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
try
{
// figure out what tx will be necessary
- auto ptx_vector = m_wallet->create_transactions_all(address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
+ auto ptx_vector = m_wallet->create_transactions_all(below, address, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra, m_trusted_daemon);
if (ptx_vector.empty())
{
- fail_msg_writer() << tr("No outputs found");
+ fail_msg_writer() << tr("No outputs found, or daemon is not ready");
return true;
}
@@ -2748,8 +2747,6 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
{
fail_msg_writer() << tr("transaction cancelled.");
- // would like to return false, because no tx made, but everything else returns true
- // and I don't know what returning false might adversely affect. *sigh*
return true;
}
@@ -2865,9 +2862,29 @@ bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
+{
+ return sweep_main(0, args_);
+}
+//----------------------------------------------------------------------------------------------------
+bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
+{
+ uint64_t below = 0;
+ if (args_.size() < 1)
+ {
+ fail_msg_writer() << tr("missing amount threshold");
+ return true;
+ }
+ if (!cryptonote::parse_amount(below, args_[0]))
+ {
+ fail_msg_writer() << tr("invalid amount threshold");
+ return true;
+ }
+ return sweep_main(below, std::vector<std::string>(++args_.begin(), args_.end()));
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::donate(const std::vector<std::string> &args_)
{
- if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
std::vector<std::string> local_args = args_;
if(local_args.empty() || local_args.size() > 3)
{
@@ -2915,6 +2932,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
size_t min_mixin = ~0;
std::unordered_map<std::string, uint64_t> dests;
const std::string wallet_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+ int first_known_non_zero_change_index = -1;
for (size_t n = 0; n < get_num_txes(); ++n)
{
const tools::wallet2::tx_construction_data &cd = get_tx(n);
@@ -2949,10 +2967,15 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes,
fail_msg_writer() << tr("Claimed change is larger than payment to the change address");
return false;
}
- if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr)))
+ if (cd.change_dts.amount > 0)
{
- fail_msg_writer() << tr("Change does to more than one address");
- return false;
+ if (first_known_non_zero_change_index == -1)
+ first_known_non_zero_change_index = n;
+ if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr)))
+ {
+ fail_msg_writer() << tr("Change goes to more than one address");
+ return false;
+ }
}
change += cd.change_dts.amount;
it->second -= cd.change_dts.amount;
@@ -3160,10 +3183,10 @@ bool simple_wallet::get_tx_key(const std::vector<std::string> &args_)
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(local_args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
{
fail_msg_writer() << tr("failed to parse txid");
- return false;
+ return true;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -3197,7 +3220,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
assert(m_wallet);
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[0], txid_data) || txid_data.size() != sizeof(crypto::hash))
{
fail_msg_writer() << tr("failed to parse txid");
return true;
@@ -3213,7 +3236,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_)
}
crypto::secret_key tx_key;
cryptonote::blobdata tx_key_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data) || tx_key_data.size() != sizeof(crypto::secret_key))
{
fail_msg_writer() << tr("failed to parse tx key");
return true;
@@ -3845,10 +3868,10 @@ bool simple_wallet::set_tx_note(const std::vector<std::string> &args)
}
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
{
fail_msg_writer() << tr("failed to parse txid");
- return false;
+ return true;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -3873,10 +3896,10 @@ bool simple_wallet::get_tx_note(const std::vector<std::string> &args)
}
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
{
fail_msg_writer() << tr("failed to parse txid");
- return false;
+ return true;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -4003,7 +4026,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
return true;
}
}
- catch (std::exception &e)
+ catch (const std::exception &e)
{
LOG_ERROR("Error exporting key images: " << e.what());
fail_msg_writer() << "Error exporting key images: " << e.what();
@@ -4173,10 +4196,10 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
}
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(args.front(), txid_data) || txid_data.size() != sizeof(crypto::hash))
{
fail_msg_writer() << tr("failed to parse txid");
- return false;
+ return true;
}
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -4328,7 +4351,8 @@ int main(int argc, char* argv[])
argc, argv,
"monero-wallet-cli [--wallet-file=<file>|--generate-new-wallet=<file>] [<COMMAND>]",
desc_params,
- positional_options
+ positional_options,
+ "monero-wallet-cli.log"
);
if (!vm)
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index ce0a24be7..1b58ff32a 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -40,11 +40,11 @@
#include <boost/optional/optional.hpp>
#include <boost/program_options/variables_map.hpp>
-#include "cryptonote_core/account.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "cryptonote_basic/account.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "wallet/wallet2.h"
#include "console_handler.h"
-#include "wallet/password_container.h"
+#include "common/password.h"
#include "crypto/crypto.h" // for definition of crypto::secret_key
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -116,6 +116,10 @@ namespace cryptonote
bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>());
bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>());
bool set_ask_password(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_unit(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_min_output_count(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_min_output_value(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_merge_destinations(const std::vector<std::string> &args = std::vector<std::string>());
bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool start_mining(const std::vector<std::string> &args);
bool stop_mining(const std::vector<std::string> &args);
@@ -130,7 +134,9 @@ namespace cryptonote
bool transfer(const std::vector<std::string> &args);
bool transfer_new(const std::vector<std::string> &args);
bool locked_transfer(const std::vector<std::string> &args);
+ bool sweep_main(uint64_t below, const std::vector<std::string> &args);
bool sweep_all(const std::vector<std::string> &args);
+ bool sweep_below(const std::vector<std::string> &args);
bool sweep_unmixable(const std::vector<std::string> &args);
bool donate(const std::vector<std::string> &args);
bool sign_transfer(const std::vector<std::string> &args);
@@ -190,10 +196,10 @@ namespace cryptonote
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
- virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount);
- virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount);
- virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx);
- virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx);
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount);
+ virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount);
+ virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx);
+ virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
//----------------------------------------------------------
friend class refresh_progress_reporter_t;
diff --git a/src/version.cmake b/src/version.cmake
index d60673ae2..623d3cf1f 100644
--- a/src/version.cmake
+++ b/src/version.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
diff --git a/src/version.h.in b/src/version.h.in
index 43e046f24..852c8023b 100644
--- a/src/version.h.in
+++ b/src/version.h.in
@@ -1,4 +1,4 @@
#define MONERO_VERSION_TAG "@VERSIONTAG@"
-#define MONERO_VERSION "0.10.1.0"
+#define MONERO_VERSION "0.10.3.1"
#define MONERO_RELEASE_NAME "Wolfram Warptangent"
#define MONERO_VERSION_FULL MONERO_VERSION "-" MONERO_VERSION_TAG
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index 922464a3c..2e7610b64 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, The Monero Project
+# Copyright (c) 2014-2017, The Monero Project
#
# All rights reserved.
#
@@ -31,7 +31,6 @@
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(wallet_sources
- password_container.cpp
wallet2.cpp
wallet_args.cpp
node_rpc_proxy.cpp
@@ -49,7 +48,6 @@ set(wallet_api_headers
set(wallet_private_headers
- password_container.h
wallet2.h
wallet_args.h
wallet_errors.h
@@ -74,6 +72,7 @@ monero_add_library(wallet
${wallet_private_headers})
target_link_libraries(wallet
PUBLIC
+ common
cryptonote_core
mnemonics
p2p
@@ -127,14 +126,19 @@ endif()
# build and install libwallet_merged only if we building for GUI
if (BUILD_GUI_DEPS)
- set(libs_to_merge wallet cryptonote_core mnemonics common crypto ringct)
+ set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common crypto ringct)
foreach(lib ${libs_to_merge})
list(APPEND objlibs $<TARGET_OBJECTS:obj_${lib}>) # matches naming convention in src/CMakeLists.txt
endforeach()
add_library(wallet_merged STATIC ${objlibs})
+ if(IOS)
+ set(lib_folder lib-${ARCH})
+ else()
+ set(lib_folder lib)
+ endif()
install(TARGETS wallet_merged
- ARCHIVE DESTINATION lib)
+ ARCHIVE DESTINATION ${lib_folder})
install(FILES ${wallet_api_headers}
DESTINATION include/wallet)
diff --git a/src/wallet/api/address_book.cpp b/src/wallet/api/address_book.cpp
index e397dac04..28f835ebd 100644
--- a/src/wallet/api/address_book.cpp
+++ b/src/wallet/api/address_book.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -180,4 +180,4 @@ AddressBookImpl::~AddressBookImpl()
} // namespace
-namespace Bitmonero = Monero; \ No newline at end of file
+namespace Bitmonero = Monero;
diff --git a/src/wallet/api/address_book.h b/src/wallet/api/address_book.h
index 5f72c5860..25f59128b 100644
--- a/src/wallet/api/address_book.h
+++ b/src/wallet/api/address_book.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp
index 760c84f4f..9798d66c6 100644
--- a/src/wallet/api/pending_transaction.cpp
+++ b/src/wallet/api/pending_transaction.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -32,9 +32,8 @@
#include "wallet.h"
#include "common_defines.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include <memory>
#include <vector>
@@ -125,7 +124,7 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite)
m_errorString = writer.str();
if (!reason.empty())
m_errorString += string(tr(". Reason: ")) + reason;
- } catch (std::exception &e) {
+ } catch (const std::exception &e) {
m_errorString = string(tr("Unknown exception: ")) + e.what();
m_status = Status_Error;
} catch (...) {
diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h
index d85a686fd..0c3e95a85 100644
--- a/src/wallet/api/pending_transaction.h
+++ b/src/wallet/api/pending_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index 1e50652c6..85f2b05ce 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_history.h b/src/wallet/api/transaction_history.h
index 37c9ea0e4..4987bdab2 100644
--- a/src/wallet/api/transaction_history.h
+++ b/src/wallet/api/transaction_history.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_info.cpp b/src/wallet/api/transaction_info.cpp
index 576ae8532..79a8fe9b5 100644
--- a/src/wallet/api/transaction_info.cpp
+++ b/src/wallet/api/transaction_info.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/transaction_info.h b/src/wallet/api/transaction_info.h
index af9696daf..16fa5da7a 100644
--- a/src/wallet/api/transaction_info.h
+++ b/src/wallet/api/transaction_info.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp
index 84ec2d9d2..1d9ef5d7c 100644
--- a/src/wallet/api/unsigned_transaction.cpp
+++ b/src/wallet/api/unsigned_transaction.cpp
@@ -32,9 +32,8 @@
#include "wallet.h"
#include "common_defines.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include <memory>
#include <vector>
@@ -105,6 +104,7 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
size_t min_mixin = ~0;
std::unordered_map<std::string, uint64_t> dests;
const std::string wallet_address = m_wallet.m_wallet->get_account().get_public_address_str(m_wallet.m_wallet->testnet());
+ int first_known_non_zero_change_index = -1;
for (size_t n = 0; n < get_num_txes(); ++n)
{
const tools::wallet2::tx_construction_data &cd = get_tx(n);
@@ -141,11 +141,16 @@ bool UnsignedTransactionImpl::checkLoadedTx(const std::function<size_t()> get_nu
m_errorString = tr("Claimed change is larger than payment to the change address");
return false;
}
- if (memcmp(&cd.change_dts.addr, &get_tx(0).change_dts.addr, sizeof(cd.change_dts.addr)))
+ if (cd.change_dts.amount > 0)
{
- m_status = Status_Error;
- m_errorString = tr("Change does to more than one address");
- return false;
+ if (first_known_non_zero_change_index == -1)
+ first_known_non_zero_change_index = n;
+ if (memcmp(&cd.change_dts.addr, &get_tx(first_known_non_zero_change_index).change_dts.addr, sizeof(cd.change_dts.addr)))
+ {
+ m_status = Status_Error;
+ m_errorString = tr("Change goes to more than one address");
+ return false;
+ }
}
change += cd.change_dts.amount;
it->second -= cd.change_dts.amount;
diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h
index 8038334e4..9c442f503 100644
--- a/src/wallet/api/unsigned_transaction.h
+++ b/src/wallet/api/unsigned_transaction.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp
index c369427b4..a9646e038 100644
--- a/src/wallet/api/utils.cpp
+++ b/src/wallet/api/utils.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 830f98909..21760ac49 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -44,6 +44,9 @@
using namespace std;
using namespace cryptonote;
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI"
+
namespace Monero {
namespace {
@@ -96,10 +99,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount)
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount)
{
- std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(tx));
+ std::string tx_hash = epee::string_tools::pod_to_hex(txid);
LOG_PRINT_L3(__FUNCTION__ << ": money received. height: " << height
<< ", tx: " << tx_hash
@@ -111,10 +114,10 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount)
+ virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, uint64_t amount)
{
- std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(tx));
+ std::string tx_hash = epee::string_tools::pod_to_hex(txid);
LOG_PRINT_L3(__FUNCTION__ << ": unconfirmed money received. height: " << height
<< ", tx: " << tx_hash
@@ -126,11 +129,11 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount,
+ virtual void on_money_spent(uint64_t height, const crypto::hash &txid, uint64_t amount,
const cryptonote::transaction& spend_tx)
{
// TODO;
- std::string tx_hash = epee::string_tools::pod_to_hex(get_transaction_hash(spend_tx));
+ std::string tx_hash = epee::string_tools::pod_to_hex(txid);
LOG_PRINT_L3(__FUNCTION__ << ": money spent. height: " << height
<< ", tx: " << tx_hash
<< ", amount: " << print_money(amount));
@@ -141,7 +144,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx)
+ virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid)
{
// TODO;
}
@@ -255,6 +258,14 @@ uint64_t Wallet::maximumAllowedAmount()
return std::numeric_limits<uint64_t>::max();
}
+void Wallet::init(const char *argv0, const char *default_log_base_name) {
+ epee::string_tools::set_module_name_and_folder(argv0);
+ mlog_configure(mlog_get_default_log_path(default_log_base_name), true);
+}
+
+void Wallet::debug(const std::string &str) {
+ MDEBUG(str);
+}
///////////////////////// WalletImpl implementation ////////////////////////
WalletImpl::WalletImpl(bool testnet)
@@ -645,9 +656,11 @@ string WalletImpl::keysFilename() const
return m_wallet->get_keys_file();
}
-bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit)
+bool WalletImpl::init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username, const std::string &daemon_password)
{
clearStatus();
+ if(daemon_username != "")
+ m_daemon_login.emplace(daemon_username, daemon_password);
return doInit(daemon_address, upper_transaction_size_limit);
}
@@ -820,7 +833,7 @@ bool WalletImpl::exportKeyImages(const string &filename)
return false;
}
}
- catch (std::exception &e)
+ catch (const std::exception &e)
{
LOG_ERROR("Error exporting key images: " << e.what());
m_errorString = e.what();
@@ -939,7 +952,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
static_cast<uint32_t>(priority),
extra, m_trustedDaemon);
} else {
- transaction->m_pending_tx = m_wallet->create_transactions_all(addr, fake_outs_count, 0 /* unlock_time */,
+ transaction->m_pending_tx = m_wallet->create_transactions_all(0, addr, fake_outs_count, 0 /* unlock_time */,
static_cast<uint32_t>(priority),
extra, m_trustedDaemon);
}
@@ -1151,7 +1164,7 @@ void WalletImpl::setDefaultMixin(uint32_t arg)
bool WalletImpl::setUserNote(const std::string &txid, const std::string &note)
{
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
return false;
const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -1162,7 +1175,7 @@ bool WalletImpl::setUserNote(const std::string &txid, const std::string &note)
std::string WalletImpl::getUserNote(const std::string &txid) const
{
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
return "";
const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
@@ -1172,7 +1185,7 @@ std::string WalletImpl::getUserNote(const std::string &txid) const
std::string WalletImpl::getTxKey(const std::string &txid) const
{
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(txid, txid_data) || txid_data.size() != sizeof(crypto::hash))
{
return "";
}
@@ -1354,7 +1367,7 @@ bool WalletImpl::isNewWallet() const
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit)
{
- if (!m_wallet->init(daemon_address, upper_transaction_size_limit))
+ if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit))
return false;
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
@@ -1400,6 +1413,18 @@ bool WalletImpl::rescanSpent()
}
return true;
}
+
+
+void WalletImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const
+{
+ m_wallet->get_hard_fork_info(version, earliest_height);
+}
+
+bool WalletImpl::useForkRules(uint8_t version, int64_t early_blocks) const
+{
+ return m_wallet->use_fork_rules(version,early_blocks);
+}
+
} // namespace
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 3994afaa3..c376dd6c1 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -78,7 +78,7 @@ public:
bool store(const std::string &path);
std::string filename() const;
std::string keysFilename() const;
- bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0);
+ bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "");
bool connectToDaemon();
ConnectionStatus connected() const;
void setTrustedDaemon(bool arg);
@@ -99,7 +99,8 @@ public:
bool watchOnly() const;
bool rescanSpent();
bool testnet() const {return m_wallet->testnet();}
-
+ void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const;
+ bool useForkRules(uint8_t version, int64_t early_blocks) const;
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count,
@@ -170,6 +171,7 @@ private:
std::atomic<bool> m_rebuildWalletCache;
// cache connection status to avoid unnecessary RPC calls
mutable std::atomic<bool> m_is_connected;
+ boost::optional<epee::net_utils::http::login> m_daemon_login{};
};
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index c761cc6d2..b2f947972 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -33,11 +33,16 @@
#include "wallet.h"
#include "common_defines.h"
#include "common/dns_utils.h"
+#include "common/util.h"
+#include "common/updates.h"
+#include "version.h"
#include "net/http_client.h"
#include <boost/filesystem.hpp>
#include <boost/regex.hpp>
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "WalletAPI"
namespace epee {
unsigned int g_test_dbg_lock_sleep = 0;
@@ -48,7 +53,7 @@ namespace {
bool connect_and_invoke(const std::string& address, const std::string& path, const Request& request, Response& response)
{
epee::net_utils::http::http_simple_client client{};
- return client.set_server(address) && epee::net_utils::invoke_http_json(path, request, response, client);
+ return client.set_server(address, boost::none) && epee::net_utils::invoke_http_json(path, request, response, client);
}
}
@@ -182,7 +187,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std:
{
error = "";
cryptonote::blobdata txid_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(txid_text, txid_data) || txid_data.size() != sizeof(crypto::hash))
{
error = tr("failed to parse txid");
return false;
@@ -196,7 +201,7 @@ bool WalletManagerImpl::checkPayment(const std::string &address_text, const std:
}
crypto::secret_key tx_key;
cryptonote::blobdata tx_key_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(txkey_text, tx_key_data) || tx_key_data.size() != sizeof(crypto::hash))
{
error = tr("failed to parse tx key");
return false;
@@ -372,26 +377,6 @@ double WalletManagerImpl::miningHashRate() const
return mres.speed;
}
-void WalletManagerImpl::hardForkInfo(uint8_t &version, uint64_t &earliest_height) const
-{
- epee::json_rpc::request<cryptonote::COMMAND_RPC_HARD_FORK_INFO::request> req_t = AUTO_VAL_INIT(req_t);
- epee::json_rpc::response<cryptonote::COMMAND_RPC_HARD_FORK_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
-
- version = 0;
- earliest_height = 0;
-
- epee::net_utils::http::http_simple_client http_client;
- req_t.jsonrpc = "2.0";
- req_t.id = epee::serialization::storage_entry(0);
- req_t.method = "hard_fork_info";
- req_t.params.version = 0;
- bool r = connect_and_invoke(m_daemonAddress, "/json_rpc", req_t, resp_t);
- if (!r || resp_t.result.status != CORE_RPC_STATUS_OK)
- return;
- version = resp_t.result.version;
- earliest_height = resp_t.result.earliest_height;
-}
-
uint64_t WalletManagerImpl::blockTarget() const
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
@@ -412,13 +397,15 @@ bool WalletManagerImpl::isMining() const
return mres.active;
}
-bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads)
+bool WalletManagerImpl::startMining(const std::string &address, uint32_t threads, bool background_mining, bool ignore_battery)
{
cryptonote::COMMAND_RPC_START_MINING::request mreq;
cryptonote::COMMAND_RPC_START_MINING::response mres;
mreq.miner_address = address;
mreq.threads_count = threads;
+ mreq.ignore_battery = ignore_battery;
+ mreq.do_background_mining = background_mining;
if (!connect_and_invoke(m_daemonAddress, "/start_mining", mreq, mres))
return false;
@@ -443,6 +430,29 @@ std::string WalletManagerImpl::resolveOpenAlias(const std::string &address, bool
return addresses.front();
}
+std::tuple<bool, std::string, std::string, std::string, std::string> WalletManager::checkUpdates(const std::string &software, const std::string &subdir)
+{
+#ifdef BUILD_TAG
+ static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG);
+#else
+ static const char buildtag[] = "source";
+#endif
+
+ std::string version, hash;
+ MDEBUG("Checking for a new " << software << " version for " << buildtag);
+ if (!tools::check_updates(software, buildtag, version, hash))
+ return std::make_tuple(false, "", "", "", "");
+
+ if (tools::vercmp(version.c_str(), MONERO_VERSION) > 0)
+ {
+ std::string user_url = tools::get_update_url(software, subdir, buildtag, version, true);
+ std::string auto_url = tools::get_update_url(software, subdir, buildtag, version, false);
+ MGINFO("Version " << version << " of " << software << " for " << buildtag << " is available: " << user_url << ", SHA256 hash " << hash);
+ return std::make_tuple(true, version, hash, user_url, auto_url);
+ }
+ return std::make_tuple(false, "", "", "", "");
+}
+
///////////////////// WalletManagerFactory implementation //////////////////////
WalletManager *WalletManagerFactory::getWalletManager()
@@ -451,7 +461,6 @@ WalletManager *WalletManagerFactory::getWalletManager()
static WalletManagerImpl * g_walletManager = nullptr;
if (!g_walletManager) {
- mlog_configure("monero-wallet-gui.log", false);
g_walletManager = new WalletManagerImpl();
}
@@ -465,7 +474,7 @@ void WalletManagerFactory::setLogLevel(int level)
void WalletManagerFactory::setLogCategories(const std::string &categories)
{
- mlog_set_categories(categories.c_str());
+ mlog_set_log(categories.c_str());
}
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index ce9b70e96..033e8108f 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -59,10 +59,9 @@ public:
uint64_t blockchainTargetHeight() const;
uint64_t networkDifficulty() const;
double miningHashRate() const;
- void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const;
uint64_t blockTarget() const;
bool isMining() const;
- bool startMining(const std::string &address, uint32_t threads = 1);
+ bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true);
bool stopMining();
std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const;
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index cc249b5cc..03e1bbd98 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -36,6 +36,8 @@ using namespace epee;
namespace tools
{
+static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
+
NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex)
: m_http_client(http_client)
, m_daemon_rpc_mutex(mutex)
@@ -45,8 +47,43 @@ NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_clien
, m_dynamic_per_kb_fee_estimate(0)
, m_dynamic_per_kb_fee_estimate_cached_height(0)
, m_dynamic_per_kb_fee_estimate_grace_blocks(0)
+ , m_rpc_version(0)
{}
+void NodeRPCProxy::invalidate()
+{
+ m_height = 0;
+ m_height_time = 0;
+ for (size_t n = 0; n < 256; ++n)
+ m_earliest_height[n] = 0;
+ m_dynamic_per_kb_fee_estimate = 0;
+ m_dynamic_per_kb_fee_estimate_cached_height = 0;
+ m_dynamic_per_kb_fee_estimate_grace_blocks = 0;
+ m_rpc_version = 0;
+}
+
+boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version)
+{
+ const time_t now = time(NULL);
+ if (m_rpc_version == 0)
+ {
+ epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_VERSION::request> req_t = AUTO_VAL_INIT(req_t);
+ epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_VERSION::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
+ req_t.jsonrpc = "2.0";
+ req_t.id = epee::serialization::storage_entry(0);
+ req_t.method = "get_version";
+ m_daemon_rpc_mutex.lock();
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
+ CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon");
+ CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, resp_t.result.status, "Failed to get daemon RPC version");
+ m_rpc_version = resp_t.result.version;
+ }
+ rpc_version = m_rpc_version;
+ return boost::optional<std::string>();
+}
+
boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height)
{
const time_t now = time(NULL);
@@ -56,7 +93,7 @@ boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height)
cryptonote::COMMAND_RPC_GET_HEIGHT::response res = AUTO_VAL_INIT(res);
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client);
+ bool r = net_utils::invoke_http_json("/getheight", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Failed to connect to daemon");
@@ -85,7 +122,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "hard_fork_info";
req_t.params.version = version;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon");
@@ -115,7 +152,7 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "get_fee_estimate";
req_t.params.grace_blocks = grace_blocks;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon");
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index e2f42d541..02d1d8d93 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -41,6 +41,9 @@ class NodeRPCProxy
public:
NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex);
+ void invalidate();
+
+ boost::optional<std::string> get_rpc_version(uint32_t &version);
boost::optional<std::string> get_height(uint64_t &height);
void set_height(uint64_t h);
boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height);
@@ -56,6 +59,7 @@ private:
uint64_t m_dynamic_per_kb_fee_estimate;
uint64_t m_dynamic_per_kb_fee_estimate_cached_height;
uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks;
+ uint32_t m_rpc_version;
};
}
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index c52281a2b..9069789ca 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -39,10 +39,10 @@ using namespace epee;
#include "cryptonote_config.h"
#include "wallet2.h"
#include "wallet2_api.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "misc_language.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "common/boost_serialization_helper.h"
#include "common/command_line.h"
#include "profile_tools.h"
@@ -82,8 +82,8 @@ using namespace cryptonote;
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\003"
#define SIGNED_TX_PREFIX "Monero signed tx set\003"
-#define RECENT_OUTPUT_RATIO (0.25) // 25% of outputs are from the recent zone
-#define RECENT_OUTPUT_ZONE (5 * 86400) // last 5 days are the recent zone
+#define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone
+#define RECENT_OUTPUT_ZONE ((time_t)(1.8 * 86400)) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al)
#define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks
@@ -105,9 +105,10 @@ namespace
struct options {
const command_line::arg_descriptor<std::string> daemon_address = {"daemon-address", tools::wallet2::tr("Use daemon instance at <host>:<port>"), ""};
const command_line::arg_descriptor<std::string> daemon_host = {"daemon-host", tools::wallet2::tr("Use daemon instance at host <arg> instead of localhost"), ""};
- const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password"), "", true};
+ const command_line::arg_descriptor<std::string> password = {"password", tools::wallet2::tr("Wallet password (escape/quote as needed)"), "", true};
const command_line::arg_descriptor<std::string> password_file = {"password-file", tools::wallet2::tr("Wallet password file"), "", true};
const command_line::arg_descriptor<int> daemon_port = {"daemon-port", tools::wallet2::tr("Use daemon instance at port <arg> instead of 18081"), 0};
+ const command_line::arg_descriptor<std::string> daemon_login = {"daemon-login", tools::wallet2::tr("Specify username[:password] for daemon RPC client"), "", true};
const command_line::arg_descriptor<bool> testnet = {"testnet", tools::wallet2::tr("For testnet. Daemon must also be launched with --testnet flag"), false};
const command_line::arg_descriptor<bool> restricted = {"restricted-rpc", tools::wallet2::tr("Restricts to view-only commands"), false};
};
@@ -152,6 +153,18 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
return nullptr;
}
+ boost::optional<epee::net_utils::http::login> login{};
+ if (command_line::has_arg(vm, opts.daemon_login))
+ {
+ auto parsed = tools::login::parse(
+ command_line::get_arg(vm, opts.daemon_login), false, "Daemon client password"
+ );
+ if (!parsed)
+ return nullptr;
+
+ login.emplace(std::move(parsed->username), std::move(parsed->password).password());
+ }
+
if (daemon_host.empty())
daemon_host = "localhost";
@@ -164,7 +177,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(testnet, restricted));
- wallet->init(std::move(daemon_address));
+ wallet->init(std::move(daemon_address), std::move(login));
return wallet;
}
@@ -200,8 +213,10 @@ boost::optional<tools::password_container> get_password(const boost::program_opt
return tools::wallet2::password_prompt(verify);
}
-std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, bool testnet, bool restricted)
+std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, const boost::program_options::variables_map& vm, const options& opts)
{
+ const bool testnet = command_line::get_arg(vm, opts.testnet);
+
/* GET_FIELD_FROM_JSON_RETURN_ON_ERROR Is a generic macro that can return
false. Gcc will coerce this into unique_ptr(nullptr), but clang correctly
fails. This large wrapper is for the use of that macro */
@@ -238,7 +253,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
if (field_viewkey_found)
{
cryptonote::blobdata viewkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(field_viewkey, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
{
tools::fail_msg_writer() << tools::wallet2::tr("failed to parse view key secret key");
return false;
@@ -256,7 +271,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
if (field_spendkey_found)
{
cryptonote::blobdata spendkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(field_spendkey, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
{
tools::fail_msg_writer() << tools::wallet2::tr("failed to parse spend key secret key");
return false;
@@ -342,7 +357,7 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file,
return false;
}
- wallet.reset(new tools::wallet2(testnet, restricted));
+ wallet.reset(make_basic(vm, opts).release());
wallet->set_refresh_from_block_height(field_scan_from_height);
try
@@ -434,14 +449,15 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.password);
command_line::add_arg(desc_params, opts.password_file);
command_line::add_arg(desc_params, opts.daemon_port);
+ command_line::add_arg(desc_params, opts.daemon_login);
command_line::add_arg(desc_params, opts.testnet);
command_line::add_arg(desc_params, opts.restricted);
}
-boost::optional<password_container> wallet2::password_prompt(const bool is_new_wallet)
+boost::optional<password_container> wallet2::password_prompt(const bool new_password)
{
auto pwd_container = tools::password_container::prompt(
- is_new_wallet, (is_new_wallet ? tr("Enter a password for your new wallet") : tr("Wallet password"))
+ new_password, (new_password ? tr("Enter new wallet password") : tr("Wallet password"))
);
if (!pwd_container)
{
@@ -453,7 +469,7 @@ boost::optional<password_container> wallet2::password_prompt(const bool is_new_w
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file)
{
const options opts{};
- return generate_from_json(json_file, command_line::get_arg(vm, opts.testnet), command_line::get_arg(vm, opts.restricted));
+ return generate_from_json(json_file, vm, opts);
}
std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_from_file(
@@ -484,14 +500,21 @@ std::pair<std::unique_ptr<wallet2>, password_container> wallet2::make_new(const
return {make_basic(vm, opts), std::move(*pwd)};
}
+std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::variables_map& vm)
+{
+ const options opts{};
+ return make_basic(vm, opts);
+}
+
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(std::string daemon_address, uint64_t upper_transaction_size_limit)
+bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit)
{
if(m_http_client.is_connected())
m_http_client.disconnect();
m_upper_transaction_size_limit = upper_transaction_size_limit;
m_daemon_address = std::move(daemon_address);
- return m_http_client.set_server(get_daemon_address());
+ m_daemon_login = std::move(daemon_login);
+ return m_http_client.set_server(get_daemon_address(), get_daemon_login());
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_deterministic() const
@@ -618,28 +641,13 @@ bool wallet2::wallet_generate_key_image_helper(const cryptonote::account_keys& a
return true;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool)
{
- class lazy_txid_getter
- {
- const cryptonote::transaction &tx;
- crypto::hash lazy_txid;
- bool computed;
- public:
- lazy_txid_getter(const transaction &tx): tx(tx), computed(false) {}
- const crypto::hash &operator()()
- {
- if (!computed)
- {
- lazy_txid = cryptonote::get_transaction_hash(tx);
- computed = true;
- }
- return lazy_txid;
- }
- } txid(tx);
+ // In this function, tx (probably) only contains the base information
+ // (that is, the prunable stuff may or may not be included)
if (!miner_tx)
- process_unconfirmed(tx, height);
+ process_unconfirmed(txid, tx, height);
std::vector<size_t> outs;
uint64_t tx_money_got_in_outs = 0;
crypto::public_key tx_pub_key = null_pkey;
@@ -648,7 +656,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
if(!parse_tx_extra(tx.extra, tx_extra_fields))
{
// Extra may only be partially parsed, it's OK if tx_extra_fields contains public key
- LOG_PRINT_L0("Transaction extra has unsupported format: " << txid());
+ LOG_PRINT_L0("Transaction extra has unsupported format: " << txid);
}
// Don't try to extract tx public key if tx has no ouputs
@@ -662,9 +670,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
{
if (pk_index > 1)
break;
- LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid());
+ LOG_PRINT_L0("Public key wasn't found in the transaction extra. Skipping transaction " << txid);
if(0 != m_callback)
- m_callback->on_skip_transaction(height, tx);
+ m_callback->on_skip_transaction(height, txid, tx);
return;
}
@@ -864,7 +872,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
- td.m_txid = txid();
+ td.m_txid = txid;
td.m_key_image = ki[o];
td.m_key_image_known = !m_watch_only;
td.m_amount = tx.vout[o].amount;
@@ -888,9 +896,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
set_unspent(m_transfers.size()-1);
m_key_images[td.m_key_image] = m_transfers.size()-1;
m_pub_keys[in_ephemeral[o].pub] = m_transfers.size()-1;
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid());
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, tx, td.m_amount);
+ m_callback->on_money_received(height, txid, tx, td.m_amount);
}
}
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount)
@@ -915,7 +923,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
td.m_internal_output_index = o;
td.m_global_output_index = o_indices[o];
td.m_tx = (const cryptonote::transaction_prefix&)tx;
- td.m_txid = txid();
+ td.m_txid = txid;
td.m_amount = tx.vout[o].amount;
td.m_pk_index = pk_index - 1;
if (td.m_amount == 0)
@@ -937,9 +945,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
THROW_WALLET_EXCEPTION_IF(td.get_public_key() != in_ephemeral[o].pub, error::wallet_internal_error, "Inconsistent public keys");
THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status");
- LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid());
+ LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, tx, td.m_amount);
+ m_callback->on_money_received(height, txid, tx, td.m_amount);
}
}
}
@@ -967,17 +975,17 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
tx_money_spent_in_ins += amount;
if (!pool)
{
- LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid());
+ LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << txid);
set_spent(it->second, height);
if (0 != m_callback)
- m_callback->on_money_spent(height, tx, amount, tx);
+ m_callback->on_money_spent(height, txid, tx, amount, tx);
}
}
}
if (tx_money_spent_in_ins > 0)
{
- process_outgoing(tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs);
+ process_outgoing(txid, tx, height, ts, tx_money_spent_in_ins, tx_money_got_in_outs);
}
uint64_t received = (tx_money_spent_in_ins < tx_money_got_in_outs) ? tx_money_got_in_outs - tx_money_spent_in_ins : 0;
@@ -1023,7 +1031,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
payment_details payment;
- payment.m_tx_hash = txid();
+ payment.m_tx_hash = txid;
payment.m_amount = received;
payment.m_block_height = height;
payment.m_unlock_time = tx.unlock_time;
@@ -1031,7 +1039,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
if (pool) {
m_unconfirmed_payments.emplace(payment_id, payment);
if (0 != m_callback)
- m_callback->on_unconfirmed_money_received(height, tx, payment.m_amount);
+ m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount);
}
else
m_payments.emplace(payment_id, payment);
@@ -1039,12 +1047,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t height)
+void wallet2::process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height)
{
if (m_unconfirmed_txs.empty())
return;
- crypto::hash txid = get_transaction_hash(tx);
auto unconf_it = m_unconfirmed_txs.find(txid);
if(unconf_it != m_unconfirmed_txs.end()) {
if (store_tx_info()) {
@@ -1060,9 +1067,8 @@ void wallet2::process_unconfirmed(const cryptonote::transaction& tx, uint64_t he
}
}
//----------------------------------------------------------------------------------------------------
-void wallet2::process_outgoing(const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received)
+void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::transaction &tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received)
{
- crypto::hash txid = get_transaction_hash(tx);
std::pair<std::unordered_map<crypto::hash, confirmed_transfer_details>::iterator, bool> entry = m_confirmed_txs.insert(std::make_pair(txid, confirmed_transfer_details()));
// fill with the info we know, some info might already be there
if (entry.second)
@@ -1105,16 +1111,19 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
{
TIME_MEASURE_START(miner_tx_handle_time);
- process_new_transaction(b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false);
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false);
TIME_MEASURE_FINISH(miner_tx_handle_time);
TIME_MEASURE_START(txs_handle_time);
- for(auto& txblob: bche.txs)
+ THROW_WALLET_EXCEPTION_IF(bche.txs.size() != b.tx_hashes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
+ size_t idx = 0;
+ for (const auto& txblob: bche.txs)
{
cryptonote::transaction tx;
- bool r = parse_and_validate_tx_from_blob(txblob, tx);
+ bool r = parse_and_validate_tx_base_from_blob(txblob, tx);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob);
- process_new_transaction(tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false);
+ process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false);
+ ++idx;
}
TIME_MEASURE_FINISH(txs_handle_time);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
@@ -1170,6 +1179,34 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
req.block_ids = short_chain_history;
+ uint32_t rpc_version;
+ boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_version(rpc_version);
+ // no error
+ if (!!result)
+ {
+ // empty string -> not connection
+ THROW_WALLET_EXCEPTION_IF(result->empty(), tools::error::no_connection_to_daemon, "getversion");
+ THROW_WALLET_EXCEPTION_IF(*result == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "getversion");
+ if (*result != CORE_RPC_STATUS_OK)
+ {
+ MDEBUG("Cannot determined daemon RPC version, not asking for pruned blocks");
+ req.prune = false; // old daemon
+ }
+ }
+ else
+ {
+ if (rpc_version >= MAKE_CORE_RPC_VERSION(1, 7))
+ {
+ MDEBUG("Daemon is recent enough, asking for pruned blocks");
+ req.prune = true;
+ }
+ else
+ {
+ MDEBUG("Daemon is too old, not asking for pruned blocks");
+ req.prune = false;
+ }
+ }
+
req.start_height = start_height;
m_daemon_rpc_mutex.lock();
bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout);
@@ -1352,25 +1389,28 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei
//----------------------------------------------------------------------------------------------------
void wallet2::update_pool_state()
{
+ MDEBUG("update_pool_state start");
+
// get the pool state
- cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::request req;
- cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL::response res;
+ cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request req;
+ cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response res;
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/get_transaction_pool", req, res, m_http_client, rpc_timeout);
+ bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool");
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin");
+ THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
+ MDEBUG("update_pool_state got pool");
// remove any pending tx that's not in the pool
std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin();
while (it != m_unconfirmed_txs.end())
{
- const std::string txid = epee::string_tools::pod_to_hex(it->first);
+ const crypto::hash &txid = it->first;
bool found = false;
- for (auto it2: res.transactions)
+ for (const auto &it2: res.tx_hashes)
{
- if (it2.id_hash == txid)
+ if (it2 == txid)
{
found = true;
break;
@@ -1415,16 +1455,17 @@ void wallet2::update_pool_state()
}
}
}
+ MDEBUG("update_pool_state done first loop");
// remove pool txes to us that aren't in the pool anymore
std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin();
while (uit != m_unconfirmed_payments.end())
{
- const std::string txid = string_tools::pod_to_hex(uit->first);
+ const crypto::hash &txid = uit->second.m_tx_hash;
bool found = false;
- for (auto it2: res.transactions)
+ for (const auto &it2: res.tx_hashes)
{
- if (it2.id_hash == txid)
+ if (it2 == txid)
{
found = true;
break;
@@ -1433,102 +1474,122 @@ void wallet2::update_pool_state()
auto pit = uit++;
if (!found)
{
+ MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool");
m_unconfirmed_payments.erase(pit);
}
}
+ MDEBUG("update_pool_state done second loop");
- // add new pool txes to us
- for (auto it: res.transactions)
+ // gather txids of new pool txes to us
+ std::vector<crypto::hash> txids;
+ for (const auto &txid: res.tx_hashes)
{
- cryptonote::blobdata txid_data;
- if(epee::string_tools::parse_hexstr_to_binbuff(it.id_hash, txid_data))
+ if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end())
+ {
+ LOG_PRINT_L2("Already seen " << txid << ", skipped");
+ continue;
+ }
+ if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end())
{
- const crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
- if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end())
+ LOG_PRINT_L1("Found new pool tx: " << txid);
+ bool found = false;
+ for (const auto &i: m_unconfirmed_txs)
{
- LOG_PRINT_L1("Found new pool tx: " << txid);
- bool found = false;
- for (const auto &i: m_unconfirmed_txs)
+ if (i.first == txid)
{
- if (i.first == txid)
- {
- found = true;
- break;
- }
+ found = true;
+ break;
}
- if (!found)
+ }
+ if (!found)
+ {
+ // not one of those we sent ourselves
+ txids.push_back(txid);
+ }
+ else
+ {
+ LOG_PRINT_L1("We sent that one");
+ }
+ }
+ else
+ {
+ LOG_PRINT_L1("Already saw that one, it's for us");
+ }
+ }
+
+ // get those txes
+ if (!txids.empty())
+ {
+ cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
+ cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
+ for (const auto &txid: txids)
+ req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
+ MDEBUG("asking for " << txids.size() << " transactions");
+ req.decode_as_json = false;
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ MDEBUG("Got " << r << " and " << res.status);
+ if (r && res.status == CORE_RPC_STATUS_OK)
+ {
+ if (res.txs.size() == txids.size())
+ {
+ size_t n = 0;
+ for (const auto &txid: txids)
{
- // not one of those we sent ourselves
- cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req;
- cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res;
- req.txs_hashes.push_back(it.id_hash);
- req.decode_as_json = false;
- m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- if (r && res.status == CORE_RPC_STATUS_OK)
+ // might have just been put in a block
+ if (res.txs[n].in_pool)
{
- if (res.txs.size() == 1)
+ cryptonote::transaction tx;
+ cryptonote::blobdata bd;
+ crypto::hash tx_hash, tx_prefix_hash;
+ if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[n].as_hex, bd))
{
- // might have just been put in a block
- if (res.txs[0].in_pool)
+ if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
{
- cryptonote::transaction tx;
- cryptonote::blobdata bd;
- crypto::hash tx_hash, tx_prefix_hash;
- if (epee::string_tools::parse_hexstr_to_binbuff(res.txs[0].as_hex, bd))
+ if (tx_hash == txid)
{
- if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash))
- {
- if (tx_hash == txid)
- {
- process_new_transaction(tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
- }
- else
- {
- LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool");
- }
- }
- else
+ process_new_transaction(txid, tx, std::vector<uint64_t>(), 0, time(NULL), false, true);
+ m_scanned_pool_txs[0].insert(txid);
+ if (m_scanned_pool_txs[0].size() > 5000)
{
- LOG_PRINT_L0("failed to validate transaction from daemon");
+ std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
+ m_scanned_pool_txs[0].clear();
}
}
else
{
- LOG_PRINT_L0("Failed to parse tx " << txid);
+ LOG_PRINT_L0("Mismatched txids when processing unconfimed txes from pool");
}
}
else
{
- LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more");
+ LOG_PRINT_L0("failed to validate transaction from daemon");
}
}
else
{
- LOG_PRINT_L0("Expected 1 tx, got " << res.txs.size());
+ LOG_PRINT_L0("Failed to parse tx " << txid);
}
}
else
{
- LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status);
+ LOG_PRINT_L1("Tx " << txid << " was in pool, but is no more");
}
- }
- else
- {
- LOG_PRINT_L1("We sent that one");
+ ++n;
}
}
else
{
- LOG_PRINT_L1("Already saw that one");
+ LOG_PRINT_L0("Expected " << txids.size() << " tx(es), got " << res.txs.size());
}
}
else
{
- LOG_PRINT_L0("Failed to parse txid");
+ LOG_PRINT_L0("Error calling gettransactions daemon RPC: r " << r << ", status " << res.status);
}
}
+ MDEBUG("update_pool_state end");
}
//----------------------------------------------------------------------------------------------------
void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history)
@@ -1865,6 +1926,18 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p
value2.SetInt(m_ask_password ? 1 :0);
json.AddMember("ask_password", value2, json.GetAllocator());
+ value2.SetUint(m_min_output_count);
+ json.AddMember("min_output_count", value2, json.GetAllocator());
+
+ value2.SetUint64(m_min_output_value);
+ json.AddMember("min_output_value", value2, json.GetAllocator());
+
+ value2.SetInt(cryptonote::get_default_decimal_point());
+ json.AddMember("default_decimal_point", value2, json.GetAllocator());
+
+ value2.SetInt(m_merge_destinations ? 1 :0);
+ json.AddMember("merge_destinations", value2, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -1933,6 +2006,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
m_refresh_type = RefreshType::RefreshDefault;
m_confirm_missing_payment_id = true;
m_ask_password = true;
+ m_min_output_count = 0;
+ m_min_output_value = 0;
+ m_merge_destinations = false;
}
else
{
@@ -1995,6 +2071,14 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa
m_confirm_missing_payment_id = field_confirm_missing_payment_id;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true);
m_ask_password = field_ask_password;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_decimal_point, int, Int, false, CRYPTONOTE_DISPLAY_DECIMAL_POINT);
+ cryptonote::set_default_decimal_point(field_default_decimal_point);
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_count, uint32_t, Uint, false, 0);
+ m_min_output_count = field_min_output_count;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, min_output_value, uint64_t, Uint64, false, 0);
+ m_min_output_value = field_min_output_value;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false);
+ m_merge_destinations = field_merge_destinations;
}
const cryptonote::account_keys& keys = m_account.get_keys();
@@ -2259,6 +2343,16 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash&
return false;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::set_default_decimal_point(unsigned int decimal_point)
+{
+ cryptonote::set_default_decimal_point(decimal_point);
+}
+//----------------------------------------------------------------------------------------------------
+unsigned int wallet2::get_default_decimal_point() const
+{
+ return cryptonote::get_default_decimal_point();
+}
+//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path)
{
do_prepare_file_names(file_path, m_keys_file, m_wallet_file);
@@ -2271,6 +2365,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout)
if(!m_http_client.is_connected())
{
+ m_node_rpc_proxy.invalidate();
if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
return false;
}
@@ -2283,7 +2378,11 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout)
req_t.id = epee::serialization::storage_entry(0);
req_t.method = "get_version";
bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
- if (!r || resp_t.result.status != CORE_RPC_STATUS_OK)
+ if(!r) {
+ *version = 0;
+ return false;
+ }
+ if (resp_t.result.status != CORE_RPC_STATUS_OK)
*version = 0;
else
*version = resp_t.result.version;
@@ -2590,28 +2689,31 @@ void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2:
//----------------------------------------------------------------------------------------------------
void wallet2::rescan_spent()
{
- std::vector<std::string> key_images;
-
- // make a list of key images for all our outputs
- for (size_t i = 0; i < m_transfers.size(); ++i)
- {
- const transfer_details& td = m_transfers[i];
- key_images.push_back(string_tools::pod_to_hex(td.m_key_image));
+ // This is RPC call that can take a long time if there are many outputs,
+ // so we call it several times, in stripes, so we don't time out spuriously
+ std::vector<int> spent_status;
+ spent_status.reserve(m_transfers.size());
+ const size_t chunk_size = 1000;
+ for (size_t start_offset = 0; start_offset < m_transfers.size(); start_offset += chunk_size)
+ {
+ const size_t n_outputs = std::min<size_t>(chunk_size, m_transfers.size() - start_offset);
+ MDEBUG("Calling is_key_image_spent on " << start_offset << " - " << (start_offset + n_outputs - 1) << ", out of " << m_transfers.size());
+ COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
+ COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
+ for (size_t n = start_offset; n < start_offset + n_outputs; ++n)
+ req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image));
+ m_daemon_rpc_mutex.lock();
+ bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != n_outputs, error::wallet_internal_error,
+ "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
+ std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(n_outputs));
+ std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
}
- COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req);
- COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
- req.key_images = key_images;
- m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status);
- THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != key_images.size(), error::wallet_internal_error,
- "daemon returned wrong response for is_key_image_spent, wrong amounts count = " +
- std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(key_images.size()));
-
// update spent status
for (size_t i = 0; i < m_transfers.size(); ++i)
{
@@ -2619,7 +2721,7 @@ void wallet2::rescan_spent()
// a view wallet may not know about key images
if (!td.m_key_image_known)
continue;
- if (td.m_spent != (daemon_resp.spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT))
+ if (td.m_spent != (spent_status[i] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT))
{
if (td.m_spent)
{
@@ -3271,20 +3373,35 @@ bool wallet2::load_tx(const std::string &signed_filename, std::vector<tools::wal
return true;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_fee_multiplier(uint32_t priority, bool use_new_fee) const
+uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
{
static const uint64_t old_multipliers[3] = {1, 2, 3};
static const uint64_t new_multipliers[3] = {1, 20, 166};
+ static const uint64_t newer_multipliers[4] = {1, 4, 20, 166};
- // 0 -> default (here, x1)
+ // 0 -> default (here, x1 till fee algorithm 2, x4 from it)
if (priority == 0)
priority = m_default_priority;
if (priority == 0)
- priority = 1;
+ {
+ if (fee_algorithm >= 2)
+ priority = 2;
+ else
+ priority = 1;
+ }
- // 1 to 3 are allowed as priorities
- if (priority >= 1 && priority <= 3)
- return (use_new_fee ? new_multipliers : old_multipliers)[priority-1];
+ // 1 to 3/4 are allowed as priorities
+ uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3;
+ if (priority >= 1 && priority <= max_priority)
+ {
+ switch (fee_algorithm)
+ {
+ case 0: return old_multipliers[priority-1];
+ case 1: return new_multipliers[priority-1];
+ case 2: return newer_multipliers[priority-1];
+ default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority);
+ }
+ }
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
return 1;
@@ -3309,17 +3426,26 @@ uint64_t wallet2::get_per_kb_fee()
return get_dynamic_per_kb_fee_estimate();
}
//----------------------------------------------------------------------------------------------------
+int wallet2::get_fee_algorithm()
+{
+ // changes at v3 and v5
+ if (use_fork_rules(5, 0))
+ return 2;
+ if (use_fork_rules(3, -720 * 14))
+ return 1;
+ return 0;
+}
+//----------------------------------------------------------------------------------------------------
// separated the call(s) to wallet2::transfer into their own function
//
// this function will make multiple calls to wallet2::transfer if multiple
// transactions will be required
std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon)
{
- const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, trusted_daemon);
+ const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true, trusted_daemon);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
+ const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
// failsafe split attempt counter
size_t attempt_count = 0;
@@ -3438,7 +3564,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
req_t.params.amounts.resize(std::distance(req_t.params.amounts.begin(), end));
req_t.params.unlocked = true;
req_t.params.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -3525,7 +3651,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
uint64_t i;
if (num_found - 1 < recent_outputs_count) // -1 to account for the real one we seeded with
{
- // equiprobable distribution over the recent outs
+ // triangular distribution over [a,b) with a=0, mode c=b=up_index_limit
uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53);
double frac = std::sqrt((double)r / ((uint64_t)1 << 53));
i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs;
@@ -3984,6 +4110,14 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
return size;
}
+static size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs)
+{
+ if (use_rct)
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1);
+ else
+ return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES;
+}
+
std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) const
{
std::vector<size_t> picks;
@@ -4044,7 +4178,7 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) co
return picks;
}
-static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices)
+bool wallet2::should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const
{
if (!use_rct)
return false;
@@ -4052,9 +4186,52 @@ static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const
return false;
if (unused_dust_indices.empty() && unused_transfers_indices.empty())
return false;
+ // we want at least one free rct output to avoid a corner case where
+ // we'd choose a non rct output which doesn't have enough "siblings"
+ // value-wise on the chain, and thus can't be mixed
+ bool found = false;
+ for (auto i: unused_dust_indices)
+ {
+ if (m_transfers[i].is_rct())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found) for (auto i: unused_transfers_indices)
+ {
+ if (m_transfers[i].is_rct())
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return false;
return true;
}
+std::vector<size_t> wallet2::get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const
+{
+ std::vector<size_t> indices;
+ for (size_t n: unused_dust_indices)
+ if (m_transfers[n].is_rct())
+ indices.push_back(n);
+ for (size_t n: unused_transfers_indices)
+ if (m_transfers[n].is_rct())
+ indices.push_back(n);
+ return indices;
+}
+
+static uint32_t get_count_above(const std::vector<wallet2::transfer_details> &transfers, const std::vector<size_t> &indices, uint64_t threshold)
+{
+ uint32_t count = 0;
+ for (size_t idx: indices)
+ if (transfers[idx].amount() >= threshold)
+ ++count;
+ return count;
+}
+
// Another implementation of transaction creation that is hopefully better
// While there is anything left to pay, it goes through random outputs and tries
// to fill the next destination/amount. If it fully fills it, it will use the
@@ -4083,13 +4260,23 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
pending_tx ptx;
size_t bytes;
- void add(const account_public_address &addr, uint64_t amount) {
- std::vector<cryptonote::tx_destination_entry>::iterator i;
- i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); });
- if (i == dsts.end())
- dsts.push_back(tx_destination_entry(amount,addr));
- else
+ void add(const account_public_address &addr, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
+ if (merge_destinations)
+ {
+ std::vector<cryptonote::tx_destination_entry>::iterator i;
+ i = std::find_if(dsts.begin(), dsts.end(), [&](const cryptonote::tx_destination_entry &d) { return !memcmp (&d.addr, &addr, sizeof(addr)); });
+ if (i == dsts.end())
+ dsts.push_back(tx_destination_entry(0,addr));
i->amount += amount;
+ }
+ else
+ {
+ THROW_WALLET_EXCEPTION_IF(original_output_index > dsts.size(), error::wallet_internal_error, "original_output_index too large");
+ if (original_output_index == dsts.size())
+ dsts.push_back(tx_destination_entry(0,addr));
+ THROW_WALLET_EXCEPTION_IF(memcmp(&dsts[original_output_index].addr, &addr, sizeof(addr)), error::wallet_internal_error, "Mismatched destination address");
+ dsts[original_output_index].amount += amount;
+ }
}
};
std::vector<TX> txes;
@@ -4098,9 +4285,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
uint64_t upper_transaction_size_limit = get_upper_tranaction_size_limit();
const bool use_rct = use_fork_rules(4, 0);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
+ const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
@@ -4120,8 +4306,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
THROW_WALLET_EXCEPTION_IF(needed_money == 0, error::zero_destination);
// gather all our dust and non dust outputs
- const std::vector<size_t> unused_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, trusted_daemon);
- for (size_t i: unused_indices)
+ for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
@@ -4180,6 +4365,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// - we have something to send
// - or we need to gather more fee
// - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2)
+ unsigned int original_output_index = 0;
while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), unused_transfers_indices, unused_dust_indices)) {
TX &tx = txes.back();
@@ -4202,22 +4388,33 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// get a random unspent output and use it to pay part (or all) of the current destination (and maybe next one, etc)
// This could be more clever, but maybe at the cost of making probabilistic inferences easier
size_t idx;
- if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee)
+ if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) {
// the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too
- idx = pop_best_value(unused_dust_indices.empty() ? unused_transfers_indices : unused_dust_indices, tx.selected_transfers, true);
- else if (!prefered_inputs.empty()) {
- idx = pop_back(prefered_inputs);
- pop_if_present(unused_transfers_indices, idx);
- pop_if_present(unused_dust_indices, idx);
+ std::vector<size_t> indices = get_only_rct(unused_dust_indices, unused_transfers_indices);
+ idx = pop_best_value(indices, tx.selected_transfers, true);
+
+ // we might not want to add it if it's a large output and we don't have many left
+ if (m_transfers[idx].amount() >= m_min_output_value) {
+ if (get_count_above(m_transfers, unused_transfers_indices, m_min_output_value) < m_min_output_count) {
+ LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_money(m_min_output_value) << ", not adding");
+ break;
+ }
+ }
// since we're trying to add a second output which is not strictly needed,
// we only add it if it's unrelated enough to the first one
float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]);
if (relatedness > SECOND_OUTPUT_RELATEDNESS_THRESHOLD)
{
- LOG_PRINT_L2("Second outout was not strictly needed, and relatedness " << relatedness << ", not adding");
+ LOG_PRINT_L2("Second output was not strictly needed, and relatedness " << relatedness << ", not adding");
break;
}
+ pop_if_present(unused_transfers_indices, idx);
+ pop_if_present(unused_dust_indices, idx);
+ } else if (!prefered_inputs.empty()) {
+ idx = pop_back(prefered_inputs);
+ pop_if_present(unused_transfers_indices, idx);
+ pop_if_present(unused_dust_indices, idx);
} else
idx = pop_best_value(unused_transfers_indices.empty() ? unused_dust_indices : unused_transfers_indices, tx.selected_transfers);
@@ -4239,22 +4436,23 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount)
+ while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit))
{
// we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].addr) <<
" for " << print_money(dsts[0].amount));
- tx.add(dsts[0].addr, dsts[0].amount);
+ tx.add(dsts[0].addr, dsts[0].amount, original_output_index, m_merge_destinations);
available_amount -= dsts[0].amount;
dsts[0].amount = 0;
pop_index(dsts, 0);
+ ++original_output_index;
}
- if (available_amount > 0 && !dsts.empty()) {
+ if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) {
// we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
- tx.add(dsts[0].addr, available_amount);
+ tx.add(dsts[0].addr, available_amount, original_output_index, m_merge_destinations);
dsts[0].amount -= available_amount;
available_amount = 0;
}
@@ -4271,11 +4469,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- size_t estimated_rct_tx_size;
- if (use_rct)
- estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
- else
- estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES;
+ const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size());
try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
}
@@ -4387,7 +4581,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
return ptx_vector;
}
-std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon)
+std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon)
{
std::vector<size_t> unused_transfers_indices;
std::vector<size_t> unused_dust_indices;
@@ -4399,10 +4593,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(const cryptono
const transfer_details& td = m_transfers[i];
if (!td.m_spent && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td))
{
- if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
- unused_transfers_indices.push_back(i);
- else
- unused_dust_indices.push_back(i);
+ if (below == 0 || td.amount() < below)
+ {
+ if (td.is_rct() || is_valid_decomposed_amount(td.amount()))
+ unused_transfers_indices.push_back(i);
+ else
+ unused_dust_indices.push_back(i);
+ }
}
}
@@ -4425,9 +4622,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<std::vector<get_outs_entry>> outs;
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
- const uint64_t fee_multiplier = get_fee_multiplier(priority, use_new_fee);
+ const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
@@ -4464,11 +4660,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit);
- size_t estimated_rct_tx_size;
- if (use_rct)
- estimated_rct_tx_size = estimate_rct_tx_size(tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
- else
- estimated_rct_tx_size = tx.selected_transfers.size() * (fake_outs_count+1) * APPROXIMATE_INPUT_BYTES;
+ const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
if (try_tx) {
@@ -4588,7 +4780,7 @@ uint64_t wallet2::get_upper_tranaction_size_limit()
{
if (m_upper_transaction_size_limit > 0)
return m_upper_transaction_size_limit;
- uint64_t full_reward_zone = use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
+ uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
//----------------------------------------------------------------------------------------------------
@@ -4614,7 +4806,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
for (const auto &td: m_transfers)
{
if (!td.m_spent)
- set.insert(td.amount());
+ set.insert(td.is_rct() ? 0 : td.amount());
}
std::vector<uint64_t> vector;
vector.reserve(set.size());
@@ -4625,7 +4817,7 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
return vector;
}
//----------------------------------------------------------------------------------------------------
-std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool trusted_daemon)
+std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon)
{
epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t);
epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response, std::string> resp_t = AUTO_VAL_INIT(resp_t);
@@ -4638,9 +4830,9 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
req_t.params.min_count = count;
req_t.params.max_count = 0;
req_t.params.unlocked = unlocked;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_unmixable_outputs");
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status);
@@ -4650,10 +4842,10 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
mixable.insert(i.amount);
}
- return select_available_outputs([mixable, atleast](const transfer_details &td) {
- if (td.is_rct())
+ return select_available_outputs([mixable, atleast, allow_rct](const transfer_details &td) {
+ if (!allow_rct && td.is_rct())
return false;
- const uint64_t amount = td.amount();
+ const uint64_t amount = td.is_rct() ? 0 : td.amount();
if (atleast) {
if (mixable.find(amount) != mixable.end())
return true;
@@ -4677,7 +4869,7 @@ uint64_t wallet2::get_num_rct_outputs()
req_t.params.amounts.push_back(0);
req_t.params.min_count = 0;
req_t.params.max_count = 0;
- bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client);
+ bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs");
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -4698,14 +4890,14 @@ std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_dae
{
// request all outputs with less than 3 instances
const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4
- return select_available_outputs_from_histogram(min_mixin + 1, false, true, trusted_daemon);
+ return select_available_outputs_from_histogram(min_mixin + 1, false, true, false, trusted_daemon);
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemon)
{
// request all outputs with at least 3 instances, so we can use mixin 2 with
const size_t min_mixin = use_fork_rules(5, 10) ? 4 : 2; // v5 increases min mixin from 2 to 4
- return select_available_outputs_from_histogram(min_mixin + 1, true, true, trusted_daemon);
+ return select_available_outputs_from_histogram(min_mixin + 1, true, true, true, trusted_daemon);
}
//----------------------------------------------------------------------------------------------------
std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bool trusted_daemon)
@@ -4714,7 +4906,6 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bo
const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
- const bool use_new_fee = use_fork_rules(3, -720 * 14);
const uint64_t fee_per_kb = get_per_kb_fee();
// may throw
@@ -5409,7 +5600,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
height_mid,
height_max
};
- bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client);
+ bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client, rpc_timeout);
if (!r || res.status != CORE_RPC_STATUS_OK)
{
std::ostringstream oss;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 567292d30..03c6a431b 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -39,13 +39,14 @@
#include <atomic>
#include "include_base_utils.h"
-#include "cryptonote_core/account.h"
-#include "cryptonote_core/account_boost_serialization.h"
-#include "cryptonote_core/cryptonote_basic_impl.h"
+#include "cryptonote_basic/account.h"
+#include "cryptonote_basic/account_boost_serialization.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "net/http_client.h"
#include "storages/http_abstract_invoke.h"
#include "rpc/core_rpc_server_commands_defs.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_core/cryptonote_tx_utils.h"
#include "common/unordered_containers_boost_serialization.h"
#include "crypto/chacha8.h"
#include "crypto/hash.h"
@@ -53,7 +54,7 @@
#include "ringct/rctOps.h"
#include "wallet_errors.h"
-#include "password_container.h"
+#include "common/password.h"
#include "node_rpc_proxy.h"
#include <iostream>
@@ -69,10 +70,10 @@ namespace tools
{
public:
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
- virtual void on_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {}
- virtual void on_unconfirmed_money_received(uint64_t height, const cryptonote::transaction& tx, uint64_t amount) {}
- virtual void on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {}
- virtual void on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) {}
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {}
+ virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount) {}
+ virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx) {}
+ virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
virtual ~i_wallet2_callback() {}
};
@@ -104,7 +105,7 @@ namespace tools
};
private:
- wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+ wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
public:
static const char* tr(const char* str);
@@ -113,7 +114,7 @@ namespace tools
static void init_options(boost::program_options::options_description& desc_params);
//! \return Password retrieved from prompt. Logs error on failure.
- static boost::optional<password_container> password_prompt(const bool is_new_wallet);
+ static boost::optional<password_container> password_prompt(const bool new_password);
//! Uses stdin and stdout. Returns a wallet2 if no errors.
static std::unique_ptr<wallet2> make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file);
@@ -125,7 +126,11 @@ namespace tools
//! Uses stdin and stdout. Returns a wallet2 and password for wallet with no file if no errors.
static std::pair<std::unique_ptr<wallet2>, password_container> make_new(const boost::program_options::variables_map& vm);
- wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+ //! Just parses variables.
+ static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm);
+
+ wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {}
+
struct transfer_details
{
uint64_t m_block_height;
@@ -343,7 +348,8 @@ namespace tools
// into account the current median block size rather than
// the minimum block size.
bool deinit();
- bool init(std::string daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = 0);
+ bool init(std::string daemon_address = "http://localhost:8080",
+ boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0);
void stop() { m_run.store(false, std::memory_order_relaxed); }
@@ -408,7 +414,7 @@ namespace tools
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
- std::vector<wallet2::pending_tx> create_transactions_all(const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
+ std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, const cryptonote::account_public_address &address, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<wallet2::pending_tx> create_transactions_from(const cryptonote::account_public_address &address, std::vector<size_t> unused_transfers_indices, std::vector<size_t> unused_dust_indices, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t> extra, bool trusted_daemon);
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
bool check_connection(uint32_t *version = NULL, uint32_t timeout = 200000);
@@ -454,7 +460,14 @@ namespace tools
a & m_tx_notes;
if(ver < 13)
return;
- a & m_unconfirmed_payments;
+ if (ver < 17)
+ {
+ // we're loading an old version, where m_unconfirmed_payments was a std::map
+ std::unordered_map<crypto::hash, payment_details> m;
+ a & m;
+ for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i)
+ m_unconfirmed_payments.insert(*i);
+ }
if(ver < 14)
return;
if(ver < 15)
@@ -473,6 +486,13 @@ namespace tools
if(ver < 16)
return;
a & m_address_book;
+ if(ver < 17)
+ return;
+ a & m_unconfirmed_payments;
+ if(ver < 18)
+ return;
+ a & m_scanned_pool_txs[0];
+ a & m_scanned_pool_txs[1];
}
/*!
@@ -508,6 +528,14 @@ namespace tools
void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; }
bool ask_password() const { return m_ask_password; }
void ask_password(bool always) { m_ask_password = always; }
+ void set_default_decimal_point(unsigned int decimal_point);
+ unsigned int get_default_decimal_point() const;
+ void set_min_output_count(uint32_t count) { m_min_output_count = count; }
+ uint32_t get_min_output_count() const { return m_min_output_count; }
+ void set_min_output_value(uint64_t value) { m_min_output_value = value; }
+ uint64_t get_min_output_value() const { return m_min_output_value; }
+ void merge_destinations(bool merge) { m_merge_destinations = merge; }
+ bool merge_destinations() const { return m_merge_destinations; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const;
@@ -523,17 +551,19 @@ namespace tools
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
bool use_fork_rules(uint8_t version, int64_t early_blocks = 0);
+ int get_fee_algorithm();
std::string get_wallet_file() const;
std::string get_keys_file() const;
std::string get_daemon_address() const;
+ const boost::optional<epee::net_utils::http::login>& get_daemon_login() const { return m_daemon_login; }
uint64_t get_daemon_blockchain_height(std::string& err);
uint64_t get_daemon_blockchain_target_height(std::string& err);
/*!
* \brief Calculates the approximate blockchain height from current date/time.
*/
uint64_t get_approximate_blockchain_height() const;
- std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool trusted_daemon);
+ std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon);
std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
std::vector<size_t> select_available_mixable_outputs(bool trusted_daemon);
@@ -582,7 +612,7 @@ namespace tools
* \param password Password of wallet file
*/
bool load_keys(const std::string& keys_file_name, const std::string& password);
- void process_new_transaction(const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool);
+ void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool);
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices);
void detach_blockchain(uint64_t height);
void get_short_chain_history(std::list<crypto::hash>& ids) const;
@@ -595,8 +625,8 @@ namespace tools
void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, const std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t& blocks_added);
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<size_t>& selected_transfers, bool trusted_daemon);
bool prepare_file_names(const std::string& file_path);
- void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height);
- void process_outgoing(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received);
+ void process_unconfirmed(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height);
+ void process_outgoing(const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t height, uint64_t ts, uint64_t spent, uint64_t received);
void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector<cryptonote::tx_destination_entry> &dests, const crypto::hash &payment_id, uint64_t change_amount);
void generate_genesis(cryptonote::block& b);
void check_genesis(const crypto::hash& genesis_hash) const; //throws
@@ -607,7 +637,7 @@ namespace tools
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_tranaction_size_limit();
std::vector<uint64_t> get_unspent_amounts_vector();
- uint64_t get_fee_multiplier(uint32_t priority, bool use_new_fee) const;
+ uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm) const;
uint64_t get_dynamic_per_kb_fee_estimate();
uint64_t get_per_kb_fee();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
@@ -617,8 +647,11 @@ namespace tools
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::list<size_t> &selected_transfers, size_t fake_outputs_count);
bool wallet_generate_key_image_helper(const cryptonote::account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
+ bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
+ std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
cryptonote::account_base m_account;
+ boost::optional<epee::net_utils::http::login> m_daemon_login;
std::string m_daemon_address;
std::string m_wallet_file;
std::string m_keys_file;
@@ -627,7 +660,7 @@ namespace tools
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs;
std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs;
- std::unordered_map<crypto::hash, payment_details> m_unconfirmed_payments;
+ std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments;
std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys;
transfer_container m_transfers;
@@ -659,10 +692,14 @@ namespace tools
uint64_t m_refresh_from_block_height;
bool m_confirm_missing_payment_id;
bool m_ask_password;
+ uint32_t m_min_output_count;
+ uint64_t m_min_output_value;
+ bool m_merge_destinations;
NodeRPCProxy m_node_rpc_proxy;
+ std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 16)
+BOOST_CLASS_VERSION(tools::wallet2, 18)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6)
diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h
index 883da7da2..17d0caf7d 100644
--- a/src/wallet/wallet2_api.h
+++ b/src/wallet/wallet2_api.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -296,7 +296,10 @@ struct Wallet
virtual std::string address() const = 0;
virtual std::string path() const = 0;
virtual bool testnet() const = 0;
-
+ //! returns current hard fork info
+ virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
+ //! check if hard fork rules should be used
+ virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
/*!
* \brief integratedAddress - returns integrated address for current wallet address and given payment_id.
* if passed "payment_id" param is an empty string or not-valid payment id string
@@ -340,7 +343,7 @@ struct Wallet
* \param upper_transaction_size_limit
* \return - true on success
*/
- virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit) = 0;
+ virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username = "", const std::string &daemon_password = "") = 0;
/*!
* \brief createWatchOnly - Creates a watch only wallet
@@ -428,6 +431,9 @@ struct Wallet
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);
static std::string paymentIdFromAddress(const std::string &str, bool testnet);
static uint64_t maximumAllowedAmount();
+ // Easylogger wrapper
+ static void init(const char *argv0, const char *default_log_base_name);
+ static void debug(const std::string &str);
/**
* @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds)
@@ -683,9 +689,6 @@ struct WalletManager
//! returns current mining hash rate (0 if not mining)
virtual double miningHashRate() const = 0;
- //! returns current hard fork info
- virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
-
//! returns current block target
virtual uint64_t blockTarget() const = 0;
@@ -693,13 +696,16 @@ struct WalletManager
virtual bool isMining() const = 0;
//! starts mining with the set number of threads
- virtual bool startMining(const std::string &address, uint32_t threads = 1) = 0;
+ virtual bool startMining(const std::string &address, uint32_t threads = 1, bool background_mining = false, bool ignore_battery = true) = 0;
//! stops mining
virtual bool stopMining() = 0;
//! resolves an OpenAlias address to a monero address
virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0;
+
+ //! checks for an update and returns version, hash and url
+ static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir);
};
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 12799f613..1508f3791 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -73,6 +73,7 @@ namespace wallet_args
const char* const usage,
boost::program_options::options_description desc_params,
const boost::program_options::positional_options_description& positional_options,
+ const char *default_log_name,
bool log_to_console)
{
@@ -85,6 +86,7 @@ namespace wallet_args
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
const command_line::arg_descriptor<uint32_t> arg_max_concurrency = {"max-concurrency", wallet_args::tr("Max number of threads to use for a parallel job"), DEFAULT_MAX_CONCURRENCY};
const command_line::arg_descriptor<std::string> arg_log_file = {"log-file", wallet_args::tr("Specify log file"), ""};
+ const command_line::arg_descriptor<std::string> arg_config_file = {"config-file", wallet_args::tr("Config file"), "", true};
std::string lang = i18n_get_language();
@@ -100,6 +102,7 @@ namespace wallet_args
command_line::add_arg(desc_params, arg_log_file, "");
command_line::add_arg(desc_params, arg_log_level);
command_line::add_arg(desc_params, arg_max_concurrency);
+ command_line::add_arg(desc_params, arg_config_file);
i18n_set_language("translations", "monero", lang);
@@ -108,43 +111,58 @@ namespace wallet_args
po::variables_map vm;
bool r = command_line::handle_error_helper(desc_all, [&]()
{
- po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm);
+ auto parser = po::command_line_parser(argc, argv).options(desc_all).positional(positional_options);
+ po::store(parser.run(), vm);
- if (command_line::get_arg(vm, command_line::arg_help))
- {
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
- tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage;
- tools::msg_writer() << desc_all;
- return false;
- }
- else if (command_line::get_arg(vm, command_line::arg_version))
+ if(command_line::has_arg(vm, arg_config_file))
{
- tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
- return false;
+ std::string config = command_line::get_arg(vm, arg_config_file);
+ bf::path config_path(config);
+ boost::system::error_code ec;
+ if (bf::exists(config_path, ec))
+ {
+ po::store(po::parse_config_file<char>(config_path.string<std::string>().c_str(), desc_params), vm);
+ }
+ else
+ {
+ tools::fail_msg_writer() << wallet_args::tr("Can't find config file ") << config;
+ return false;
+ }
}
- auto parser = po::command_line_parser(argc, argv).options(desc_params).positional(positional_options);
- po::store(parser.run(), vm);
po::notify(vm);
return true;
});
if (!r)
return boost::none;
- if(command_line::has_arg(vm, arg_max_concurrency))
- tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
-
std::string log_path;
if (!vm["log-file"].defaulted())
log_path = command_line::get_arg(vm, arg_log_file);
else
- log_path = mlog_get_default_log_path("monero-wallet-cli.log");
+ log_path = mlog_get_default_log_path(default_log_name);
mlog_configure(log_path, log_to_console);
if (!vm["log-level"].defaulted())
{
mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str());
}
+ if (command_line::get_arg(vm, command_line::arg_help))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage;
+ tools::msg_writer() << desc_all;
+ return boost::none;
+ }
+ else if (command_line::get_arg(vm, command_line::arg_version))
+ {
+ tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
+ return boost::none;
+ }
+
+ if(command_line::has_arg(vm, arg_max_concurrency))
+ tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency));
+
tools::scoped_message_writer(epee::console_color_white, true) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")";
if (!vm["log-level"].defaulted())
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index e0719d203..cf23ffded 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -50,5 +50,5 @@ namespace wallet_args
const char* const usage,
boost::program_options::options_description desc_params,
const boost::program_options::positional_options_description& positional_options,
- bool log_to_console = false);
+ const char *default_log_name, bool log_to_console = false);
}
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 785a72e4b..3e3578149 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -35,7 +35,7 @@
#include <string>
#include <vector>
-#include "cryptonote_core/cryptonote_format_utils.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "include_base_utils.h"
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 22f5f8bb6..f2b3dcaf5 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -38,13 +38,16 @@ using namespace epee;
#include "common/command_line.h"
#include "common/i18n.h"
#include "common/util.h"
-#include "cryptonote_core/cryptonote_format_utils.h"
-#include "cryptonote_core/account.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_basic/account.h"
#include "wallet_rpc_server_commands_defs.h"
#include "misc_language.h"
#include "string_coding.h"
#include "string_tools.h"
#include "crypto/hash.h"
+#include "mnemonics/electrum-words.h"
+#include "rpc/rpc_args.h"
+#include "rpc/core_rpc_server_commands_defs.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
@@ -52,10 +55,9 @@ using namespace epee;
namespace
{
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
- const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"};
- const command_line::arg_descriptor<std::string> arg_rpc_login = {"rpc-login", "Specify username[:password] required for RPC connection"};
- const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC"};
- const command_line::arg_descriptor<bool> arg_confirm_external_bind = {"confirm-external-bind", "Confirm rcp-bind-ip value is NOT a loopback (local) IP"};
+ const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC connections served by this process"};
+ const command_line::arg_descriptor<bool> arg_trusted_daemon = {"trusted-daemon", "Enable commands which rely on a trusted daemon", false};
+ const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
constexpr const char default_rpc_username[] = "monero";
}
@@ -68,8 +70,9 @@ namespace tools
}
//------------------------------------------------------------------------------------------------------------------------------
- wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w), rpc_login_filename(), m_stop(false)
- {}
+ wallet_rpc_server::wallet_rpc_server():m_wallet(NULL), rpc_login_filename(), m_stop(false), m_trusted_daemon(false)
+ {
+ }
//------------------------------------------------------------------------------------------------------------------------------
wallet_rpc_server::~wallet_rpc_server()
{
@@ -81,12 +84,17 @@ namespace tools
catch (...) {}
}
//------------------------------------------------------------------------------------------------------------------------------
+ void wallet_rpc_server::set_wallet(wallet2 *cr)
+ {
+ m_wallet = cr;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::run()
{
m_stop = false;
m_net_server.add_idle_handler([this](){
try {
- m_wallet.refresh();
+ if (m_wallet) m_wallet->refresh();
} catch (const std::exception& ex) {
LOG_ERROR("Exception at while refreshing, what=" << ex.what());
}
@@ -105,77 +113,83 @@ namespace tools
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::run(1, true);
}
//------------------------------------------------------------------------------------------------------------------------------
- bool wallet_rpc_server::init(const boost::program_options::variables_map& vm)
+ void wallet_rpc_server::stop()
{
- std::string bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip);
- if (!bind_ip.empty())
+ if (m_wallet)
{
- // always parse IP here for error consistency
- boost::system::error_code ec{};
- const auto parsed_ip = boost::asio::ip::address::from_string(bind_ip, ec);
- if (ec)
- {
- LOG_ERROR(tr("Invalid IP address given for rpc-bind-ip argument"));
- return false;
- }
+ m_wallet->store();
+ delete m_wallet;
+ m_wallet = NULL;
+ }
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::init(const boost::program_options::variables_map *vm)
+ {
+ auto rpc_config = cryptonote::rpc_args::process(*vm);
+ if (!rpc_config)
+ return false;
+
+ m_vm = vm;
+ tools::wallet2 *walvars;
+ std::unique_ptr<tools::wallet2> tmpwal;
- if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg_confirm_external_bind))
+ if (m_wallet)
+ walvars = m_wallet;
+ else
+ {
+ tmpwal = tools::wallet2::make_dummy(*m_vm);
+ walvars = tmpwal.get();
+ }
+ boost::optional<epee::net_utils::http::login> http_login{};
+ std::string bind_port = command_line::get_arg(*m_vm, arg_rpc_bind_port);
+ const bool disable_auth = command_line::get_arg(*m_vm, arg_disable_rpc_login);
+ m_trusted_daemon = command_line::get_arg(*m_vm, arg_trusted_daemon);
+ if (!command_line::has_arg(*m_vm, arg_trusted_daemon))
+ {
+ if (tools::is_local_address(walvars->get_daemon_address()))
{
- LOG_ERROR(
- tr("The rpc-bind-ip value is listening for unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --confirm-external-bind")
- );
- return false;
+ MINFO(tr("Daemon is local, assuming trusted"));
+ m_trusted_daemon = true;
}
}
-
- epee::net_utils::http::login login{};
-
- const bool disable_auth = command_line::get_arg(vm, arg_disable_rpc_login);
- const std::string user_pass = command_line::get_arg(vm, arg_rpc_login);
- const std::string bind_port = command_line::get_arg(vm, arg_rpc_bind_port);
+ if (command_line::has_arg(*m_vm, arg_wallet_dir))
+ {
+ m_wallet_dir = command_line::get_arg(*m_vm, arg_wallet_dir);
+#ifdef _WIN32
+#define MKDIR(path, mode) mkdir(path)
+#else
+#define MKDIR(path, mode) mkdir(path, mode)
+#endif
+ MKDIR(m_wallet_dir.c_str(), 0700);
+ }
if (disable_auth)
{
- if (!user_pass.empty())
+ if (rpc_config->login)
{
- LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg_rpc_login.name);
+ const cryptonote::rpc_args::descriptors arg{};
+ LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg.rpc_login.name);
return false;
}
}
else // auth enabled
{
- if (user_pass.empty())
+ if (!rpc_config->login)
{
- login.username = default_rpc_username;
-
std::array<std::uint8_t, 16> rand_128bit{{}};
crypto::rand(rand_128bit.size(), rand_128bit.data());
- login.password = string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size());
+ http_login.emplace(
+ default_rpc_username,
+ string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size())
+ );
}
- else // user password
+ else
{
- const auto loc = user_pass.find(':');
- login.username = user_pass.substr(0, loc);
- if (loc != std::string::npos)
- {
- login.password = user_pass.substr(loc + 1);
- }
- else
- {
- login.password = tools::password_container::prompt(true, "RPC password").value_or(
- tools::password_container{}
- ).password();
- }
-
- if (login.username.empty() || login.password.empty())
- {
- LOG_ERROR(tr("Blank username or password not permitted for RPC authenticaion"));
- return false;
- }
+ http_login.emplace(
+ std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()
+ );
}
-
- assert(!login.username.empty());
- assert(!login.password.empty());
+ assert(bool(http_login));
std::string temp = "monero-wallet-rpc." + bind_port + ".login";
const auto cookie = tools::create_private_file(temp);
@@ -186,9 +200,9 @@ namespace tools
}
rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup
temp = rpc_login_filename;
- std::fputs(login.username.c_str(), cookie.get());
+ std::fputs(http_login->username.c_str(), cookie.get());
std::fputc(':', cookie.get());
- std::fputs(login.password.c_str(), cookie.get());
+ std::fputs(http_login->password.c_str(), cookie.get());
std::fflush(cookie.get());
if (std::ferror(cookie.get()))
{
@@ -198,12 +212,21 @@ namespace tools
LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
} // end auth enabled
+ m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login());
+
m_net_server.set_threads_prefix("RPC");
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- std::move(bind_port), std::move(bind_ip), std::string{}, boost::make_optional(!disable_auth, std::move(login))
+ std::move(bind_port), std::move(rpc_config->bind_ip), std::move(http_login)
);
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::not_open(epee::json_rpc::error& er)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_NOT_OPEN;
+ er.message = "No wallet file";
+ return false;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd)
{
entry.txid = string_tools::pod_to_hex(pd.m_tx_hash);
@@ -214,7 +237,7 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.fee = 0; // TODO
- entry.note = m_wallet.get_tx_note(pd.m_tx_hash);
+ entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "in";
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -229,13 +252,13 @@ namespace tools
entry.fee = pd.m_amount_in - pd.m_amount_out;
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
entry.amount = pd.m_amount_in - change - entry.fee;
- entry.note = m_wallet.get_tx_note(txid);
+ entry.note = m_wallet->get_tx_note(txid);
for (const auto &d: pd.m_dests) {
entry.destinations.push_back(wallet_rpc::transfer_destination());
wallet_rpc::transfer_destination &td = entry.destinations.back();
td.amount = d.amount;
- td.address = get_account_address_as_str(m_wallet.testnet(), d.addr);
+ td.address = get_account_address_as_str(m_wallet->testnet(), d.addr);
}
entry.type = "out";
@@ -253,7 +276,7 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.fee = pd.m_amount_in - pd.m_amount_out;
entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
- entry.note = m_wallet.get_tx_note(txid);
+ entry.note = m_wallet->get_tx_note(txid);
entry.type = is_failed ? "failed" : "pending";
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -267,18 +290,19 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.fee = 0; // TODO
- entry.note = m_wallet.get_tx_note(pd.m_tx_hash);
+ entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = "pool";
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
- res.balance = m_wallet.balance();
- res.unlocked_balance = m_wallet.unlocked_balance();
+ res.balance = m_wallet->balance();
+ res.unlocked_balance = m_wallet->unlocked_balance();
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -289,11 +313,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
- res.address = m_wallet.get_account().get_public_address_str(m_wallet.testnet());
+ res.address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -304,11 +329,12 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getheight(const wallet_rpc::COMMAND_RPC_GET_HEIGHT::request& req, wallet_rpc::COMMAND_RPC_GET_HEIGHT::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
- res.height = m_wallet.get_blockchain_current_height();
+ res.height = m_wallet->get_blockchain_current_height();
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -326,7 +352,7 @@ namespace tools
cryptonote::tx_destination_entry de;
bool has_payment_id;
crypto::hash8 new_payment_id;
- if(!get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet.testnet(), it->address))
+ if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, false))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address;
@@ -397,7 +423,8 @@ namespace tools
std::vector<uint8_t> extra;
LOG_PRINT_L3("on_transfer_split starts");
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -413,11 +440,11 @@ namespace tools
try
{
uint64_t mixin = req.mixin;
- if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) {
+ if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) {
LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
mixin = 2;
}
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, req.trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
// reject proposed transactions if there are more than one. see on_transfer_split below.
if (ptx_vector.size() != 1)
@@ -427,7 +454,7 @@ namespace tools
return false;
}
- m_wallet.commit_tx(ptx_vector);
+ 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));
@@ -465,7 +492,8 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -481,17 +509,17 @@ namespace tools
try
{
uint64_t mixin = req.mixin;
- if (mixin < 2 && m_wallet.use_fork_rules(2, 10)) {
+ if (mixin < 2 && m_wallet->use_fork_rules(2, 10)) {
LOG_PRINT_L1("Requested mixin " << req.mixin << " too low for hard fork 2, using 2");
mixin = 2;
}
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.trusted_daemon);
+ ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
LOG_PRINT_L2("on_transfer_split calling commit_txyy");
- m_wallet.commit_tx(ptx_vector);
+ m_wallet->commit_tx(ptx_vector);
LOG_PRINT_L2("on_transfer_split called commit_txyy");
// populate response with tx hashes
@@ -530,7 +558,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -539,9 +568,9 @@ namespace tools
try
{
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_unmixable_sweep_transactions(req.trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon);
- m_wallet.commit_tx(ptx_vector);
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -582,7 +611,8 @@ namespace tools
std::vector<cryptonote::tx_destination_entry> dsts;
std::vector<uint8_t> extra;
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -601,9 +631,9 @@ namespace tools
try
{
- std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions_all(dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, req.trusted_daemon);
+ std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon);
- m_wallet.commit_tx(ptx_vector);
+ m_wallet->commit_tx(ptx_vector);
// populate response with tx hashes
for (auto & ptx : ptx_vector)
@@ -641,6 +671,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_make_integrated_address(const wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_MAKE_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
crypto::hash8 payment_id;
@@ -658,11 +689,11 @@ namespace tools
}
}
- res.integrated_address = m_wallet.get_account().get_public_integrated_address_str(payment_id, m_wallet.testnet());
+ res.integrated_address = m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet());
res.payment_id = epee::string_tools::pod_to_hex(payment_id);
return true;
}
- catch (std::exception &e)
+ catch (const std::exception &e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -673,13 +704,14 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_split_integrated_address(const wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_SPLIT_INTEGRATED_ADDRESS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
cryptonote::account_public_address address;
crypto::hash8 payment_id;
bool has_payment_id;
- if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.integrated_address))
+ if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet->testnet(), req.integrated_address))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Invalid address";
@@ -691,11 +723,11 @@ namespace tools
er.message = "Address is not an integrated address";
return false;
}
- res.standard_address = get_account_address_as_str(m_wallet.testnet(),address);
+ res.standard_address = get_account_address_as_str(m_wallet->testnet(),address);
res.payment_id = epee::string_tools::pod_to_hex(payment_id);
return true;
}
- catch (std::exception &e)
+ catch (const std::exception &e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -706,7 +738,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -715,9 +748,9 @@ namespace tools
try
{
- m_wallet.store();
+ m_wallet->store();
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -728,6 +761,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
crypto::hash payment_id;
crypto::hash8 payment_id8;
cryptonote::blobdata payment_id_blob;
@@ -757,7 +791,7 @@ namespace tools
res.payments.clear();
std::list<wallet2::payment_details> payment_list;
- m_wallet.get_payments(payment_id, payment_list);
+ m_wallet->get_payments(payment_id, payment_list);
for (auto & payment : payment_list)
{
wallet_rpc::payment_details rpc_payment;
@@ -775,12 +809,13 @@ namespace tools
bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er)
{
res.payments.clear();
+ if (!m_wallet) return not_open(er);
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
if (req.payment_ids.empty())
{
std::list<std::pair<crypto::hash,wallet2::payment_details>> payment_list;
- m_wallet.get_payments(payment_list, req.min_block_height);
+ m_wallet->get_payments(payment_list, req.min_block_height);
for (auto & payment : payment_list)
{
@@ -829,7 +864,7 @@ namespace tools
}
std::list<wallet2::payment_details> payment_list;
- m_wallet.get_payments(payment_id, payment_list, req.min_block_height);
+ m_wallet->get_payments(payment_id, payment_list, req.min_block_height);
for (auto & payment : payment_list)
{
@@ -848,6 +883,7 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
if(req.transfer_type.compare("all") != 0 && req.transfer_type.compare("available") != 0 && req.transfer_type.compare("unavailable") != 0)
{
er.code = WALLET_RPC_ERROR_CODE_TRANSFER_TYPE;
@@ -869,7 +905,7 @@ namespace tools
}
wallet2::transfer_container transfers;
- m_wallet.get_transfers(transfers);
+ m_wallet->get_transfers(transfers);
bool transfers_found = false;
for (const auto& td : transfers)
@@ -896,7 +932,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -905,7 +942,7 @@ namespace tools
if (req.key_type.compare("mnemonic") == 0)
{
- if (!m_wallet.get_seed(res.key))
+ if (!m_wallet->get_seed(res.key))
{
er.message = "The wallet is non-deterministic. Cannot display seed.";
return false;
@@ -913,7 +950,7 @@ namespace tools
}
else if(req.key_type.compare("view_key") == 0)
{
- res.key = string_tools::pod_to_hex(m_wallet.get_account().get_keys().m_view_secret_key);
+ res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key);
}
else
{
@@ -926,7 +963,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -935,9 +973,9 @@ namespace tools
try
{
- m_wallet.rescan_blockchain();
+ m_wallet->rescan_blockchain();
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -948,20 +986,22 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
- res.signature = m_wallet.sign(req.data);
+ res.signature = m_wallet->sign(req.data);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_verify(const wallet_rpc::COMMAND_RPC_VERIFY::request& req, wallet_rpc::COMMAND_RPC_VERIFY::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -971,20 +1011,21 @@ namespace tools
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id;
- if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id, m_wallet.testnet(), req.address))
+ if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, false))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "";
return false;
}
- res.good = m_wallet.verify(req.data, address, req.signature);
+ res.good = m_wallet->verify(req.data, address, req.signature);
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_stop_wallet(const wallet_rpc::COMMAND_RPC_STOP_WALLET::request& req, wallet_rpc::COMMAND_RPC_STOP_WALLET::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -993,10 +1034,10 @@ namespace tools
try
{
- m_wallet.store();
+ m_wallet->store();
m_stop.store(true, std::memory_order_relaxed);
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = e.what();
@@ -1007,6 +1048,14 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
if (req.txids.size() != req.notes.size())
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
@@ -1019,7 +1068,7 @@ namespace tools
while (i != req.txids.end())
{
cryptonote::blobdata txid_blob;
- if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "TX ID has invalid format";
@@ -1034,7 +1083,7 @@ namespace tools
std::list<std::string>::const_iterator in = req.notes.begin();
while (il != txids.end())
{
- m_wallet.set_tx_note(*il++, *in++);
+ m_wallet->set_tx_note(*il++, *in++);
}
return true;
@@ -1043,13 +1092,14 @@ namespace tools
bool wallet_rpc_server::on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er)
{
res.notes.clear();
+ if (!m_wallet) return not_open(er);
std::list<crypto::hash> txids;
std::list<std::string>::const_iterator i = req.txids.begin();
while (i != req.txids.end())
{
cryptonote::blobdata txid_blob;
- if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(*i++, txid_blob) || txid_blob.size() != sizeof(crypto::hash))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "TX ID has invalid format";
@@ -1063,14 +1113,15 @@ namespace tools
std::list<crypto::hash>::const_iterator il = txids.begin();
while (il != txids.end())
{
- res.notes.push_back(m_wallet.get_tx_note(*il++));
+ res.notes.push_back(m_wallet->get_tx_note(*il++));
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -1087,7 +1138,7 @@ namespace tools
if (req.in)
{
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
- m_wallet.get_payments(payments, min_height, max_height);
+ m_wallet->get_payments(payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.in.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.in.back(), i->second.m_tx_hash, i->first, i->second);
@@ -1097,7 +1148,7 @@ namespace tools
if (req.out)
{
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments;
- m_wallet.get_payments_out(payments, min_height, max_height);
+ m_wallet->get_payments_out(payments, min_height, max_height);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.out.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.out.back(), i->first, i->second);
@@ -1106,7 +1157,7 @@ namespace tools
if (req.pending || req.failed) {
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
- m_wallet.get_unconfirmed_payments_out(upayments);
+ m_wallet->get_unconfirmed_payments_out(upayments);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed;
@@ -1120,10 +1171,10 @@ namespace tools
if (req.pool)
{
- m_wallet.update_pool_state();
+ m_wallet->update_pool_state();
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
- m_wallet.get_unconfirmed_payments(payments);
+ m_wallet->get_unconfirmed_payments(payments);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
res.pool.push_back(wallet_rpc::transfer_entry());
fill_transfer_entry(res.pool.back(), i->first, i->second);
@@ -1135,7 +1186,8 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er)
{
- if (m_wallet.restricted())
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
@@ -1163,9 +1215,9 @@ namespace tools
}
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
- m_wallet.get_payments(payments, 0);
+ m_wallet->get_payments(payments, 0);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
- if (i->first == txid)
+ if (i->second.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
return true;
@@ -1173,7 +1225,7 @@ namespace tools
}
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> payments_out;
- m_wallet.get_payments_out(payments_out, 0);
+ m_wallet->get_payments_out(payments_out, 0);
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
if (i->first == txid)
{
@@ -1183,7 +1235,7 @@ namespace tools
}
std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>> upayments;
- m_wallet.get_unconfirmed_payments_out(upayments);
+ m_wallet->get_unconfirmed_payments_out(upayments);
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
if (i->first == txid)
{
@@ -1192,10 +1244,10 @@ namespace tools
}
}
- m_wallet.update_pool_state();
+ m_wallet->update_pool_state();
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments;
- m_wallet.get_unconfirmed_payments(pool_payments);
+ m_wallet->get_unconfirmed_payments(pool_payments);
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
if (i->second.m_tx_hash == txid)
{
@@ -1211,9 +1263,10 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
try
{
- std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet.export_key_images();
+ std::vector<std::pair<crypto::key_image, crypto::signature>> ski = m_wallet->export_key_images();
res.signed_key_images.resize(ski.size());
for (size_t n = 0; n < ski.size(); ++n)
{
@@ -1234,6 +1287,14 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
try
{
std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
@@ -1242,7 +1303,7 @@ namespace tools
{
cryptonote::blobdata bd;
- if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].key_image, bd))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].key_image, bd) || bd.size() != sizeof(crypto::key_image))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
er.message = "failed to parse key image";
@@ -1250,7 +1311,7 @@ namespace tools
}
ski[n].first = *reinterpret_cast<const crypto::key_image*>(bd.data());
- if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].signature, bd))
+ if(!epee::string_tools::parse_hexstr_to_binbuff(req.signed_key_images[n].signature, bd) || bd.size() != sizeof(crypto::signature))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_SIGNATURE;
er.message = "failed to parse signature";
@@ -1259,7 +1320,7 @@ namespace tools
ski[n].second = *reinterpret_cast<const crypto::signature*>(bd.data());
}
uint64_t spent = 0, unspent = 0;
- uint64_t height = m_wallet.import_key_images(ski, spent, unspent);
+ uint64_t height = m_wallet->import_key_images(ski, spent, unspent);
res.spent = spent;
res.unspent = unspent;
res.height = height;
@@ -1278,7 +1339,7 @@ namespace tools
bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er)
{
std::string error;
- std::string uri = m_wallet.make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error);
+ std::string uri = m_wallet->make_uri(req.address, req.payment_id, req.amount, req.tx_description, req.recipient_name, error);
if (uri.empty())
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_URI;
@@ -1292,8 +1353,9 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
std::string error;
- if (!m_wallet.parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error))
+ if (!m_wallet->parse_uri(req.uri, res.uri.address, res.uri.payment_id, res.uri.amount, res.uri.tx_description, res.uri.recipient_name, res.unknown_parameters, error))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_URI;
er.message = "Error parsing URI: " + error;
@@ -1304,12 +1366,13 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er)
{
- const auto ab = m_wallet.get_address_book();
+ if (!m_wallet) return not_open(er);
+ const auto ab = m_wallet->get_address_book();
if (req.entries.empty())
{
uint64_t idx = 0;
for (const auto &entry: ab)
- res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
+ res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx++, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
}
else
{
@@ -1322,7 +1385,7 @@ namespace tools
return false;
}
const auto &entry = ab[idx];
- res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet.testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
+ res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, get_account_address_as_str(m_wallet->testnet(), entry.m_address), epee::string_tools::pod_to_hex(entry.m_payment_id), entry.m_description});
}
}
return true;
@@ -1330,11 +1393,19 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er)
{
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
cryptonote::account_public_address address;
bool has_payment_id;
crypto::hash8 payment_id8;
crypto::hash payment_id = cryptonote::null_hash;
- if(!get_account_integrated_address_from_str(address, has_payment_id, payment_id8, m_wallet.testnet(), req.address))
+ if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, false))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
@@ -1372,26 +1443,34 @@ namespace tools
}
}
}
- if (!m_wallet.add_address_book_row(address, payment_id, req.description))
+ if (!m_wallet->add_address_book_row(address, payment_id, req.description))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to add address book entry";
return false;
}
- res.index = m_wallet.get_address_book().size();
+ res.index = m_wallet->get_address_book().size();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er)
{
- const auto ab = m_wallet.get_address_book();
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
+ const auto ab = m_wallet->get_address_book();
if (req.index >= ab.size())
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_INDEX;
er.message = "Index out of range: " + std::to_string(req.index);
return false;
}
- if (!m_wallet.delete_address_book_row(req.index))
+ if (!m_wallet->delete_address_book_row(req.index))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to delete address book entry";
@@ -1400,6 +1479,224 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_wallet->restricted())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+ try
+ {
+ m_wallet->rescan_spent();
+ return true;
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = e.what();
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er)
+ {
+ if (!m_wallet) return not_open(er);
+ if (!m_trusted_daemon)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "This command requires a trusted daemon.";
+ return false;
+ }
+
+ size_t max_mining_threads_count = (std::max)(tools::get_max_concurrency(), static_cast<unsigned>(2));
+ if (req.threads_count < 1 || max_mining_threads_count < req.threads_count)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "The specified number of threads is inappropriate.";
+ return false;
+ }
+
+ cryptonote::COMMAND_RPC_START_MINING::request daemon_req = AUTO_VAL_INIT(daemon_req);
+ daemon_req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet());
+ daemon_req.threads_count = req.threads_count;
+ daemon_req.do_background_mining = req.do_background_mining;
+ daemon_req.ignore_battery = req.ignore_battery;
+
+ cryptonote::COMMAND_RPC_START_MINING::response daemon_res;
+ bool r = net_utils::invoke_http_json("/start_mining", daemon_req, daemon_res, m_http_client);
+ if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Couldn't start mining due to unknown error.";
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er)
+ {
+ cryptonote::COMMAND_RPC_STOP_MINING::request daemon_req;
+ cryptonote::COMMAND_RPC_STOP_MINING::response daemon_res;
+ bool r = net_utils::invoke_http_json("/stop_mining", daemon_req, daemon_res, m_http_client);
+ if (!r || daemon_res.status != CORE_RPC_STATUS_OK)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Couldn't stop mining due to unknown error.";
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er)
+ {
+ crypto::ElectrumWords::get_language_list(res.languages);
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er)
+ {
+ if (m_wallet_dir.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "No wallet dir configured";
+ return false;
+ }
+
+ namespace po = boost::program_options;
+ po::variables_map vm2;
+ const char *ptr = strchr(req.filename.c_str(), '/');
+#ifdef _WIN32
+ if (!ptr)
+ ptr = strchr(req.filename.c_str(), '\\');
+ if (!ptr)
+ ptr = strchr(req.filename.c_str(), ':');
+#endif
+ if (ptr)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Invalid filename";
+ return false;
+ }
+ std::string wallet_file = m_wallet_dir + "/" + req.filename;
+ {
+ std::vector<std::string> languages;
+ crypto::ElectrumWords::get_language_list(languages);
+ std::vector<std::string>::iterator it;
+ std::string wallet_file;
+ char *ptr;
+
+ it = std::find(languages.begin(), languages.end(), req.language);
+ if (it == languages.end())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Unknown language";
+ return false;
+ }
+ }
+ {
+ po::options_description desc("dummy");
+ const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
+ const char *argv[4];
+ int argc = 3;
+ argv[0] = "wallet-rpc";
+ argv[1] = "--password";
+ argv[2] = req.password.c_str();
+ argv[3] = NULL;
+ vm2 = *m_vm;
+ command_line::add_arg(desc, arg_password);
+ po::store(po::parse_command_line(argc, argv, desc), vm2);
+ }
+ std::unique_ptr<tools::wallet2> wal = tools::wallet2::make_new(vm2).first;
+ if (!wal)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to create wallet";
+ return false;
+ }
+ wal->set_seed_language(req.language);
+ cryptonote::COMMAND_RPC_GET_HEIGHT::request hreq;
+ cryptonote::COMMAND_RPC_GET_HEIGHT::response hres;
+ hres.height = 0;
+ bool r = net_utils::invoke_http_json("/getheight", hreq, hres, m_http_client);
+ wal->set_refresh_from_block_height(hres.height);
+ crypto::secret_key dummy_key;
+ try {
+ wal->generate(wallet_file, req.password, dummy_key, false, false);
+ }
+ catch (const std::exception& e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to generate wallet";
+ return false;
+ }
+ if (m_wallet)
+ delete m_wallet;
+ m_wallet = wal.release();
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er)
+ {
+ if (m_wallet_dir.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "No wallet dir configured";
+ return false;
+ }
+
+ namespace po = boost::program_options;
+ po::variables_map vm2;
+ const char *ptr = strchr(req.filename.c_str(), '/');
+#ifdef _WIN32
+ if (!ptr)
+ ptr = strchr(req.filename.c_str(), '\\');
+ if (!ptr)
+ ptr = strchr(req.filename.c_str(), ':');
+#endif
+ if (ptr)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Invalid filename";
+ return false;
+ }
+ std::string wallet_file = m_wallet_dir + "/" + req.filename;
+ {
+ po::options_description desc("dummy");
+ const command_line::arg_descriptor<std::string, true> arg_password = {"password", "password"};
+ const char *argv[4];
+ int argc = 3;
+ argv[0] = "wallet-rpc";
+ argv[1] = "--password";
+ argv[2] = req.password.c_str();
+ argv[3] = NULL;
+ vm2 = *m_vm;
+ command_line::add_arg(desc, arg_password);
+ po::store(po::parse_command_line(argc, argv, desc), vm2);
+ }
+ std::unique_ptr<tools::wallet2> wal;
+ try {
+ wal = tools::wallet2::make_from_file(vm2, wallet_file).first;
+ }
+ catch (const std::exception& e)
+ {
+ wal = nullptr;
+ }
+ if (!wal)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to open wallet";
+ return false;
+ }
+ if (m_wallet)
+ delete m_wallet;
+ m_wallet = wal.release();
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
}
int main(int argc, char** argv) {
@@ -1410,19 +1707,21 @@ int main(int argc, char** argv) {
po::options_description desc_params(wallet_args::tr("Wallet options"));
tools::wallet2::init_options(desc_params);
- command_line::add_arg(desc_params, arg_rpc_bind_ip);
command_line::add_arg(desc_params, arg_rpc_bind_port);
- command_line::add_arg(desc_params, arg_rpc_login);
command_line::add_arg(desc_params, arg_disable_rpc_login);
- command_line::add_arg(desc_params, arg_confirm_external_bind);
+ command_line::add_arg(desc_params, arg_trusted_daemon);
+ cryptonote::rpc_args::init_options(desc_params);
command_line::add_arg(desc_params, arg_wallet_file);
command_line::add_arg(desc_params, arg_from_json);
+ command_line::add_arg(desc_params, arg_wallet_dir);
+
const auto vm = wallet_args::main(
argc, argv,
- "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>] [--rpc-bind-port=<port>]",
+ "monero-wallet-rpc [--wallet-file=<file>|--generate-from-json=<file>|--wallet-dir=<directory>] [--rpc-bind-port=<port>]",
desc_params,
po::positional_options_description(),
+ "monero-wallet-rpc.log",
true
);
if (!vm)
@@ -1435,6 +1734,7 @@ int main(int argc, char** argv) {
{
const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file);
const auto from_json = command_line::get_arg(*vm, arg_from_json);
+ const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir);
if(!wallet_file.empty() && !from_json.empty())
{
@@ -1442,9 +1742,15 @@ int main(int argc, char** argv) {
return 1;
}
+ if (!wallet_dir.empty())
+ {
+ wal = NULL;
+ goto just_dir;
+ }
+
if (wallet_file.empty() && from_json.empty())
{
- LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json"));
+ LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
return 1;
}
@@ -1485,8 +1791,10 @@ int main(int argc, char** argv) {
LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
return 1;
}
- tools::wallet_rpc_server wrpc(*wal);
- bool r = wrpc.init(*vm);
+just_dir:
+ tools::wallet_rpc_server wrpc;
+ if (wal) wrpc.set_wallet(wal.release());
+ bool r = wrpc.init(&(vm.get()));
CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet rpc server"));
tools::signal_handler::install([&wrpc, &wal](int) {
wrpc.send_stop_signal();
@@ -1497,7 +1805,7 @@ int main(int argc, char** argv) {
try
{
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Storing wallet..."));
- wal->store();
+ wrpc.stop();
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stored ok"));
}
catch (const std::exception& e)
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 98e6e72ed..230dcee5b 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -52,11 +52,14 @@ namespace tools
static const char* tr(const char* str);
- wallet_rpc_server(wallet2& cr);
+ wallet_rpc_server();
~wallet_rpc_server();
- bool init(const boost::program_options::variables_map& vm);
+ bool init(const boost::program_options::variables_map *vm);
bool run();
+ void stop();
+ void set_wallet(wallet2 *cr);
+
private:
CHAIN_HTTP_TO_MAP2(connection_context); //forward http requests to uri map
@@ -92,6 +95,12 @@ namespace tools
MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY)
+ MAP_JON_RPC_WE("rescan_spent", on_rescan_spent, wallet_rpc::COMMAND_RPC_RESCAN_SPENT)
+ MAP_JON_RPC_WE("start_mining", on_start_mining, wallet_rpc::COMMAND_RPC_START_MINING)
+ MAP_JON_RPC_WE("stop_mining", on_stop_mining, wallet_rpc::COMMAND_RPC_STOP_MINING)
+ MAP_JON_RPC_WE("get_languages", on_get_languages, wallet_rpc::COMMAND_RPC_GET_LANGUAGES)
+ MAP_JON_RPC_WE("create_wallet", on_create_wallet, wallet_rpc::COMMAND_RPC_CREATE_WALLET)
+ MAP_JON_RPC_WE("open_wallet", on_open_wallet, wallet_rpc::COMMAND_RPC_OPEN_WALLET)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -125,6 +134,12 @@ namespace tools
bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er);
bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er);
bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er);
+ bool on_rescan_spent(const wallet_rpc::COMMAND_RPC_RESCAN_SPENT::request& req, wallet_rpc::COMMAND_RPC_RESCAN_SPENT::response& res, epee::json_rpc::error& er);
+ bool on_start_mining(const wallet_rpc::COMMAND_RPC_START_MINING::request& req, wallet_rpc::COMMAND_RPC_START_MINING::response& res, epee::json_rpc::error& er);
+ bool on_stop_mining(const wallet_rpc::COMMAND_RPC_STOP_MINING::request& req, wallet_rpc::COMMAND_RPC_STOP_MINING::response& res, epee::json_rpc::error& er);
+ bool on_get_languages(const wallet_rpc::COMMAND_RPC_GET_LANGUAGES::request& req, wallet_rpc::COMMAND_RPC_GET_LANGUAGES::response& res, epee::json_rpc::error& er);
+ bool on_create_wallet(const wallet_rpc::COMMAND_RPC_CREATE_WALLET::request& req, wallet_rpc::COMMAND_RPC_CREATE_WALLET::response& res, epee::json_rpc::error& er);
+ bool on_open_wallet(const wallet_rpc::COMMAND_RPC_OPEN_WALLET::request& req, wallet_rpc::COMMAND_RPC_OPEN_WALLET::response& res, epee::json_rpc::error& er);
//json rpc v2
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er);
@@ -134,9 +149,14 @@ namespace tools
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd);
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd);
void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd);
+ bool not_open(epee::json_rpc::error& er);
- wallet2& m_wallet;
+ wallet2 *m_wallet;
+ std::string m_wallet_dir;
std::string rpc_login_filename;
std::atomic<bool> m_stop;
+ bool m_trusted_daemon;
+ epee::net_utils::http::http_simple_client m_http_client;
+ const boost::program_options::variables_map *m_vm;
};
}
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index e132b4e2b..3c10dc41f 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -30,7 +30,7 @@
#pragma once
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
-#include "cryptonote_core/cryptonote_basic.h"
+#include "cryptonote_basic/cryptonote_basic.h"
#include "crypto/hash.h"
#include "wallet_rpc_server_error_codes.h"
@@ -119,7 +119,6 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool get_tx_key;
- bool trusted_daemon;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -128,7 +127,6 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_key)
- KV_SERIALIZE(trusted_daemon)
END_KV_SERIALIZE_MAP()
};
@@ -158,7 +156,6 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool get_tx_keys;
- bool trusted_daemon;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(destinations)
@@ -167,7 +164,6 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_keys)
- KV_SERIALIZE(trusted_daemon)
END_KV_SERIALIZE_MAP()
};
@@ -199,11 +195,9 @@ namespace wallet_rpc
struct request
{
bool get_tx_keys;
- bool trusted_daemon;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(get_tx_keys)
- KV_SERIALIZE(trusted_daemon)
END_KV_SERIALIZE_MAP()
};
@@ -240,7 +234,7 @@ namespace wallet_rpc
uint64_t unlock_time;
std::string payment_id;
bool get_tx_keys;
- bool trusted_daemon;
+ uint64_t below_amount;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
@@ -249,7 +243,7 @@ namespace wallet_rpc
KV_SERIALIZE(unlock_time)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_keys)
- KV_SERIALIZE(trusted_daemon)
+ KV_SERIALIZE(below_amount)
END_KV_SERIALIZE_MAP()
};
@@ -865,5 +859,113 @@ namespace wallet_rpc
};
};
+ struct COMMAND_RPC_RESCAN_SPENT
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_START_MINING
+ {
+ struct request
+ {
+ uint64_t threads_count;
+ bool do_background_mining;
+ bool ignore_battery;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(threads_count)
+ KV_SERIALIZE(do_background_mining)
+ KV_SERIALIZE(ignore_battery)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_STOP_MINING
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_GET_LANGUAGES
+ {
+ struct request
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ struct response
+ {
+ std::vector<std::string> languages;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(languages)
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_CREATE_WALLET
+ {
+ struct request
+ {
+ std::string filename;
+ std::string password;
+ std::string language;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(filename)
+ KV_SERIALIZE(password)
+ KV_SERIALIZE(language)
+ END_KV_SERIALIZE_MAP()
+ };
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
+
+ struct COMMAND_RPC_OPEN_WALLET
+ {
+ struct request
+ {
+ std::string filename;
+ std::string password;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(filename)
+ KV_SERIALIZE(password)
+ END_KV_SERIALIZE_MAP()
+ };
+ struct response
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ };
}
}
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index 7b541c8f6..3c79c0ac3 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2016, The Monero Project
+// Copyright (c) 2014-2017, The Monero Project
//
// All rights reserved.
//
@@ -43,3 +43,4 @@
#define WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE -10
#define WALLET_RPC_ERROR_CODE_WRONG_URI -11
#define WALLET_RPC_ERROR_CODE_WRONG_INDEX -12
+#define WALLET_RPC_ERROR_CODE_NOT_OPEN -13