aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--src/blockchain_db/CMakeLists.txt16
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp2296
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h452
-rw-r--r--src/blockchain_db/blockchain_db.cpp114
-rw-r--r--src/blockchain_db/blockchain_db.h53
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp62
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h10
-rw-r--r--src/blockchain_db/testdb.h10
-rw-r--r--src/blockchain_utilities/blockchain_ancestry.cpp25
-rw-r--r--src/blockchain_utilities/blockchain_blackball.cpp16
-rw-r--r--src/blockchain_utilities/blockchain_depth.cpp25
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp25
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp89
-rw-r--r--src/blockchain_utilities/blockchain_prune.cpp29
-rw-r--r--src/blockchain_utilities/blockchain_prune_known_spent_data.cpp24
-rw-r--r--src/blockchain_utilities/blockchain_stats.cpp24
-rw-r--r--src/blockchain_utilities/blockchain_usage.cpp25
-rw-r--r--src/blockchain_utilities/blockchain_utilities.h2
-rw-r--r--src/blockchain_utilities/blocksdat_file.cpp15
-rw-r--r--src/blockchain_utilities/blocksdat_file.h3
-rw-r--r--src/blocks/checkpoints.datbin234980 -> 244676 bytes
-rw-r--r--src/checkpoints/checkpoints.cpp1
-rw-r--r--src/common/perf_timer.cpp2
-rw-r--r--src/common/perf_timer.h13
-rw-r--r--src/common/threadpool.cpp31
-rw-r--r--src/common/threadpool.h5
-rw-r--r--src/crypto/CMakeLists.txt4
-rw-r--r--src/crypto/blake256.c9
-rw-r--r--src/crypto/c_threads.h58
-rw-r--r--src/crypto/hash-ops.h8
-rw-r--r--src/crypto/hash.h5
-rw-r--r--src/crypto/rx-slow-hash.c313
-rw-r--r--src/crypto/slow-hash.c26
-rw-r--r--src/cryptonote_basic/connection_context.h5
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h74
-rw-r--r--src/cryptonote_basic/cryptonote_basic_impl.cpp7
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp131
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.h3
-rw-r--r--src/cryptonote_basic/hardfork.cpp4
-rw-r--r--src/cryptonote_basic/hardfork.h11
-rw-r--r--src/cryptonote_basic/miner.cpp20
-rw-r--r--src/cryptonote_basic/miner.h7
-rw-r--r--src/cryptonote_basic/verification_context.h1
-rw-r--r--src/cryptonote_config.h14
-rw-r--r--src/cryptonote_core/CMakeLists.txt1
-rw-r--r--src/cryptonote_core/blockchain.cpp609
-rw-r--r--src/cryptonote_core/blockchain.h70
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp216
-rw-r--r--src/cryptonote_core/cryptonote_core.h102
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp91
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h7
-rw-r--r--src/cryptonote_core/i_core_events.h44
-rw-r--r--src/cryptonote_core/tx_pool.cpp308
-rw-r--r--src/cryptonote_core/tx_pool.h63
-rw-r--r--src/cryptonote_protocol/block_queue.cpp43
-rw-r--r--src/cryptonote_protocol/block_queue.h2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h50
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h6
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl263
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler_common.h4
-rw-r--r--src/cryptonote_protocol/enums.h43
-rw-r--r--src/cryptonote_protocol/fwd.h37
-rw-r--r--src/cryptonote_protocol/levin_notify.cpp193
-rw-r--r--src/cryptonote_protocol/levin_notify.h18
-rw-r--r--src/daemon/command_parser_executor.cpp23
-rw-r--r--src/daemon/command_parser_executor.h4
-rw-r--r--src/daemon/command_server.cpp13
-rw-r--r--src/daemon/core.h22
-rw-r--r--src/daemon/main.cpp11
-rw-r--r--src/daemon/rpc_command_executor.cpp141
-rw-r--r--src/daemon/rpc_command_executor.h6
-rw-r--r--src/daemonizer/posix_fork.cpp7
-rw-r--r--src/device/device_ledger.cpp266
-rw-r--r--src/device/device_ledger.hpp29
-rw-r--r--src/gen_ssl_cert/CMakeLists.txt49
-rw-r--r--src/gen_ssl_cert/gen_ssl_cert.cpp254
-rw-r--r--src/hardforks/CMakeLists.txt47
-rw-r--r--src/hardforks/hardforks.cpp114
-rw-r--r--src/hardforks/hardforks.h52
-rw-r--r--src/net/CMakeLists.txt8
-rw-r--r--src/net/zmq.cpp188
-rw-r--r--src/net/zmq.h136
-rw-r--r--src/p2p/net_node.cpp24
-rw-r--r--src/p2p/net_node.h34
-rw-r--r--src/p2p/net_node.inl71
-rw-r--r--src/p2p/net_node_common.h16
-rw-r--r--src/p2p/net_peerlist.h5
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h9
-rw-r--r--src/p2p/p2p_protocol_defs.h9
-rw-r--r--src/ringct/bulletproofs.cc61
-rw-r--r--src/ringct/rctTypes.cpp38
-rw-r--r--src/ringct/rctTypes.h1
-rw-r--r--src/rpc/CMakeLists.txt15
-rw-r--r--src/rpc/bootstrap_daemon.cpp2
-rw-r--r--src/rpc/bootstrap_daemon.h2
-rw-r--r--src/rpc/core_rpc_server.cpp833
-rw-r--r--src/rpc/core_rpc_server.h27
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h887
-rw-r--r--src/rpc/core_rpc_server_error_codes.h29
-rw-r--r--src/rpc/daemon_handler.cpp24
-rw-r--r--src/rpc/message_data_structs.h1
-rw-r--r--src/rpc/rpc_args.cpp6
-rw-r--r--src/rpc/rpc_args.h2
-rw-r--r--src/rpc/rpc_payment.cpp402
-rw-r--r--src/rpc/rpc_payment.h146
-rw-r--r--src/rpc/rpc_payment_costs.h50
-rw-r--r--src/rpc/rpc_payment_signature.cpp107
-rw-r--r--src/rpc/rpc_payment_signature.h39
-rw-r--r--src/rpc/rpc_version_str.cpp55
-rw-r--r--src/rpc/rpc_version_str.h (renamed from src/blockchain_db/db_types.h)15
-rw-r--r--src/rpc/zmq_server.cpp136
-rw-r--r--src/rpc/zmq_server.h20
-rw-r--r--src/serialization/json_object.cpp23
-rw-r--r--src/serialization/json_object.h3
-rw-r--r--src/simplewallet/simplewallet.cpp695
-rw-r--r--src/simplewallet/simplewallet.h32
-rw-r--r--src/version.cpp.in4
-rw-r--r--src/wallet/CMakeLists.txt73
-rw-r--r--src/wallet/api/subaddress_account.cpp4
-rw-r--r--src/wallet/api/wallet.cpp8
-rw-r--r--src/wallet/message_store.cpp2
-rw-r--r--src/wallet/node_rpc_proxy.cpp184
-rw-r--r--src/wallet/node_rpc_proxy.h66
-rw-r--r--src/wallet/wallet2.cpp925
-rw-r--r--src/wallet/wallet2.h106
-rw-r--r--src/wallet/wallet_args.cpp4
-rw-r--r--src/wallet/wallet_args.h1
-rw-r--r--src/wallet/wallet_errors.h23
-rw-r--r--src/wallet/wallet_light_rpc.h47
-rw-r--r--src/wallet/wallet_rpc_helpers.h94
-rw-r--r--src/wallet/wallet_rpc_payments.cpp196
-rw-r--r--src/wallet/wallet_rpc_server.cpp224
-rw-r--r--src/wallet/wallet_rpc_server.h4
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h81
135 files changed, 8175 insertions, 5305 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8a21763c8..d45363e24 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -112,12 +112,11 @@ add_subdirectory(cryptonote_core)
add_subdirectory(lmdb)
add_subdirectory(multisig)
add_subdirectory(net)
-if(NOT IOS)
- add_subdirectory(blockchain_db)
-endif()
+add_subdirectory(hardforks)
+add_subdirectory(blockchain_db)
add_subdirectory(mnemonics)
+add_subdirectory(rpc)
if(NOT IOS)
- add_subdirectory(rpc)
add_subdirectory(serialization)
endif()
add_subdirectory(wallet)
@@ -128,6 +127,7 @@ add_subdirectory(cryptonote_protocol)
if(NOT IOS)
add_subdirectory(simplewallet)
add_subdirectory(gen_multisig)
+ add_subdirectory(gen_ssl_cert)
add_subdirectory(daemonizer)
add_subdirectory(daemon)
add_subdirectory(blockchain_utilities)
diff --git a/src/blockchain_db/CMakeLists.txt b/src/blockchain_db/CMakeLists.txt
index db161fa5e..0a21e4920 100644
--- a/src/blockchain_db/CMakeLists.txt
+++ b/src/blockchain_db/CMakeLists.txt
@@ -31,14 +31,6 @@ set(blockchain_db_sources
lmdb/db_lmdb.cpp
)
-if (BERKELEY_DB)
- set(blockchain_db_sources
- ${blockchain_db_sources}
- berkeleydb/db_bdb.cpp
- )
-endif()
-
-
set(blockchain_db_headers)
set(blockchain_db_private_headers
@@ -46,13 +38,6 @@ set(blockchain_db_private_headers
lmdb/db_lmdb.h
)
-if (BERKELEY_DB)
- set(blockchain_db_private_headers
- ${blockchain_db_private_headers}
- berkeleydb/db_bdb.h
- )
-endif()
-
monero_private_headers(blockchain_db
${crypto_private_headers})
monero_add_library(blockchain_db
@@ -65,7 +50,6 @@ target_link_libraries(blockchain_db
cncrypto
ringct
${LMDB_LIBRARY}
- ${BDB_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
${Boost_THREAD_LIBRARY}
PRIVATE
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
deleted file mode 100644
index d138a1e7e..000000000
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ /dev/null
@@ -1,2296 +0,0 @@
-// Copyright (c) 2014-2019, 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 "db_bdb.h"
-
-#include <boost/filesystem.hpp>
-#include <memory> // std::unique_ptr
-#include <cstring> // memcpy
-
-#include "cryptonote_basic/cryptonote_format_utils.h"
-#include "crypto/crypto.h"
-#include "profile_tools.h"
-
-using epee::string_tools::pod_to_hex;
-#define DB_DEFAULT_TX (m_write_txn != nullptr ? *m_write_txn : (DbTxn*) nullptr)
-
-// Increase when the DB changes in a non backward compatible way, and there
-// is no automatic conversion, so that a full resync is needed.
-#define VERSION 0
-
-namespace
-{
-
-template <typename T>
-inline void throw0(const T &e)
-{
- LOG_PRINT_L0(e.what());
- throw e;
-}
-
-template <typename T>
-inline void throw1(const T &e)
-{
- LOG_PRINT_L1(e.what());
- throw e;
-}
-
-// cursor needs to be closed when it goes out of scope,
-// this helps if the function using it throws
-struct bdb_cur
-{
- bdb_cur(DbTxn* txn, Db* dbi)
- {
- if (dbi->cursor(txn, &m_cur, 0))
- throw0(cryptonote::DB_ERROR("Error opening db cursor"));
- done = false;
- }
-
- ~bdb_cur()
- {
- close();
- }
-
- operator Dbc*()
- {
- return m_cur;
- }
- operator Dbc**()
- {
- return &m_cur;
- }
- Dbc* operator->()
- {
- return m_cur;
- }
-
- void close()
- {
- if (!done)
- {
- m_cur->close();
- done = true;
- }
- }
-
-private:
- Dbc* m_cur;
- bool done;
-};
-
-const char* const BDB_BLOCKS = "blocks";
-const char* const BDB_BLOCK_TIMESTAMPS = "block_timestamps";
-const char* const BDB_BLOCK_HEIGHTS = "block_heights";
-const char* const BDB_BLOCK_HASHES = "block_hashes";
-const char* const BDB_BLOCK_SIZES = "block_sizes";
-const char* const BDB_BLOCK_DIFFS = "block_diffs";
-const char* const BDB_BLOCK_COINS = "block_coins";
-
-const char* const BDB_TXS = "txs";
-const char* const BDB_TX_UNLOCKS = "tx_unlocks";
-const char* const BDB_TX_HEIGHTS = "tx_heights";
-const char* const BDB_TX_OUTPUTS = "tx_outputs";
-
-const char* const BDB_OUTPUT_TXS = "output_txs";
-const char* const BDB_OUTPUT_INDICES = "output_indices";
-const char* const BDB_OUTPUT_AMOUNTS = "output_amounts";
-const char* const BDB_OUTPUT_KEYS = "output_keys";
-
-const char* const BDB_SPENT_KEYS = "spent_keys";
-
-const char* const BDB_HF_STARTING_HEIGHTS = "hf_starting_heights";
-const char* const BDB_HF_VERSIONS = "hf_versions";
-
-const char* const BDB_PROPERTIES = "properties";
-
-const unsigned int MB = 1024 * 1024;
-// ND: FIXME: db keeps running out of locks when doing full syncs. Possible bug??? Set it to 5K for now.
-const unsigned int DB_MAX_LOCKS = 5000;
-const unsigned int DB_BUFFER_LENGTH = 32 * MB;
-// 256MB cache adjust as necessary using DB_CONFIG
-const unsigned int DB_DEF_CACHESIZE = 256 * MB;
-
-#if defined(BDB_BULK_CAN_THREAD)
-const unsigned int DB_BUFFER_COUNT = tools::get_max_concurrency();
-#else
-const unsigned int DB_BUFFER_COUNT = 1;
-#endif
-
-template<typename T>
-struct Dbt_copy: public Dbt
-{
- Dbt_copy(const T &t) :
- t_copy(t)
- {
- init();
- }
-
- Dbt_copy()
- {
- init();
- }
-
- void init()
- {
- set_data(&t_copy);
- set_size(sizeof(T));
- set_ulen(sizeof(T));
- set_flags(DB_DBT_USERMEM);
- }
-
- operator T()
- {
- return t_copy;
- }
-private:
- T t_copy;
-};
-
-template<>
-struct Dbt_copy<cryptonote::blobdata>: public Dbt
-{
- Dbt_copy(const cryptonote::blobdata &bd) :
- m_data(new char[bd.size()])
- {
- memcpy(m_data.get(), bd.data(), bd.size());
- set_data(m_data.get());
- set_size(bd.size());
- set_ulen(bd.size());
- set_flags(DB_DBT_USERMEM);
- }
-private:
- std::unique_ptr<char[]> m_data;
-};
-
-template<>
-struct Dbt_copy<const char*>: public Dbt
-{
- Dbt_copy(const char *s) :
- m_data(strdup(s))
- {
- size_t len = strlen(s) + 1; // include the NUL, makes it easier for compare
- set_data(m_data.get());
- set_size(len);
- set_ulen(len);
- set_flags(DB_DBT_USERMEM);
- }
-private:
- std::unique_ptr<char[]> m_data;
-};
-
-struct Dbt_safe : public Dbt
-{
- Dbt_safe()
- {
- set_data(NULL);
- set_flags(DB_DBT_MALLOC);
- }
- ~Dbt_safe()
- {
- void* buf = get_data();
- if (buf != NULL)
- {
- free(buf);
- }
- }
-};
-
-} // anonymous namespace
-
-namespace cryptonote
-{
-
-void BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> val_h(blk_hash);
- if (m_block_heights->exists(DB_DEFAULT_TX, &val_h, 0) == 0)
- throw1(BLOCK_EXISTS("Attempting to add block that's already in the db"));
-
- if (m_height > 0)
- {
- Dbt_copy<crypto::hash> parent_key(blk.prev_id);
- Dbt_copy<uint32_t> parent_h;
- if (m_block_heights->get(DB_DEFAULT_TX, &parent_key, &parent_h, 0))
- {
- LOG_PRINT_L3("m_height: " << m_height);
- LOG_PRINT_L3("parent_key: " << blk.prev_id);
- throw0(DB_ERROR("Failed to get top block hash to check for new block's parent"));
- }
- uint32_t parent_height = parent_h;
- if (parent_height != m_height)
- throw0(BLOCK_PARENT_DNE("Top block is not new block's parent"));
- }
-
- Dbt_copy<uint32_t> key(m_height + 1);
-
- Dbt_copy<blobdata> blob(block_to_blob(blk));
- auto res = m_blocks->put(DB_DEFAULT_TX, &key, &blob, 0);
- if (res)
- throw0(DB_ERROR("Failed to add block blob to db transaction."));
-
- Dbt_copy<size_t> sz(block_weight);
- if (m_block_sizes->put(DB_DEFAULT_TX, &key, &sz, 0))
- throw0(DB_ERROR("Failed to add block size to db transaction."));
-
- Dbt_copy<uint64_t> ts(blk.timestamp);
- if (m_block_timestamps->put(DB_DEFAULT_TX, &key, &ts, 0))
- throw0(DB_ERROR("Failed to add block timestamp to db transaction."));
-
- Dbt_copy<difficulty_type> diff(cumulative_difficulty);
- if (m_block_diffs->put(DB_DEFAULT_TX, &key, &diff, 0))
- throw0(DB_ERROR("Failed to add block cumulative difficulty to db transaction."));
-
- Dbt_copy<uint64_t> coinsgen(coins_generated);
- if (m_block_coins->put(DB_DEFAULT_TX, &key, &coinsgen, 0))
- throw0(DB_ERROR("Failed to add block total generated coins to db transaction."));
-
- if (m_block_heights->put(DB_DEFAULT_TX, &val_h, &key, 0))
- throw0(DB_ERROR("Failed to add block height by hash to db transaction."));
-
- if (m_block_hashes->put(DB_DEFAULT_TX, &key, &val_h, 0))
- throw0(DB_ERROR("Failed to add block hash to db transaction."));
-}
-
-void BlockchainBDB::remove_block()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- if (m_height == 0)
- throw0(BLOCK_DNE ("Attempting to remove block from an empty blockchain"));
-
- Dbt_copy<uint32_t> k(m_height);
- Dbt_copy<crypto::hash> h;
- if (m_block_hashes->get(DB_DEFAULT_TX, &k, &h, 0))
- throw1(BLOCK_DNE("Attempting to remove block that's not in the db"));
-
- if (m_blocks->del(DB_DEFAULT_TX, &k, 0))
- throw1(DB_ERROR("Failed to add removal of block to db transaction"));
-
- if (m_block_sizes->del(DB_DEFAULT_TX, &k, 0))
- throw1(DB_ERROR("Failed to add removal of block size to db transaction"));
-
- if (m_block_diffs->del(DB_DEFAULT_TX, &k, 0))
- throw1(DB_ERROR("Failed to add removal of block cumulative difficulty to db transaction"));
-
- if (m_block_coins->del(DB_DEFAULT_TX, &k, 0))
- throw1(DB_ERROR("Failed to add removal of block total generated coins to db transaction"));
-
- if (m_block_timestamps->del(DB_DEFAULT_TX, &k, 0))
- throw1(DB_ERROR("Failed to add removal of block timestamp to db transaction"));
-
- if (m_block_heights->del(DB_DEFAULT_TX, &h, 0))
- throw1(DB_ERROR("Failed to add removal of block height by hash to db transaction"));
-
- if (m_block_hashes->del(DB_DEFAULT_TX, &k, 0))
- throw1(DB_ERROR("Failed to add removal of block hash to db transaction"));
-}
-
-void BlockchainBDB::add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> val_h(tx_hash);
-
- if (m_txs->exists(DB_DEFAULT_TX, &val_h, 0) == 0)
- throw1(TX_EXISTS("Attempting to add transaction that's already in the db"));
-
- Dbt_copy<blobdata> blob(tx_to_blob(tx));
- if (m_txs->put(DB_DEFAULT_TX, &val_h, &blob, 0))
- throw0(DB_ERROR("Failed to add tx blob to db transaction"));
-
- Dbt_copy<uint64_t> height(m_height + 1);
- if (m_tx_heights->put(DB_DEFAULT_TX, &val_h, &height, 0))
- throw0(DB_ERROR("Failed to add tx block height to db transaction"));
-
- Dbt_copy<uint64_t> unlock_time(tx.unlock_time);
- if (m_tx_unlocks->put(DB_DEFAULT_TX, &val_h, &unlock_time, 0))
- throw0(DB_ERROR("Failed to add tx unlock time to db transaction"));
-}
-
-void BlockchainBDB::remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> val_h(tx_hash);
- if (m_txs->exists(DB_DEFAULT_TX, &val_h, 0))
- throw1(TX_DNE("Attempting to remove transaction that isn't in the db"));
-
- if (m_txs->del(DB_DEFAULT_TX, &val_h, 0))
- throw1(DB_ERROR("Failed to add removal of tx to db transaction"));
- if (m_tx_unlocks->del(DB_DEFAULT_TX, &val_h, 0))
- throw1(DB_ERROR("Failed to add removal of tx unlock time to db transaction"));
- if (m_tx_heights->del(DB_DEFAULT_TX, &val_h, 0))
- throw1(DB_ERROR("Failed to add removal of tx block height to db transaction"));
-
- remove_tx_outputs(tx_hash, tx);
-
- auto result = m_tx_outputs->del(DB_DEFAULT_TX, &val_h, 0);
- if (result == DB_NOTFOUND)
- LOG_PRINT_L1("tx has no outputs to remove: " << tx_hash);
- else if (result)
- throw1(DB_ERROR("Failed to add removal of tx outputs to db transaction"));
-}
-
-void BlockchainBDB::add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> k(m_num_outputs + 1);
- Dbt_copy<crypto::hash> v(tx_hash);
-
- if (m_output_txs->put(DB_DEFAULT_TX, &k, &v, 0))
- throw0(DB_ERROR("Failed to add output tx hash to db transaction"));
- if (m_tx_outputs->put(DB_DEFAULT_TX, &v, &k, 0))
- throw0(DB_ERROR("Failed to add tx output index to db transaction"));
-
- Dbt_copy<uint64_t> val_local_index(local_index);
- if (m_output_indices->put(DB_DEFAULT_TX, &k, &val_local_index, 0))
- throw0(DB_ERROR("Failed to add tx output index to db transaction"));
-
- Dbt_copy<uint64_t> val_amount(tx_output.amount);
- if (m_output_amounts->put(DB_DEFAULT_TX, &val_amount, &k, 0))
- throw0(DB_ERROR("Failed to add output amount to db transaction."));
-
- if (tx_output.target.type() == typeid(txout_to_key))
- {
- output_data_t od;
- od.pubkey = boost::get < txout_to_key > (tx_output.target).key;
- od.unlock_time = unlock_time;
- od.height = m_height;
-
- Dbt_copy<output_data_t> data(od);
- if (m_output_keys->put(DB_DEFAULT_TX, &k, &data, 0))
- throw0(DB_ERROR("Failed to add output pubkey to db transaction"));
- }
- else
- {
- throw0(DB_ERROR("Wrong output type: expected txout_to_key"));
- }
-
- m_num_outputs++;
-}
-
-void BlockchainBDB::remove_tx_outputs(const crypto::hash& tx_hash, const transaction& tx)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
-
- bdb_cur cur(DB_DEFAULT_TX, m_tx_outputs);
-
- Dbt_copy<crypto::hash> k(tx_hash);
- Dbt_copy<uint32_t> v;
-
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- {
- LOG_PRINT_L2("tx has no outputs, so no global output indices");
- }
- else if (result)
- {
- throw0(DB_ERROR("DB error attempting to get an output"));
- }
- else
- {
- result = cur->get(&k, &v, DB_NEXT_NODUP);
- if (result != 0 && result != DB_NOTFOUND)
- throw0(DB_ERROR("DB error attempting to get next non-duplicate tx hash"));
-
- if (result == 0)
- result = cur->get(&k, &v, DB_PREV);
- else if (result == DB_NOTFOUND)
- result = cur->get(&k, &v, DB_LAST);
-
- db_recno_t num_elems = 0;
- cur->count(&num_elems, 0);
-
- // remove in order: from newest to oldest
- for (uint64_t i = num_elems; i > 0; --i)
- {
- const tx_out tx_output = tx.vout[i-1];
- remove_output(v, tx_output.amount);
- if (i > 1)
- {
- cur->get(&k, &v, DB_PREV_DUP);
- }
- }
- }
-
- cur.close();
-}
-
-// TODO: probably remove this function
-void BlockchainBDB::remove_output(const tx_out& tx_output)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__ << " (unused version - does nothing)");
- return;
-}
-
-void BlockchainBDB::remove_output(const uint64_t& out_index, const uint64_t amount)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> k(out_index);
-
- auto result = m_output_indices->del(DB_DEFAULT_TX, &k, 0);
- if (result == DB_NOTFOUND)
- {
- LOG_PRINT_L0("Unexpected: global output index not found in m_output_indices");
- }
- else if (result)
- {
- throw1(DB_ERROR("Error adding removal of output tx index to db transaction"));
- }
-
- result = m_output_txs->del(DB_DEFAULT_TX, &k, 0);
- // if (result != 0 && result != DB_NOTFOUND)
- // throw1(DB_ERROR("Error adding removal of output tx hash to db transaction"));
- if (result == DB_NOTFOUND)
- {
- LOG_PRINT_L0("Unexpected: global output index not found in m_output_txs");
- }
- else if (result)
- {
- throw1(DB_ERROR("Error adding removal of output tx hash to db transaction"));
- }
-
- result = m_output_keys->del(DB_DEFAULT_TX, &k, 0);
- if (result == DB_NOTFOUND)
- {
- LOG_PRINT_L0("Unexpected: global output index not found in m_output_keys");
- }
- else if (result)
- throw1(DB_ERROR("Error adding removal of output pubkey to db transaction"));
-
- remove_amount_output_index(amount, out_index);
-
- m_num_outputs--;
-}
-
-void BlockchainBDB::remove_amount_output_index(const uint64_t amount, const uint64_t global_output_index)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_cur cur(DB_DEFAULT_TX, m_output_amounts);
-
- Dbt_copy<uint64_t> k(amount);
- Dbt_copy<uint32_t> v;
-
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
- else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
-
- db_recno_t num_elems = 0;
- cur->count(&num_elems, 0);
-
- // workaround for Berkeley DB to start at end of k's duplicate list:
- // if next key exists:
- // - move cursor to start of next key's duplicate list, then move back one
- // duplicate element to reach the end of the original key's duplicate
- // list.
- //
- // else if the next key doesn't exist:
- // - that means we're already on the last key.
- // - move cursor to last element in the db, which is the last element of
- // the desired key's duplicate list.
-
- result = cur->get(&k, &v, DB_NEXT_NODUP);
- if (result != 0 && result != DB_NOTFOUND)
- throw0(DB_ERROR("DB error attempting to get next non-duplicate output amount"));
-
- if (result == 0)
- result = cur->get(&k, &v, DB_PREV);
- else if (result == DB_NOTFOUND)
- result = cur->get(&k, &v, DB_LAST);
-
- bool found_index = false;
- uint64_t amount_output_index = 0;
- uint64_t goi = 0;
-
- for (uint64_t i = num_elems; i > 0; --i)
- {
- goi = v;
- if (goi == global_output_index)
- {
- amount_output_index = i-1;
- found_index = true;
- break;
- }
- if (i > 1)
- cur->get(&k, &v, DB_PREV_DUP);
- }
-
- if (found_index)
- {
- // found the amount output index
- // now delete it
- result = cur->del(0);
- if (result)
- throw0(DB_ERROR(std::string("Error deleting amount output index ").append(boost::lexical_cast<std::string>(amount_output_index)).c_str()));
- }
- else
- {
- // not found
- throw1(OUTPUT_DNE("Failed to find amount output index"));
- }
- cur.close();
-}
-
-void BlockchainBDB::add_spent_key(const crypto::key_image& k_image)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::key_image> val_key(k_image);
- if (m_spent_keys->exists(DB_DEFAULT_TX, &val_key, 0) == 0)
- throw1(KEY_IMAGE_EXISTS("Attempting to add spent key image that's already in the db"));
-
- Dbt_copy<char> val('\0');
- if (m_spent_keys->put(DB_DEFAULT_TX, &val_key, &val, 0))
- throw1(DB_ERROR("Error adding spent key image to db transaction."));
-}
-
-void BlockchainBDB::remove_spent_key(const crypto::key_image& k_image)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::key_image> k(k_image);
- auto result = m_spent_keys->del(DB_DEFAULT_TX, &k, 0);
- if (result != 0 && result != DB_NOTFOUND)
- throw1(DB_ERROR("Error adding removal of key image to db transaction"));
-}
-
-bool BlockchainBDB::for_all_key_images(std::function<bool(const crypto::key_image&)> f) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_cur cur(DB_DEFAULT_TX, m_spent_keys);
-
- Dbt_copy<crypto::key_image> k;
- Dbt_copy<char> v;
- bool ret = true;
- int result;
- while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
- {
- if (!f(k))
- {
- ret = false;
- break;
- }
- }
- if (result != DB_NOTFOUND)
- ret = false;
-
- cur.close();
- return ret;
-}
-
-bool BlockchainBDB::for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)> f) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_cur cur(DB_DEFAULT_TX, m_blocks);
-
- Dbt_copy<uint32_t> k;
- Dbt_safe v;
- bool ret = true;
- int result;
- while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
- {
- uint64_t height = k - 1;
- blobdata bd;
- bd.assign(reinterpret_cast<char*>(v.get_data()), v.get_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"));
- crypto::hash hash;
- if (!get_block_hash(b, hash))
- throw0(DB_ERROR("Failed to get block hash from blob retrieved from the db"));
- if (!f(height, hash, b))
- {
- ret = false;
- break;
- }
- }
- if (result != DB_NOTFOUND)
- ret = false;
-
- cur.close();
- return ret;
-}
-
-bool BlockchainBDB::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f, bool pruned) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_cur cur(DB_DEFAULT_TX, m_txs);
-
- Dbt_copy<crypto::hash> k;
- Dbt_safe v;
- bool ret = true;
- int result;
- while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
- {
- blobdata bd;
- bd.assign(reinterpret_cast<char*>(v.get_data()), v.get_size());
- transaction tx;
- if (!parse_and_validate_tx_from_blob(bd, tx))
- throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
- if (!f(k, tx))
- {
- ret = false;
- break;
- }
- }
- if (result != DB_NOTFOUND)
- ret = false;
-
- cur.close();
- return ret;
-}
-
-bool BlockchainBDB::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_cur cur(DB_DEFAULT_TX, m_output_amounts);
-
- Dbt_copy<uint64_t> k;
- Dbt_copy<uint32_t> v;
- bool ret = true;
- int result;
- while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
- {
- uint32_t global_index = v - 1;
- tx_out_index toi = get_output_tx_and_index_from_global(global_index);
- if (!f(k, toi.first, toi.second))
- {
- ret = false;
- break;
- }
- }
- if (result != DB_NOTFOUND)
- ret = false;
-
- cur.close();
- return ret;
-}
-
-uint64_t BlockchainBDB::get_output_global_index(const uint64_t& amount, const uint64_t& index)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- std::vector < uint64_t > offsets;
- std::vector < uint64_t > global_indices;
- offsets.push_back(index);
- get_output_global_indices(amount, offsets, global_indices);
- if (!global_indices.size())
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
-
- return global_indices[0];
-}
-
-void BlockchainBDB::check_open() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- if (!m_open)
- throw0(DB_ERROR("DB operation attempted on a not-open DB instance"));
-}
-
-BlockchainBDB::~BlockchainBDB()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
-
- if (m_open)
- {
- close();
- }
-}
-
-BlockchainBDB::BlockchainBDB(bool batch_transactions) :
- BlockchainDB(),
- m_buffer(DB_BUFFER_COUNT, DB_BUFFER_LENGTH)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- // initialize folder to something "safe" just in case
- // someone accidentally misuses this class...
- m_folder = "thishsouldnotexistbecauseitisgibberish";
- m_run_checkpoint = 0;
- m_batch_transactions = batch_transactions;
- m_write_txn = nullptr;
- m_height = 0;
-
- m_hardfork = nullptr;
-}
-
-void BlockchainBDB::open(const std::string& filename, const int db_flags)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
-
- if (m_open)
- throw0(DB_OPEN_FAILURE("Attempted to open db, but it's already open"));
-
- boost::filesystem::path direc(filename);
- if (boost::filesystem::exists(direc))
- {
- if (!boost::filesystem::is_directory(direc))
- throw0(DB_OPEN_FAILURE("DB needs a directory path, but a file was passed"));
- }
- else
- {
- if (!boost::filesystem::create_directories(direc))
- throw0(DB_OPEN_FAILURE(std::string("Failed to create directory ").append(filename).c_str()));
- }
-
- m_folder = filename;
-
- try
- {
-
- //Create BerkeleyDB environment
- m_env = new DbEnv(0); // no flags needed for DbEnv
-
- uint32_t db_env_open_flags = DB_CREATE | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN | DB_RECOVER | DB_THREAD;
-
- // Set some default values for these parameters.
- // They can be overridden using the DB_CONFIG file.
- m_env->set_cachesize(0, DB_DEF_CACHESIZE, 1);
- m_env->set_lk_max_locks(DB_MAX_LOCKS);
- m_env->set_lk_max_lockers(DB_MAX_LOCKS);
- m_env->set_lk_max_objects(DB_MAX_LOCKS);
-
- #ifndef __OpenBSD__ //OpenBSD's DB package is too old to support this feature
- if(m_auto_remove_logs)
- m_env->log_set_config(DB_LOG_AUTO_REMOVE, 1);
- #endif
-
- // last parameter left 0, files will be created with default rw access
- m_env->open(filename.c_str(), db_env_open_flags, 0);
- m_env->set_flags(db_flags, 1);
-
- // begin transaction to init dbs
- bdb_txn_safe txn;
- m_env->txn_begin(NULL, txn, 0);
-
- // create Dbs in the environment
- m_blocks = new Db(m_env, 0);
- m_block_heights = new Db(m_env, 0);
- m_block_hashes = new Db(m_env, 0);
- m_block_timestamps = new Db(m_env, 0);
- m_block_sizes = new Db(m_env, 0);
- m_block_diffs = new Db(m_env, 0);
- m_block_coins = new Db(m_env, 0);
-
- m_txs = new Db(m_env, 0);
- m_tx_unlocks = new Db(m_env, 0);
- m_tx_heights = new Db(m_env, 0);
- m_tx_outputs = new Db(m_env, 0);
-
- m_output_txs = new Db(m_env, 0);
- m_output_indices = new Db(m_env, 0);
- m_output_amounts = new Db(m_env, 0);
- m_output_keys = new Db(m_env, 0);
-
- m_spent_keys = new Db(m_env, 0);
-
- m_hf_starting_heights = new Db(m_env, 0);
- m_hf_versions = new Db(m_env, 0);
-
- m_properties = new Db(m_env, 0);
-
- // Tell DB about Dbs that need duplicate support
- // Note: no need to tell about sorting,
- // as the default is insertion order, which we want
- m_tx_outputs->set_flags(DB_DUP);
- m_output_amounts->set_flags(DB_DUP);
-
- // Tell DB about fixed-size values.
- m_block_hashes->set_re_len(sizeof(crypto::hash));
- m_block_timestamps->set_re_len(sizeof(uint64_t));
- m_block_sizes->set_re_len(sizeof(size_t)); // should really store block size as uint64_t...
- m_block_diffs->set_re_len(sizeof(difficulty_type));
- m_block_coins->set_re_len(sizeof(uint64_t));
-
- m_output_txs->set_re_len(sizeof(crypto::hash));
- m_output_indices->set_re_len(sizeof(uint64_t));
- m_output_keys->set_re_len(sizeof(output_data_t));
-
- m_hf_starting_heights->set_re_len(sizeof(uint64_t));
- m_hf_versions->set_re_len(sizeof(uint8_t));
-
- //TODO: Find out if we need to do Db::set_flags(DB_RENUMBER)
- // for the RECNO databases. We shouldn't as we're only
- // inserting/removing from the end, but we'll see.
-
- // open Dbs in the environment
- // m_tx_outputs and m_output_amounts must be DB_HASH or DB_BTREE
- // because they need duplicate entry support. The rest are DB_RECNO,
- // as it seems that will be the most performant choice.
- m_blocks->open(txn, BDB_BLOCKS, NULL, DB_RECNO, DB_CREATE, 0);
-
- m_block_timestamps->open(txn, BDB_BLOCK_TIMESTAMPS, NULL, DB_RECNO, DB_CREATE, 0);
- m_block_heights->open(txn, BDB_BLOCK_HEIGHTS, NULL, DB_HASH, DB_CREATE, 0);
- m_block_hashes->open(txn, BDB_BLOCK_HASHES, NULL, DB_RECNO, DB_CREATE, 0);
- m_block_sizes->open(txn, BDB_BLOCK_SIZES, NULL, DB_RECNO, DB_CREATE, 0);
- m_block_diffs->open(txn, BDB_BLOCK_DIFFS, NULL, DB_RECNO, DB_CREATE, 0);
- m_block_coins->open(txn, BDB_BLOCK_COINS, NULL, DB_RECNO, DB_CREATE, 0);
-
- m_txs->open(txn, BDB_TXS, NULL, DB_HASH, DB_CREATE, 0);
- m_tx_unlocks->open(txn, BDB_TX_UNLOCKS, NULL, DB_HASH, DB_CREATE, 0);
- m_tx_heights->open(txn, BDB_TX_HEIGHTS, NULL, DB_HASH, DB_CREATE, 0);
- m_tx_outputs->open(txn, BDB_TX_OUTPUTS, NULL, DB_HASH, DB_CREATE, 0);
-
- m_output_txs->open(txn, BDB_OUTPUT_TXS, NULL, DB_RECNO, DB_CREATE, 0);
- m_output_indices->open(txn, BDB_OUTPUT_INDICES, NULL, DB_RECNO, DB_CREATE, 0);
- m_output_amounts->open(txn, BDB_OUTPUT_AMOUNTS, NULL, DB_HASH, DB_CREATE, 0);
- m_output_keys->open(txn, BDB_OUTPUT_KEYS, NULL, DB_RECNO, DB_CREATE, 0);
-
- m_spent_keys->open(txn, BDB_SPENT_KEYS, NULL, DB_HASH, DB_CREATE, 0);
-
- m_hf_starting_heights->open(txn, BDB_HF_STARTING_HEIGHTS, NULL, DB_RECNO, DB_CREATE, 0);
- m_hf_versions->open(txn, BDB_HF_VERSIONS, NULL, DB_RECNO, DB_CREATE, 0);
-
- m_properties->open(txn, BDB_PROPERTIES, NULL, DB_HASH, DB_CREATE, 0);
-
- txn.commit();
-
- DB_BTREE_STAT* stats;
-
- // DB_FAST_STAT can apparently cause an incorrect number of records
- // to be returned. The flag should be set to 0 instead if this proves
- // to be the case.
-
- // ND: The bug above can occur when a block is popped and the application
- // exits without pushing a new block to the db. Set txn to NULL and DB_FAST_STAT
- // to zero (0) for reliability.
- m_blocks->stat(NULL, &stats, 0);
- m_height = stats->bt_nkeys;
- free(stats);
-
- // see above comment about DB_FAST_STAT
- m_output_indices->stat(NULL, &stats, 0);
- m_num_outputs = stats->bt_nkeys;
- free(stats);
-
- // checks for compatibility
- bool compatible = true;
-
- Dbt_copy<const char*> key("version");
- Dbt_copy<uint32_t> result;
- auto get_result = m_properties->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == 0)
- {
- if (result > VERSION)
- {
- LOG_PRINT_RED_L0("Existing BerkeleyDB database was made by a later version. We don't know how it will change yet.");
- compatible = false;
- }
-#if VERSION > 0
- else if (result < VERSION)
- {
- compatible = false;
- }
-#endif
- }
- else
- {
- // if not found, but we're on version 0, it's fine. If the DB's empty, it's fine too.
- if (VERSION > 0 && m_height > 0)
- compatible = false;
- }
-
- if (!compatible)
- {
- m_open = false;
- LOG_PRINT_RED_L0("Existing BerkeleyDB database is incompatible with this version.");
- LOG_PRINT_RED_L0("Please delete the existing database and resync.");
- return;
- }
-
- if (1 /* this can't be set readonly atm */)
- {
- // only write version on an empty DB
- if (m_height == 0)
- {
- Dbt_copy<const char*> k("version");
- Dbt_copy<uint32_t> v(VERSION);
- auto put_result = m_properties->put(DB_DEFAULT_TX, &k, &v, 0);
- if (put_result != 0)
- {
- m_open = false;
- LOG_PRINT_RED_L0("Failed to write version to database.");
- return;
- }
- }
- }
-
- // run checkpoint thread
- m_run_checkpoint = true;
- m_checkpoint_thread.reset(new boost::thread(&BlockchainBDB::checkpoint_worker, this));
- }
- catch (const std::exception& e)
- {
- throw0(DB_OPEN_FAILURE(e.what()));
- }
-
- m_open = true;
-}
-
-void BlockchainBDB::close()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- this->sync();
-
- m_run_checkpoint = false;
- m_checkpoint_thread->join();
- m_checkpoint_thread.reset();
-
- // FIXME: not yet thread safe!!! Use with care.
- m_open = false;
-
- // DB_FORCESYNC is only available on newer version of libdb.
- // The libdb doc says using the DB_FORCESYNC flag to DB_ENV->close
- // is "similar to calling the DB->close(0) method to close each
- // database handle". So this is what we do here as a fallback.
-#ifdef DB_FORCESYNC
- m_env->close(DB_FORCESYNC);
-#else
- m_blocks->close(0);
- m_block_heights->close(0);
- m_block_hashes->close(0);
- m_block_timestamps->close(0);
- m_block_sizes->close(0);
- m_block_diffs->close(0);
- m_block_coins->close(0);
-
- m_txs->close(0);
- m_tx_unlocks->close(0);
- m_tx_heights->close(0);
- m_tx_outputs->close(0);
-
- m_output_txs->close(0);
- m_output_indices->close(0);
- m_output_amounts->close(0);
- m_output_keys->close(0);
-
- m_spent_keys->close(0);
-
- m_hf_starting_heights->close(0);
- m_hf_versions->close(0);
-
- m_properties->close(0);
-
- m_env->close(0);
-#endif
-}
-
-void BlockchainBDB::sync()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- try
- {
- m_blocks->sync(0);
- m_block_heights->sync(0);
- m_block_hashes->sync(0);
- m_block_timestamps->sync(0);
- m_block_sizes->sync(0);
- m_block_diffs->sync(0);
- m_block_coins->sync(0);
-
- m_txs->sync(0);
- m_tx_unlocks->sync(0);
- m_tx_heights->sync(0);
- m_tx_outputs->sync(0);
-
- m_output_txs->sync(0);
- m_output_indices->sync(0);
- m_output_amounts->sync(0);
- m_output_keys->sync(0);
-
- m_spent_keys->sync(0);
-
- if (m_hf_starting_heights != nullptr)
- m_hf_starting_heights->sync(0);
- if (m_hf_versions != nullptr)
- m_hf_versions->sync(0);
-
- m_properties->sync(0);
- }
- catch (const std::exception& e)
- {
- throw0(DB_ERROR(std::string("Failed to sync database: ").append(e.what()).c_str()));
- }
-}
-
-void BlockchainBDB::reset()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_txn_safe txn;
- if (m_env->txn_begin(NULL, txn, 0))
- throw0(DB_ERROR("Failed to create a transaction for the db"));
- m_write_txn = &txn;
- try
- {
- uint32_t count;
-
- m_blocks->truncate(*m_write_txn, &count, 0);
- m_block_heights->truncate(*m_write_txn, &count, 0);
- m_block_hashes->truncate(*m_write_txn, &count, 0);
- m_block_timestamps->truncate(*m_write_txn, &count, 0);
- m_block_sizes->truncate(*m_write_txn, &count, 0);
- m_block_diffs->truncate(*m_write_txn, &count, 0);
- m_block_coins->truncate(*m_write_txn, &count, 0);
-
- m_txs->truncate(*m_write_txn, &count, 0);
- m_tx_unlocks->truncate(*m_write_txn, &count, 0);
- m_tx_heights->truncate(*m_write_txn, &count, 0);
- m_tx_outputs->truncate(*m_write_txn, &count, 0);
-
- m_output_txs->truncate(*m_write_txn, &count, 0);
- m_output_indices->truncate(*m_write_txn, &count, 0);
- m_output_amounts->truncate(*m_write_txn, &count, 0);
- m_output_keys->truncate(*m_write_txn, &count, 0);
-
- m_spent_keys->truncate(*m_write_txn, &count, 0);
-
- m_hf_starting_heights->truncate(*m_write_txn, &count, 0);
- m_hf_versions->truncate(*m_write_txn, &count, 0);
-
- m_properties->truncate(*m_write_txn, &count, 0);
- }
- catch (const std::exception& e)
- {
- throw0(DB_ERROR(std::string("Failed to reset database: ").append(e.what()).c_str()));
- }
- m_write_txn = NULL;
-}
-
-std::vector<std::string> BlockchainBDB::get_filenames() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- std::vector<std::string> filenames;
-
- char *fname, *dbname;
- const char **pfname, **pdbname;
-
- pfname = (const char **)&fname;
- pdbname = (const char **)&dbname;
-
- m_blocks->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_block_heights->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_block_hashes->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_block_timestamps->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_block_sizes->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_block_diffs->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_block_coins->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_txs->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_tx_unlocks->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_tx_heights->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_tx_outputs->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_output_txs->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_output_indices->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_output_amounts->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_output_keys->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_spent_keys->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_hf_starting_heights->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_hf_versions->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- m_properties->get_dbname(pfname, pdbname);
- filenames.push_back(fname);
-
- std::vector<std::string> full_paths;
-
- for (auto& filename : filenames)
- {
- boost::filesystem::path p(m_folder);
- p /= filename;
- full_paths.push_back(p.string());
- }
-
- return full_paths;
-}
-
-bool BlockchainBDB::remove_data_file(const std::string& folder)
-{
- return true;
-}
-
-std::string BlockchainBDB::get_db_name() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
-
- return std::string("BerkeleyDB");
-}
-
-// TODO: this?
-bool BlockchainBDB::lock()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- return false;
-}
-
-// TODO: this?
-void BlockchainBDB::unlock()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-}
-
-bool BlockchainBDB::block_exists(const crypto::hash& h, uint64_t *height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> key(h);
-
- auto get_result = m_block_heights->exists(DB_DEFAULT_TX, &key, 0);
- if (get_result == DB_NOTFOUND)
- {
- LOG_PRINT_L3("Block with hash " << epee::string_tools::pod_to_hex(h) << " not found in db");
- return false;
- }
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch block index from hash"));
-
- if (height)
- *height = get_result - 1;
-
- return true;
-}
-
-block BlockchainBDB::get_block(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- return get_block_from_height(get_block_height(h));
-}
-
-uint64_t BlockchainBDB::get_block_height(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> key(h);
- Dbt_copy<uint32_t> result;
-
- auto get_result = m_block_heights->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- throw1(BLOCK_DNE("Attempted to retrieve non-existent block height"));
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve a block height from the db"));
-
- return result - 1;
-}
-
-block_header BlockchainBDB::get_block_header(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- // block_header object is automatically cast from block object
- return get_block(h);
-}
-
-block BlockchainBDB::get_block_from_height(const uint64_t& height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> key(height + 1);
- Dbt_safe result;
- auto get_result = m_blocks->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- {
- throw0(BLOCK_DNE(std::string("Attempt to get block from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block not in db").c_str()));
- }
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve a block from the db"));
-
- blobdata bd;
- bd.assign(reinterpret_cast<char*>(result.get_data()), result.get_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"));
-
- return b;
-}
-
-uint64_t BlockchainBDB::get_block_timestamp(const uint64_t& height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> key(height + 1);
- Dbt_copy<uint64_t> result;
- auto get_result = m_block_timestamps->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- {
- throw0(BLOCK_DNE(std::string("Attempt to get timestamp from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- timestamp not in db").c_str()));
- }
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve a timestamp from the db"));
-
- return result;
-}
-
-uint64_t BlockchainBDB::get_top_block_timestamp() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- // if no blocks, return 0
- if (m_height == 0)
- {
- return 0;
- }
-
- return get_block_timestamp(m_height - 1);
-}
-
-size_t BlockchainBDB::get_block_weight(const uint64_t& height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> key(height + 1);
- Dbt_copy<size_t> result;
- auto get_result = m_block_sizes->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- {
- throw0(BLOCK_DNE(std::string("Attempt to get block size from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str()));
- }
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve a block size from the db"));
-
- return result;
-}
-
-difficulty_type BlockchainBDB::get_block_cumulative_difficulty(const uint64_t& height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__ << " height: " << height);
- check_open();
-
- Dbt_copy<uint32_t> key(height + 1);
- Dbt_copy<difficulty_type> result;
- auto get_result = m_block_diffs->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- {
- throw0(BLOCK_DNE(std::string("Attempt to get cumulative difficulty from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- difficulty not in db").c_str()));
- }
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve a cumulative difficulty from the db"));
-
- return result;
-}
-
-difficulty_type BlockchainBDB::get_block_difficulty(const uint64_t& height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- difficulty_type diff1 = 0;
- difficulty_type diff2 = 0;
-
- diff1 = get_block_cumulative_difficulty(height);
- if (height != 0)
- {
- diff2 = get_block_cumulative_difficulty(height - 1);
- }
-
- return diff1 - diff2;
-}
-
-uint64_t BlockchainBDB::get_block_already_generated_coins(const uint64_t& height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> key(height + 1);
- Dbt_copy<uint64_t> result;
- auto get_result = m_block_coins->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- {
- throw0(BLOCK_DNE(std::string("Attempt to get generated coins from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block size not in db").c_str()));
- }
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve a total generated coins from the db"));
-
- return result;
-}
-
-crypto::hash BlockchainBDB::get_block_hash_from_height(const uint64_t& height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> key(height + 1);
- Dbt_copy<crypto::hash> result;
- auto get_result = m_block_hashes->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- {
- throw0(BLOCK_DNE(std::string("Attempt to get hash from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- hash not in db").c_str()));
- }
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve a block hash from the db."));
-
- return result;
-}
-
-std::vector<block> BlockchainBDB::get_blocks_range(const uint64_t& h1, const uint64_t& h2) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- std::vector<block> v;
-
- for (uint64_t height = h1; height <= h2; ++height)
- {
- v.push_back(get_block_from_height(height));
- }
-
- return v;
-}
-
-std::vector<crypto::hash> BlockchainBDB::get_hashes_range(const uint64_t& h1, const uint64_t& h2) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- std::vector<crypto::hash> v;
-
- for (uint64_t height = h1; height <= h2; ++height)
- {
- v.push_back(get_block_hash_from_height(height));
- }
-
- return v;
-}
-
-crypto::hash BlockchainBDB::top_block_hash() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- if (m_height > 0)
- {
- return get_block_hash_from_height(m_height - 1);
- }
-
- return null_hash;
-}
-
-block BlockchainBDB::get_top_block() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- if (m_height > 0)
- {
- return get_block_from_height(m_height - 1);
- }
-
- block b;
- return b;
-}
-
-uint64_t BlockchainBDB::height() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- return m_height;
-}
-
-bool BlockchainBDB::tx_exists(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> key(h);
-
- TIME_MEASURE_START(time1);
- auto get_result = m_txs->exists(DB_DEFAULT_TX, &key, 0);
- TIME_MEASURE_FINISH(time1);
- time_tx_exists += time1;
- if (get_result == DB_NOTFOUND)
- {
- LOG_PRINT_L1("transaction with hash " << epee::string_tools::pod_to_hex(h) << " not found in db");
- return false;
- }
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch transaction from hash"));
-
- return true;
-}
-
-uint64_t BlockchainBDB::get_tx_unlock_time(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> key(h);
- Dbt_copy<uint64_t> result;
- auto get_result = m_tx_unlocks->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- throw1(TX_DNE(std::string("tx unlock time with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch tx unlock time from hash"));
-
- return result;
-}
-
-transaction BlockchainBDB::get_tx(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> key(h);
- Dbt_safe result;
- auto get_result = m_txs->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- throw1(TX_DNE(std::string("tx with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch tx from hash"));
-
- blobdata bd;
- bd.assign(reinterpret_cast<char*>(result.get_data()), result.get_size());
-
- transaction tx;
- if (!parse_and_validate_tx_from_blob(bd, tx))
- throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
-
- return tx;
-}
-
-uint64_t BlockchainBDB::get_tx_count() const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- DB_BTREE_STAT* stats;
-
- // DB_FAST_STAT can apparently cause an incorrect number of records
- // to be returned. The flag should be set to 0 instead if this proves
- // to be the case.
- m_txs->stat(DB_DEFAULT_TX, &stats, DB_FAST_STAT);
- auto num_txs = stats->bt_nkeys;
- delete stats;
-
- return num_txs;
-}
-
-std::vector<transaction> BlockchainBDB::get_tx_list(const std::vector<crypto::hash>& hlist) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- std::vector<transaction> v;
-
-for (auto& h : hlist)
- {
- v.push_back(get_tx(h));
- }
-
- return v;
-}
-
-uint64_t BlockchainBDB::get_tx_block_height(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::hash> key(h);
- Dbt_copy<uint64_t> result;
- auto get_result = m_tx_heights->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND)
- {
- throw1(TX_DNE(std::string("tx height with hash ").append(epee::string_tools::pod_to_hex(h)).append(" not found in db").c_str()));
- }
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch tx height from hash"));
-
- return (uint64_t)result - 1;
-}
-
-uint64_t BlockchainBDB::get_num_outputs(const uint64_t& amount) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_cur cur(DB_DEFAULT_TX, m_output_amounts);
-
- Dbt_copy<uint64_t> k(amount);
- Dbt_copy<uint32_t> v;
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- {
- return 0;
- }
- else if (result)
- throw0(DB_ERROR("DB error attempting to get number of outputs of an amount"));
-
- db_recno_t num_elems = 0;
- cur->count(&num_elems, 0);
-
- cur.close();
-
- return num_elems;
-}
-
-output_data_t BlockchainBDB::get_output_key(const uint64_t& global_index) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> k(global_index);
- Dbt_copy<output_data_t> v;
- auto get_result = m_output_keys->get(DB_DEFAULT_TX, &k, &v, 0);
- if (get_result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("Attempting to get output pubkey by global index, but key does not exist"));
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve an output pubkey from the db"));
-
- return v;
-}
-
-output_data_t BlockchainBDB::get_output_key(const uint64_t& amount, const uint64_t& index) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- uint64_t glob_index = get_output_global_index(amount, index);
- return get_output_key(glob_index);
-}
-
-tx_out_index BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- std::vector < uint64_t > offsets;
- std::vector<tx_out_index> indices;
- offsets.push_back(index);
- get_output_tx_and_index(amount, offsets, indices);
- if (!indices.size())
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
-
- return indices[0];
-}
-
-std::vector<uint64_t> BlockchainBDB::get_tx_amount_output_indices(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- std::vector<uint64_t> index_vec;
- std::vector<uint64_t> index_vec2;
-
- // get the transaction's global output indices first
- index_vec = get_tx_output_indices(h);
- // these are next used to obtain the amount output indices
-
- transaction tx = get_tx(h);
-
- uint64_t i = 0;
- uint64_t global_index;
- for (const auto& vout : tx.vout)
- {
- uint64_t amount = vout.amount;
-
- global_index = index_vec[i];
-
- bdb_cur cur(DB_DEFAULT_TX, m_output_amounts);
-
- Dbt_copy<uint64_t> k(amount);
- Dbt_copy<uint32_t> v;
-
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
- else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
-
- db_recno_t num_elems = 0;
- cur->count(&num_elems, 0);
-
- uint64_t amount_output_index = 0;
- uint64_t output_index = 0;
- bool found_index = false;
- for (uint64_t j = 0; j < num_elems; ++j)
- {
- output_index = v;
- if (output_index == global_index)
- {
- amount_output_index = j;
- found_index = true;
- break;
- }
- cur->get(&k, &v, DB_NEXT_DUP);
- }
- if (found_index)
- {
- index_vec2.push_back(amount_output_index);
- }
- else
- {
- // not found
- cur.close();
- throw1(OUTPUT_DNE("specified output not found in db"));
- }
-
- cur.close();
- ++i;
- }
-
- return index_vec2;
-}
-
-
-tx_out_index BlockchainBDB::get_output_tx_and_index_from_global(const uint64_t& index) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> k(index);
- Dbt_copy<crypto::hash > v;
-
- auto get_result = m_output_txs->get(DB_DEFAULT_TX, &k, &v, 0);
- if (get_result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("output with given index not in db"));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch output tx hash"));
-
- crypto::hash tx_hash = v;
-
- Dbt_copy<uint64_t> result;
- get_result = m_output_indices->get(DB_DEFAULT_TX, &k, &result, 0);
- if (get_result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("output with given index not in db"));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch output tx index"));
-
- return tx_out_index(tx_hash, result);
-}
-
-bool BlockchainBDB::has_key_image(const crypto::key_image& img) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<crypto::key_image> val_key(img);
- if (m_spent_keys->exists(DB_DEFAULT_TX, &val_key, 0) == 0)
- {
- return true;
- }
-
- return false;
-}
-
-// Ostensibly BerkeleyDB has batch transaction support built-in,
-// so the following few functions will be NOP.
-
-bool BlockchainBDB::batch_start(uint64_t batch_num_blocks)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- return false;
-}
-
-void BlockchainBDB::batch_commit()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
-}
-
-void BlockchainBDB::batch_stop()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
-}
-
-void BlockchainBDB::batch_abort()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
-}
-
-void BlockchainBDB::set_batch_transactions(bool batch_transactions)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- m_batch_transactions = batch_transactions;
- LOG_PRINT_L3("batch transactions " << (m_batch_transactions ? "enabled" : "disabled"));
-}
-
-void BlockchainBDB::block_txn_start(bool readonly)
-{
- // TODO
-}
-
-void BlockchainBDB::block_txn_stop()
-{
- // TODO
-}
-
-void BlockchainBDB::block_txn_abort()
-{
- // TODO
-}
-
-uint64_t BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<transaction>& txs)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_txn_safe txn;
- if (m_env->txn_begin(NULL, txn, 0))
- throw0(DB_ERROR("Failed to create a transaction for the db"));
- m_write_txn = &txn;
-
- uint64_t num_outputs = m_num_outputs;
- try
- {
- BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs);
- m_write_txn = NULL;
-
- TIME_MEASURE_START(time1);
- txn.commit();
- TIME_MEASURE_FINISH(time1);
- time_commit1 += time1;
- }
- catch (const std::exception& e)
- {
- m_num_outputs = num_outputs;
- m_write_txn = NULL;
- throw;
- }
-
- return ++m_height;
-}
-
-void BlockchainBDB::pop_block(block& blk, std::vector<transaction>& txs)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_txn_safe txn;
- if (m_env->txn_begin(NULL, txn, 0))
- throw0(DB_ERROR("Failed to create a transaction for the db"));
- m_write_txn = &txn;
-
- uint64_t num_outputs = m_num_outputs;
- try
- {
- BlockchainDB::pop_block(blk, txs);
-
- m_write_txn = NULL;
- txn.commit();
- }
- catch (...)
- {
- m_num_outputs = num_outputs;
- m_write_txn = NULL;
- throw;
- }
-
- --m_height;
-}
-
-void BlockchainBDB::get_output_tx_and_index_from_global(const std::vector<uint64_t> &global_indices, std::vector<tx_out_index> &tx_out_indices) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- tx_out_indices.clear();
-
- for (const uint64_t &index : global_indices)
- {
- Dbt_copy<uint32_t> k(index);
- Dbt_copy<crypto::hash> v;
-
- auto get_result = m_output_txs->get(DB_DEFAULT_TX, &k, &v, 0);
- if (get_result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("output with given index not in db"));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch output tx hash"));
-
- crypto::hash tx_hash = v;
-
- Dbt_copy<uint64_t> result;
- get_result = m_output_indices->get(DB_DEFAULT_TX, &k, &result, 0);
- if (get_result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("output with given index not in db"));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch output tx index"));
- auto hashindex = tx_out_index(tx_hash, result);
- tx_out_indices.push_back(hashindex);
- }
-}
-
-void BlockchainBDB::get_output_global_indices(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<uint64_t> &global_indices)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- TIME_MEASURE_START(txx);
- check_open();
-
- bdb_cur cur(DB_DEFAULT_TX, m_output_amounts);
- uint64_t max = 0;
- for (const uint64_t& index : offsets)
- {
- if (index > max)
- max = index;
- }
-
- // get returned keypairs count
-#define DB_COUNT_RECORDS(dbt, cnt) \
- do { \
- uint32_t *_p = (uint32_t *) ((uint8_t *)(dbt)->data + \
- (dbt)->ulen - sizeof(uint32_t)); \
- cnt = 0; \
- while(*_p != (uint32_t) -1) { \
- _p -= 2; \
- ++cnt; \
- } \
- } while(0); \
-
- Dbt_copy<uint64_t> k(amount);
- Dbt_copy<uint32_t> v;
- uint64_t buflen = 0;
- uint64_t t_dbmul = 0;
- uint64_t t_dbscan = 0;
-
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
- else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
-
- db_recno_t num_elems = 0;
- cur->count(&num_elems, 0);
-
- if (max <= 1 && num_elems <= max)
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but output not found"));
-
- TIME_MEASURE_START(db2);
- if (max <= 1)
- {
- for (const uint64_t& index : offsets)
- {
- TIME_MEASURE_START(t_seek);
-
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
- else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
-
- for (uint64_t i = 0; i < index; ++i)
- cur->get(&k, &v, DB_NEXT_DUP);
-
- uint64_t glob_index = v;
-
- LOG_PRINT_L3("L0->v: " << glob_index);
- global_indices.push_back(glob_index);
-
- TIME_MEASURE_FINISH(t_seek);
- }
- }
- else
- {
- // setup a 256KB minimum buffer size
- uint32_t pagesize = 256 * 1024;
-
- // Retrieve only a suitable portion of the kvp data, up to somewhere near
- // the maximum offset value being retrieved
- buflen = (max + 1) * 4 * sizeof(uint64_t);
- buflen = ((buflen / pagesize) + ((buflen % pagesize) > 0 ? 1 : 0)) * pagesize;
-
- bool nomem = false;
- Dbt data;
-
- bool singlebuff = buflen <= m_buffer.get_buffer_size();
- buflen = buflen < m_buffer.get_buffer_size() ? buflen : m_buffer.get_buffer_size();
- bdb_safe_buffer_t::type buffer = nullptr;
- bdb_safe_buffer_autolock<bdb_safe_buffer_t> lock(m_buffer, buffer);
-
- data.set_data(buffer);
- data.set_ulen(buflen);
- data.set_size(buflen);
- data.set_flags(DB_DBT_USERMEM);
-
- uint32_t curcount = 0;
- uint32_t blockstart = 0;
- for (const uint64_t& index : offsets)
- {
- if (index >= num_elems)
- {
- LOG_PRINT_L1("Index: " << index << " Elems: " << num_elems << " partial results found for get_output_tx_and_index");
- break;
- }
-
- // fixme! for whatever reason, the first call to DB_MULTIPLE | DB_SET does not
- // retrieve the first value.
- if (index <= 1 || nomem)
- {
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- {
- throw1(OUTPUT_DNE("Attempting to get an output index by amount and amount index, but amount not found"));
- }
- else if (result)
- {
- throw0(DB_ERROR("DB error attempting to get an output"));
- }
-
- for (uint64_t i = 0; i < index; ++i)
- cur->get(&k, &v, DB_NEXT_DUP);
- }
- else
- {
- while (index >= curcount)
- {
- TIME_MEASURE_START(t_db1);
- try
- {
- cur->get(&k, &data, DB_MULTIPLE | (curcount == 0 ? DB_SET : DB_NEXT_DUP));
- blockstart = curcount;
-
- int count = 0;
- DB_COUNT_RECORDS((DBT * ) &data, count);
- curcount += count;
- }
- catch (const std::exception &e)
- {
- cur.close();
- throw0(DB_ERROR(std::string("Failed on DB_MULTIPLE: ").append(e.what()).c_str()));
- }
-
- TIME_MEASURE_FINISH(t_db1);
- t_dbmul += t_db1;
- if (singlebuff)
- break;
- }
-
- LOG_PRINT_L3("Records returned: " << curcount << " Index: " << index);
- TIME_MEASURE_START(t_db2);
- DBT *pdata = (DBT *) &data;
-
- uint8_t *value;
- uint64_t dlen = 0;
-
- void *pbase = ((uint8_t *) (pdata->data)) + pdata->ulen - sizeof(uint32_t);
- uint32_t *p = (uint32_t *) pbase;
- if (*p == (uint32_t) -1)
- {
- value = NULL;
- }
- else
- {
- p -= (index - blockstart) * 2; // index * 4 + 2; <- if DB_MULTIPLE_KEY
- value = (uint8_t *) pdata->data + *p--;
- dlen = *p--;
- if (value == (uint8_t *) pdata->data)
- value = NULL;
- }
-
- if (value != NULL)
- {
- v = dlen == sizeof(uint64_t) ? *((uint64_t *) value) : *((uint32_t *) value);
- }
- TIME_MEASURE_FINISH(t_db2);
- t_dbscan += t_db2;
- }
-
- uint64_t glob_index = v;
-
- LOG_PRINT_L3("L1->v: " << glob_index);
- global_indices.push_back(glob_index);
- }
- }
- TIME_MEASURE_FINISH(db2);
-
- cur.close();
-
- TIME_MEASURE_FINISH(txx);
-
- LOG_PRINT_L3("blen: " << buflen << " txx: " << txx << " db1: " << t_dbmul << " db2: " << t_dbscan);
-
-}
-
-void BlockchainBDB::get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- TIME_MEASURE_START(txx);
- outputs.clear();
-
- std::vector < uint64_t > global_indices;
- get_output_global_indices(amount, offsets, global_indices);
-
- TIME_MEASURE_START(db3);
- if (global_indices.size() > 0)
- {
- for (const uint64_t &index : global_indices)
- {
- Dbt_copy<uint32_t> k(index);
- Dbt_copy<output_data_t> v;
-
- auto get_result = m_output_keys->get(DB_DEFAULT_TX, &k, &v, 0);
- if (get_result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("output with given index not in db"));
- else if (get_result)
- throw0(DB_ERROR("DB error attempting to fetch output tx hash"));
-
- output_data_t data = *(output_data_t *) v.get_data();
- outputs.push_back(data);
- }
- }
-
- TIME_MEASURE_FINISH(txx);
- LOG_PRINT_L3("db3: " << db3);
-}
-
-void BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- std::vector < uint64_t > global_indices;
- get_output_global_indices(amount, offsets, global_indices);
-
- TIME_MEASURE_START(db3);
- if (global_indices.size() > 0)
- get_output_tx_and_index_from_global(global_indices, indices);
- TIME_MEASURE_FINISH(db3);
-
- LOG_PRINT_L3("db3: " << db3);
-}
-
-std::map<uint64_t, uint64_t>::BlockchainBDB::get_output_histogram(const std::vector<uint64_t> &amounts) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- throw1(DB_ERROR("Not implemented."));
-}
-
-void BlockchainBDB::check_hard_fork_info()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- if (m_hf_versions == nullptr)
- {
- LOG_PRINT_L0("hf versions DB not open, so not checking");
- return;
- }
-
- DB_BTREE_STAT* db_stat1, * db_stat2;
-
- // DB_FAST_STAT can apparently cause an incorrect number of records
- // to be returned. The flag should be set to 0 instead if this proves
- // to be the case.
-
- // Set txn to NULL and DB_FAST_STAT to zero (0) for reliability.
- m_blocks->stat(NULL, &db_stat1, 0);
- m_hf_versions->stat(NULL, &db_stat2, 0);
- if (db_stat1->bt_nkeys != db_stat2->bt_nkeys)
- {
- LOG_PRINT_L0("num blocks " << db_stat1->bt_nkeys << " != " << "num hf_versions " << db_stat2->bt_nkeys << " - will clear the two hard fork DBs");
-
- bdb_txn_safe txn;
- bdb_txn_safe* txn_ptr = &txn;
- if (m_write_txn)
- txn_ptr = m_write_txn;
- else
- {
- if (m_env->txn_begin(NULL, txn, 0))
- throw0(DB_ERROR("Failed to create a transaction for the db"));
- }
-
- try
- {
- uint32_t count;
- m_hf_starting_heights->truncate(*txn_ptr, &count, 0);
- LOG_PRINT_L0("hf_starting_heights count: " << count);
- m_hf_versions->truncate(*txn_ptr, &count, 0);
- LOG_PRINT_L0("hf_versions count: " << count);
-
- if (!m_write_txn)
- txn.commit();
- }
- catch (const std::exception& e)
- {
- throw0(DB_ERROR(std::string("Failed to clear two hard fork DBs: ").append(e.what()).c_str()));
- }
- }
- delete db_stat1;
- delete db_stat2;
-}
-
-void BlockchainBDB::drop_hard_fork_info()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- bdb_txn_safe txn;
- bdb_txn_safe* txn_ptr = &txn;
- if (m_write_txn)
- txn_ptr = m_write_txn;
- else
- {
- if (m_env->txn_begin(NULL, txn, 0))
- throw0(DB_ERROR("Failed to create a transaction for the db"));
- }
-
- try
- {
- m_hf_starting_heights->close(0);
- m_hf_versions->close(0);
- m_hf_starting_heights = nullptr;
- m_hf_versions = nullptr;
- if (m_env->dbremove(*txn_ptr, BDB_HF_STARTING_HEIGHTS, NULL, 0) != 0)
- LOG_ERROR("Error removing hf_starting_heights");
- if (m_env->dbremove(*txn_ptr, BDB_HF_VERSIONS, NULL, 0) != 0)
- LOG_ERROR("Error removing hf_versions");
-
- if (!m_write_txn)
- txn.commit();
- }
- catch (const std::exception& e)
- {
- throw0(DB_ERROR(std::string("Failed to drop hard fork info: ").append(e.what()).c_str()));
- }
-}
-
-void BlockchainBDB::set_hard_fork_version(uint64_t height, uint8_t version)
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> val_key(height + 1);
- Dbt_copy<uint8_t> val(version);
- if (m_hf_versions->put(DB_DEFAULT_TX, &val_key, &val, 0))
- throw1(DB_ERROR("Error adding hard fork version to db transaction."));
-}
-
-uint8_t BlockchainBDB::get_hard_fork_version(uint64_t height) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
-
- Dbt_copy<uint32_t> key(height + 1);
- Dbt_copy<uint8_t> result;
-
- auto get_result = m_hf_versions->get(DB_DEFAULT_TX, &key, &result, 0);
- if (get_result == DB_NOTFOUND || get_result == DB_KEYEMPTY)
- throw0(OUTPUT_DNE("Error attempting to retrieve hard fork version from the db"));
- else if (get_result)
- throw0(DB_ERROR("Error attempting to retrieve hard fork version from the db"));
-
- return result;
-}
-
-void BlockchainBDB::checkpoint_worker() const
-{
- LOG_PRINT_L0("Entering BDB checkpoint thread.");
- int count = 0;
- while(m_run_checkpoint && m_open)
- {
- // sleep every second, so we don't delay exit condition m_run_checkpoint = false
- sleep(1);
- // checkpoint every 5 minutes
- if(count++ >= 300)
- {
- count = 0;
- if(m_env->txn_checkpoint(0, 0, 0) != 0)
- {
- LOG_PRINT_L0("BDB txn_checkpoint failed.");
- break;
- }
- }
- }
- LOG_PRINT_L0("Leaving BDB checkpoint thread.");
-}
-
-bool BlockchainBDB::is_read_only() const
-{
- return false;
-}
-
-void BlockchainBDB::fixup()
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- // Always call parent as well
- BlockchainDB::fixup();
-}
-
-} // namespace cryptonote
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
deleted file mode 100644
index 3ae90efe1..000000000
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ /dev/null
@@ -1,452 +0,0 @@
-// Copyright (c) 2014-2019, 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 <db_cxx.h>
-
-#include "blockchain_db/blockchain_db.h"
-#include "cryptonote_basic/blobdatatype.h" // for type blobdata
-
-#include <unordered_map>
-#include <condition_variable>
-
-// ND: Enables multi-threaded bulk reads for when getting indices.
-// TODO: Disabled for now, as it doesn't seem to provide noticeable improvements (??. Reason: TBD.
-// #define BDB_BULK_CAN_THREAD
-namespace cryptonote
-{
-
-struct bdb_txn_safe
-{
- bdb_txn_safe() : m_txn(NULL) { }
- ~bdb_txn_safe()
- {
- LOG_PRINT_L3("bdb_txn_safe: destructor");
-
- if (m_txn != NULL)
- abort();
- }
-
- void commit(std::string message = "")
- {
- if (message.size() == 0)
- {
- message = "Failed to commit a transaction to the db";
- }
-
- if (m_txn->commit(0))
- {
- m_txn = NULL;
- LOG_PRINT_L0(message);
- throw DB_ERROR(message.c_str());
- }
- m_txn = NULL;
- }
-
- void abort()
- {
- LOG_PRINT_L3("bdb_txn_safe: abort()");
- if(m_txn != NULL)
- {
- m_txn->abort();
- m_txn = NULL;
- }
- else
- {
- LOG_PRINT_L0("WARNING: bdb_txn_safe: abort() called, but m_txn is NULL");
- }
- }
-
- operator DbTxn*()
- {
- return m_txn;
- }
-
- operator DbTxn**()
- {
- return &m_txn;
- }
-private:
- DbTxn* m_txn;
-};
-
-// ND: Class to handle buffer management when doing bulk queries
-// (DB_MULTIPLE). Allocates buffers then handles thread queuing
-// so a fixed set of buffers can be used (instead of allocating
-// every time a bulk query is needed).
-template <typename T>
-class bdb_safe_buffer
-{
- // limit the number of buffers to 8
- const size_t MaxAllowedBuffers = 8;
-public:
- bdb_safe_buffer(size_t num_buffers, size_t count)
- {
- if(num_buffers > MaxAllowedBuffers)
- num_buffers = MaxAllowedBuffers;
-
- set_count(num_buffers);
- for (size_t i = 0; i < num_buffers; i++)
- m_buffers.push_back((T) malloc(sizeof(T) * count));
- m_buffer_count = count;
- }
-
- ~bdb_safe_buffer()
- {
- for (size_t i = 0; i < m_buffers.size(); i++)
- {
- if (m_buffers[i])
- {
- free(m_buffers[i]);
- m_buffers[i] = nullptr;
- }
- }
-
- m_buffers.resize(0);
- }
-
- T acquire_buffer()
- {
- boost::unique_lock<boost::mutex> lock(m_lock);
- m_cv.wait(lock, [&]{ return m_count > 0; });
-
- --m_count;
- size_t index = -1;
- for (size_t i = 0; i < m_open_slot.size(); i++)
- {
- if (m_open_slot[i])
- {
- m_open_slot[i] = false;
- index = i;
- break;
- }
- }
-
- assert(index >= 0);
-
- T buffer = m_buffers[index];
- m_buffer_map.emplace(buffer, index);
- return buffer;
- }
-
- void release_buffer(T buffer)
- {
- boost::unique_lock<boost::mutex> lock(m_lock);
-
- assert(buffer != nullptr);
- auto it = m_buffer_map.find(buffer);
- if (it != m_buffer_map.end())
- {
- auto index = it->second;
-
- assert(index < m_open_slot.size());
- assert(m_open_slot[index] == false);
- assert(m_count < m_open_slot.size());
-
- ++m_count;
- m_open_slot[index] = true;
- m_buffer_map.erase(it);
- m_cv.notify_one();
- }
- }
-
- size_t get_buffer_size() const
- {
- return m_buffer_count * sizeof(T);
- }
-
- size_t get_buffer_count() const
- {
- return m_buffer_count;
- }
-
- typedef T type;
-
-private:
- void set_count(size_t count)
- {
- assert(count > 0);
- m_open_slot.resize(count, true);
- m_count = count;
- }
-
- std::vector<T> m_buffers;
- std::unordered_map<T, size_t> m_buffer_map;
-
- boost::condition_variable m_cv;
- std::vector<bool> m_open_slot;
- size_t m_count;
- boost::mutex m_lock;
-
- size_t m_buffer_count;
-};
-
-template <typename T>
-class bdb_safe_buffer_autolock
-{
-public:
- bdb_safe_buffer_autolock(T &safe_buffer, typename T::type &buffer) :
- m_safe_buffer(safe_buffer), m_buffer(nullptr)
- {
- m_buffer = m_safe_buffer.acquire_buffer();
- buffer = m_buffer;
- }
-
- ~bdb_safe_buffer_autolock()
- {
- if (m_buffer != nullptr)
- {
- m_safe_buffer.release_buffer(m_buffer);
- m_buffer = nullptr;
- }
- }
-private:
- T &m_safe_buffer;
- typename T::type m_buffer;
-};
-
-class BlockchainBDB : public BlockchainDB
-{
-public:
- BlockchainBDB(bool batch_transactions=false);
- ~BlockchainBDB();
-
- virtual void open(const std::string& filename, const int db_flags);
-
- virtual void close();
-
- virtual void sync();
-
- virtual void reset();
-
- virtual std::vector<std::string> get_filenames() const;
-
- virtual bool remove_data_file(const std::string& folder);
-
- virtual std::string get_db_name() const;
-
- virtual bool lock();
-
- virtual void unlock();
-
- 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 uint64_t get_block_timestamp(const uint64_t& height) const;
-
- virtual uint64_t get_top_block_timestamp() const;
-
- virtual size_t get_block_weight(const uint64_t& height) const;
-
- virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const;
-
- virtual difficulty_type get_block_difficulty(const uint64_t& height) const;
-
- virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const;
-
- virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const;
-
- virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const;
-
- virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const;
-
- virtual crypto::hash top_block_hash() const;
-
- virtual block get_top_block() const;
-
- virtual uint64_t height() const;
-
- virtual bool tx_exists(const crypto::hash& h) const;
-
- virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const;
-
- virtual transaction get_tx(const crypto::hash& h) const;
-
- virtual uint64_t get_tx_count() const;
-
- virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const;
-
- virtual uint64_t get_tx_block_height(const crypto::hash& h) const;
-
- virtual uint64_t get_num_outputs(const uint64_t& amount) const;
-
- virtual uint64_t get_indexing_base() const { return 1; }
-
- virtual output_data_t get_output_key(const uint64_t& amount, const uint64_t& index);
- virtual void get_output_key(const uint64_t &amount, const std::vector<uint64_t> &offsets, std::vector<output_data_t> &outputs);
-
- 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,
- std::vector<tx_out_index> &tx_out_indices) const;
-
- virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index);
- virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices);
-
- virtual std::vector<uint64_t> get_tx_amount_output_indices(const crypto::hash& h) const;
-
- virtual bool has_key_image(const crypto::key_image& img) const;
-
- virtual uint64_t add_block( const block& blk
- , size_t block_weight
- , const difficulty_type& cumulative_difficulty
- , const uint64_t& coins_generated
- , const std::vector<transaction>& txs
- );
-
- virtual void set_batch_transactions(bool batch_transactions);
- virtual bool batch_start(uint64_t batch_num_blocks=0);
- virtual void batch_commit();
- virtual void batch_stop();
- virtual void batch_abort();
-
- virtual void block_txn_start(bool readonly);
- virtual void block_txn_stop();
- virtual void block_txn_abort();
-
- virtual void pop_block(block& blk, std::vector<transaction>& txs);
-
-#if defined(BDB_BULK_CAN_THREAD)
- virtual bool can_thread_bulk_indices() const { return true; }
-#else
- virtual bool can_thread_bulk_indices() const { return false; }
-#endif
-
- /**
- * @brief return a histogram of outputs on the blockchain
- *
- * @param amounts optional set of amounts to lookup
- *
- * @return a set of amount/instances
- */
- std::map<uint64_t, uint64_t> get_output_histogram(const std::vector<uint64_t> &amounts) const;
-
-private:
- virtual void add_block( const block& blk
- , size_t block_weight
- , const difficulty_type& cumulative_difficulty
- , const uint64_t& coins_generated
- , const crypto::hash& block_hash
- );
-
- virtual void remove_block();
-
- virtual void add_transaction_data(const crypto::hash& blk_hash, const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash);
-
- virtual void remove_transaction_data(const crypto::hash& tx_hash, const transaction& tx);
-
- virtual void add_output(const crypto::hash& tx_hash, const tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment);
-
- virtual void remove_output(const tx_out& tx_output);
-
- void remove_tx_outputs(const crypto::hash& tx_hash, const transaction& tx);
-
- void remove_output(const uint64_t& out_index, const uint64_t amount);
- void remove_amount_output_index(const uint64_t amount, const uint64_t global_output_index);
-
- virtual void add_spent_key(const crypto::key_image& k_image);
-
- virtual void remove_spent_key(const crypto::key_image& k_image);
-
- void get_output_global_indices(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<uint64_t> &global_indices);
-
- virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
- virtual bool for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const;
- virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const;
- virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const;
-
- // Hard fork related storage
- virtual void set_hard_fork_version(uint64_t height, uint8_t version);
- virtual uint8_t get_hard_fork_version(uint64_t height) const;
- virtual void check_hard_fork_info();
- virtual void drop_hard_fork_info();
-
- /**
- * @brief get the global index of the index-th output of the given amount
- *
- * @param amount the output amount
- * @param index the index into the set of outputs of that amount
- *
- * @return the global index of the desired output
- */
- uint64_t get_output_global_index(const uint64_t& amount, const uint64_t& index);
- output_data_t get_output_key(const uint64_t& global_index) const;
- void checkpoint_worker() const;
- void check_open() const;
-
- virtual bool is_read_only() const;
-
- //
- // fix up anything that may be wrong due to past bugs
- virtual void fixup();
-
- bool m_run_checkpoint;
- std::unique_ptr<boost::thread> m_checkpoint_thread;
- typedef bdb_safe_buffer<void *> bdb_safe_buffer_t;
- bdb_safe_buffer_t m_buffer;
-
- DbEnv* m_env;
-
- Db* m_blocks;
- Db* m_block_heights;
- Db* m_block_hashes;
- Db* m_block_timestamps;
- Db* m_block_sizes;
- Db* m_block_diffs;
- Db* m_block_coins;
-
- Db* m_txs;
- Db* m_tx_unlocks;
- Db* m_tx_heights;
- Db* m_tx_outputs;
-
- Db* m_output_txs;
- Db* m_output_indices;
- Db* m_output_amounts;
- Db* m_output_keys;
-
- Db* m_spent_keys;
-
- Db* m_hf_starting_heights;
- Db* m_hf_versions;
-
- Db* m_properties;
-
- uint64_t m_height;
- uint64_t m_num_outputs;
- std::string m_folder;
- bdb_txn_safe *m_write_txn;
-
- bool m_batch_transactions; // support for batch transactions
-};
-
-} // namespace cryptonote
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 2b039f557..1a6a19da5 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -35,17 +35,6 @@
#include "ringct/rctOps.h"
#include "lmdb/db_lmdb.h"
-#ifdef BERKELEY_DB
-#include "berkeleydb/db_bdb.h"
-#endif
-
-static const char *db_types[] = {
- "lmdb",
-#ifdef BERKELEY_DB
- "berkeley",
-#endif
- NULL
-};
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain.db"
@@ -55,36 +44,71 @@ using epee::string_tools::pod_to_hex;
namespace cryptonote
{
-bool blockchain_valid_db_type(const std::string& db_type)
+bool matches_category(relay_method method, relay_category category) noexcept
{
- int i;
- for (i=0; db_types[i]; i++)
+ switch (category)
{
- if (db_types[i] == db_type)
+ default:
+ return false;
+ case relay_category::all:
+ return true;
+ case relay_category::relayable:
+ if (method == relay_method::none)
+ return false;
return true;
+ case relay_category::broadcasted:
+ case relay_category::legacy:
+ break;
}
- return false;
+ // check for "broadcasted" or "legacy" methods:
+ switch (method)
+ {
+ default:
+ case relay_method::local:
+ return false;
+ case relay_method::block:
+ case relay_method::fluff:
+ return true;
+ case relay_method::none:
+ break;
+ }
+ return category == relay_category::legacy;
}
-std::string blockchain_db_types(const std::string& sep)
+void txpool_tx_meta_t::set_relay_method(relay_method method) noexcept
{
- int i;
- std::string ret = "";
- for (i=0; db_types[i]; i++)
+ kept_by_block = 0;
+ do_not_relay = 0;
+ is_local = 0;
+
+ switch (method)
{
- if (i)
- ret += sep;
- ret += db_types[i];
+ case relay_method::none:
+ do_not_relay = 1;
+ break;
+ case relay_method::local:
+ is_local = 1;
+ break;
+ default:
+ case relay_method::fluff:
+ break;
+ case relay_method::block:
+ kept_by_block = 1;
+ break;
}
- return ret;
}
-std::string arg_db_type_description = "Specify database type, available: " + cryptonote::blockchain_db_types(", ");
-const command_line::arg_descriptor<std::string> arg_db_type = {
- "db-type"
-, arg_db_type_description.c_str()
-, DEFAULT_DB_TYPE
-};
+relay_method txpool_tx_meta_t::get_relay_method() const noexcept
+{
+ if (kept_by_block)
+ return relay_method::block;
+ if (do_not_relay)
+ return relay_method::none;
+ if (is_local)
+ return relay_method::local;
+ return relay_method::fluff;
+}
+
const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
"db-sync-mode"
, "Specify sync option, using format [safe|fast|fastest]:[sync|async]:[<nblocks_per_sync>[blocks]|<nbytes_per_sync>[bytes]]."
@@ -96,20 +120,13 @@ const command_line::arg_descriptor<bool> arg_db_salvage = {
, false
};
-BlockchainDB *new_db(const std::string& db_type)
+BlockchainDB *new_db()
{
- if (db_type == "lmdb")
- return new BlockchainLMDB();
-#if defined(BERKELEY_DB)
- if (db_type == "berkeley")
- return new BlockchainBDB();
-#endif
- return NULL;
+ return new BlockchainLMDB();
}
void BlockchainDB::init_options(boost::program_options::options_description& desc)
{
- command_line::add_arg(desc, arg_db_type);
command_line::add_arg(desc, arg_db_sync_mode);
command_line::add_arg(desc, arg_db_salvage);
}
@@ -972,4 +989,23 @@ void BlockchainDB::fixup()
batch_stop();
}
+bool BlockchainDB::txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category)
+{
+ try
+ {
+ txpool_tx_meta_t meta{};
+ if (!get_txpool_tx_meta(tx_hash, meta))
+ {
+ MERROR("Failed to get tx meta from txpool");
+ return false;
+ }
+ return meta.matches(category);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to get tx meta from txpool: " << e.what());
+ }
+ return false;
+}
+
} // namespace cryptonote
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index bb4de3ce6..acd7976a8 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -39,6 +39,7 @@
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/difficulty.h"
#include "cryptonote_basic/hardfork.h"
+#include "cryptonote_protocol/enums.h"
/** \file
* Cryptonote Blockchain Database Interface
@@ -102,10 +103,19 @@ namespace cryptonote
/** a pair of <transaction hash, output index>, typedef for convenience */
typedef std::pair<crypto::hash, uint64_t> tx_out_index;
-extern const command_line::arg_descriptor<std::string> arg_db_type;
extern const command_line::arg_descriptor<std::string> arg_db_sync_mode;
extern const command_line::arg_descriptor<bool, false> arg_db_salvage;
+enum class relay_category : uint8_t
+{
+ broadcasted = 0,//!< Public txes received via block/fluff
+ relayable, //!< Every tx not marked `relay_method::none`
+ legacy, //!< `relay_category::broadcasted` + `relay_method::none` for rpc relay requests or historical reasons
+ all //!< Everything in the db
+};
+
+bool matches_category(relay_method method, relay_category category) noexcept;
+
#pragma pack(push, 1)
/**
@@ -156,11 +166,23 @@ struct txpool_tx_meta_t
uint8_t relayed;
uint8_t do_not_relay;
uint8_t double_spend_seen: 1;
- uint8_t bf_padding: 7;
+ uint8_t pruned: 1;
+ uint8_t is_local: 1;
+ uint8_t bf_padding: 5;
uint8_t padding[76]; // till 192 bytes
+
+ void set_relay_method(relay_method method) noexcept;
+ relay_method get_relay_method() const noexcept;
+
+ //! See `relay_category` description
+ bool matches(const relay_category category) const noexcept
+ {
+ return matches_category(get_relay_method(), category);
+ }
};
+
#define DBF_SAFE 1
#define DBF_FAST 2
#define DBF_FASTEST 4
@@ -1465,12 +1487,12 @@ public:
/**
* @brief get the number of transactions in the txpool
*/
- virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const = 0;
+ virtual uint64_t get_txpool_tx_count(relay_category tx_category = relay_category::broadcasted) const = 0;
/**
- * @brief check whether a txid is in the txpool
+ * @brief check whether a txid is in the txpool and meets tx_category requirements
*/
- virtual bool txpool_has_tx(const crypto::hash &txid) const = 0;
+ virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const = 0;
/**
* @brief remove a txpool transaction
@@ -1494,10 +1516,11 @@ public:
*
* @param txid the transaction id of the transation to lookup
* @param bd the blob to return
+ * @param tx_category for filtering out hidden/private txes
*
- * @return true if the txid was in the txpool, false otherwise
+ * @return True iff `txid` is in the pool and meets `tx_category` requirements
*/
- virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const = 0;
+ virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const = 0;
/**
* @brief get a txpool transaction's blob
@@ -1506,7 +1529,17 @@ public:
*
* @return the blob for that transaction
*/
- virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const = 0;
+ virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const = 0;
+
+ /**
+ * @brief Check if `tx_hash` relay status is in `category`.
+ *
+ * @param tx_hash hash of the transaction to lookup
+ * @param category relay status category to test against
+ *
+ * @return True if `tx_hash` latest relay status is in `category`.
+ */
+ bool txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category);
/**
* @brief prune output data for the given amount
@@ -1604,7 +1637,7 @@ public:
*
* @return false if the function returns false for any transaction, otherwise true
*/
- virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const = 0;
+ virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const = 0;
/**
* @brief runs a function over all key images stored
@@ -1826,7 +1859,7 @@ private:
class db_rtxn_guard: public db_txn_guard { public: db_rtxn_guard(BlockchainDB *db): db_txn_guard(db, true) {} };
class db_wtxn_guard: public db_txn_guard { public: db_wtxn_guard(BlockchainDB *db): db_txn_guard(db, false) {} };
-BlockchainDB *new_db(const std::string& db_type);
+BlockchainDB *new_db();
} // namespace cryptonote
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 760e380a9..6eb5501b7 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -163,7 +163,15 @@ int BlockchainLMDB::compare_string(const MDB_val *a, const MDB_val *b)
{
const char *va = (const char*) a->mv_data;
const char *vb = (const char*) b->mv_data;
- return strcmp(va, vb);
+ const size_t sz = std::min(a->mv_size, b->mv_size);
+ int ret = strncmp(va, vb, sz);
+ if (ret)
+ return ret;
+ if (a->mv_size < b->mv_size)
+ return -1;
+ if (a->mv_size > b->mv_size)
+ return 1;
+ return 0;
}
}
@@ -1771,7 +1779,7 @@ void BlockchainLMDB::update_txpool_tx(const crypto::hash &txid, const txpool_tx_
}
}
-uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const
+uint64_t BlockchainLMDB::get_txpool_tx_count(relay_category category) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1781,7 +1789,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const
TXN_PREFIX_RDONLY();
- if (include_unrelayed_txes)
+ if (category == relay_category::all)
{
// No filtering, we can get the number of tx the "fast" way
MDB_stat db_stats;
@@ -1807,7 +1815,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const
if (result)
throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str()));
const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data;
- if (!meta.do_not_relay)
+ if (meta.matches(category))
++num_entries;
}
}
@@ -1816,7 +1824,7 @@ uint64_t BlockchainLMDB::get_txpool_tx_count(bool include_unrelayed_txes) const
return num_entries;
}
-bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const
+bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid, relay_category tx_category) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1825,11 +1833,21 @@ bool BlockchainLMDB::txpool_has_tx(const crypto::hash& txid) const
RCURSOR(txpool_meta)
MDB_val k = {sizeof(txid), (void *)&txid};
- auto result = mdb_cursor_get(m_cur_txpool_meta, &k, NULL, MDB_SET);
+ MDB_val v;
+ auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET);
if (result != 0 && result != MDB_NOTFOUND)
throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str()));
+ if (result == MDB_NOTFOUND)
+ return false;
+
+ bool found = true;
+ if (tx_category != relay_category::all)
+ {
+ const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data;
+ found = meta.matches(tx_category);
+ }
TXN_POSTFIX_RDONLY();
- return result != MDB_NOTFOUND;
+ return found;
}
void BlockchainLMDB::remove_txpool_tx(const crypto::hash& txid)
@@ -1883,7 +1901,7 @@ bool BlockchainLMDB::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta
return true;
}
-bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const
+bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1893,6 +1911,21 @@ bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::bl
MDB_val k = {sizeof(txid), (void *)&txid};
MDB_val v;
+
+ // if filtering, make sure those requirements are met before copying blob
+ if (tx_category != relay_category::all)
+ {
+ auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ return false;
+ if (result != 0)
+ throw1(DB_ERROR(lmdb_error("Error finding txpool tx meta: ", result).c_str()));
+
+ const txpool_tx_meta_t& meta = *(const txpool_tx_meta_t*)v.mv_data;
+ if (!meta.matches(tx_category))
+ return false;
+ }
+
auto result = mdb_cursor_get(m_cur_txpool_blob, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
return false;
@@ -1904,10 +1937,10 @@ bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::bl
return true;
}
-cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid) const
+cryptonote::blobdata BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const
{
cryptonote::blobdata bd;
- if (!get_txpool_tx_blob(txid, bd))
+ if (!get_txpool_tx_blob(txid, bd, tx_category))
throw1(DB_ERROR("Tx not found in txpool: "));
return bd;
}
@@ -2056,7 +2089,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
++n_prunable_records;
result = mdb_cursor_get(c_txs_prunable, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
- MWARNING("Already pruned at height " << block_height << "/" << blockchain_height);
+ MDEBUG("Already pruned at height " << block_height << "/" << blockchain_height);
else if (result)
throw0(DB_ERROR(lmdb_error("Failed to find transaction prunable data: ", result).c_str()));
else
@@ -2152,7 +2185,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
{
++n_prunable_records;
if (result == MDB_NOTFOUND)
- MWARNING("Already pruned at height " << block_height << "/" << blockchain_height);
+ MDEBUG("Already pruned at height " << block_height << "/" << blockchain_height);
else
{
MDEBUG("Pruning at height " << block_height << "/" << blockchain_height);
@@ -2245,7 +2278,7 @@ bool BlockchainLMDB::check_pruning()
return prune_worker(prune_mode_check, 0);
}
-bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const
+bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, relay_category category) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -2269,8 +2302,7 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&,
throw0(DB_ERROR(lmdb_error("Failed to enumerate txpool tx metadata: ", result).c_str()));
const crypto::hash txid = *(const crypto::hash*)k.mv_data;
const txpool_tx_meta_t &meta = *(const txpool_tx_meta_t*)v.mv_data;
- if (!include_unrelayed_txes && meta.do_not_relay)
- // Skipping that tx
+ if (!meta.matches(category))
continue;
const cryptonote::blobdata *passed_bd = NULL;
cryptonote::blobdata bd;
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 61a551476..e56711e8f 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -281,12 +281,12 @@ public:
virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t& meta);
virtual void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t& meta);
- virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
- virtual bool txpool_has_tx(const crypto::hash &txid) const;
+ virtual uint64_t get_txpool_tx_count(relay_category category = relay_category::broadcasted) const;
+ virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const;
virtual void remove_txpool_tx(const crypto::hash& txid);
virtual bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const;
- virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
- virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
+ virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata& bd, relay_category tx_category) const;
+ virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const;
virtual uint32_t get_blockchain_pruning_seed() const;
virtual bool prune_blockchain(uint32_t pruning_seed = 0);
virtual bool update_pruning();
@@ -298,7 +298,7 @@ public:
virtual uint64_t get_alt_block_count();
virtual void drop_alt_blocks();
- virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const;
+ virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, relay_category category = relay_category::broadcasted) const;
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const;
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index ac19fae25..a5847dec6 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -126,14 +126,14 @@ public:
virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) override {}
virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) override {}
- virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override { return 0; }
- virtual bool txpool_has_tx(const crypto::hash &txid) const override { return false; }
+ virtual uint64_t get_txpool_tx_count(relay_category tx_relay = relay_category::broadcasted) const override { return 0; }
+ virtual bool txpool_has_tx(const crypto::hash &txid, relay_category tx_category) const override { return false; }
virtual void remove_txpool_tx(const crypto::hash& txid) override {}
virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const override { return false; }
- virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const override { return false; }
+ virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const override { return false; }
virtual uint64_t get_database_size() const override { return 0; }
- virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const override { return ""; }
- virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const override { return false; }
+ virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const override { return ""; }
+ virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const override { return false; }
virtual void add_block( const cryptonote::block& blk
, size_t block_weight
diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp
index a6ee0573f..db6d6f7d7 100644
--- a/src/blockchain_utilities/blockchain_ancestry.cpp
+++ b/src/blockchain_utilities/blockchain_ancestry.cpp
@@ -40,7 +40,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -336,11 +335,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
tools::on_startup();
@@ -350,9 +344,6 @@ 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_log_level = {"log-level", "0-4 or categories", ""};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<std::string> arg_txid = {"txid", "Get ancestry for this txid", ""};
const command_line::arg_descriptor<std::string> arg_output = {"output", "Get ancestry for this output (amount/offset format)", ""};
const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get ancestry for all txes at this height", 0};
@@ -367,7 +358,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_txid);
command_line::add_arg(desc_cmd_sett, arg_output);
command_line::add_arg(desc_cmd_sett, arg_height);
@@ -446,13 +436,6 @@ int main(int argc, char* argv[])
}
}
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
-
// If we wanted to use the memory pool, we would set up a fake_core.
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
@@ -468,13 +451,13 @@ int main(int argc, char* argv[])
std::unique_ptr<Blockchain> core_storage;
tx_memory_pool m_mempool(*core_storage);
core_storage.reset(new Blockchain(m_mempool));
- BlockchainDB *db = new_db(db_type);
+ BlockchainDB *db = new_db();
if (db == NULL)
{
- LOG_ERROR("Attempted to use non-existent database type: " << db_type);
- throw std::runtime_error("Attempting to use non-existent database type");
+ LOG_ERROR("Failed to initialize a database");
+ throw std::runtime_error("Failed to initialize a database");
}
- LOG_PRINT_L0("database: " << db_type);
+ LOG_PRINT_L0("database: LMDB");
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp
index 857e97afd..4a5712921 100644
--- a/src/blockchain_utilities/blockchain_blackball.cpp
+++ b/src/blockchain_utilities/blockchain_blackball.cpp
@@ -39,7 +39,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/db_types.h"
#include "wallet/ringdb.h"
#include "version.h"
@@ -1170,11 +1169,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
tools::on_startup();
@@ -1188,9 +1182,6 @@ int main(int argc, char* argv[])
get_default_db_path(),
};
const command_line::arg_descriptor<std::string> arg_log_level = {"log-level", "0-4 or categories", ""};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<bool> arg_rct_only = {"rct-only", "Only work on ringCT outputs", false};
const command_line::arg_descriptor<bool> arg_check_subsets = {"check-subsets", "Check ring subsets (very expensive)", false};
const command_line::arg_descriptor<bool> arg_verbose = {"verbose", "Verbose output)", false};
@@ -1207,7 +1198,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, arg_blackball_db_dir);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_rct_only);
command_line::add_arg(desc_cmd_sett, arg_check_subsets);
command_line::add_arg(desc_cmd_sett, arg_verbose);
@@ -1261,12 +1251,6 @@ int main(int argc, char* argv[])
std::string extra_spent_list = command_line::get_arg(vm, arg_extra_spent_list);
std::vector<std::pair<uint64_t, uint64_t>> extra_spent_outputs = extra_spent_list.empty() ? std::vector<std::pair<uint64_t, uint64_t>>() : load_outputs(extra_spent_list);
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
std::string db_sync_mode = command_line::get_arg(vm, arg_db_sync_mode);
if (!parse_db_sync_mode(db_sync_mode))
diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp
index 8be83ee67..153f5f7c6 100644
--- a/src/blockchain_utilities/blockchain_depth.cpp
+++ b/src/blockchain_utilities/blockchain_depth.cpp
@@ -34,7 +34,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -50,11 +49,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
tools::on_startup();
@@ -64,9 +58,6 @@ 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_log_level = {"log-level", "0-4 or categories", ""};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<std::string> arg_txid = {"txid", "Get min depth for this txid", ""};
const command_line::arg_descriptor<uint64_t> arg_height = {"height", "Get min depth for all txes at this height", 0};
const command_line::arg_descriptor<bool> arg_include_coinbase = {"include-coinbase", "Include coinbase in the average", false};
@@ -75,7 +66,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_txid);
command_line::add_arg(desc_cmd_sett, arg_height);
command_line::add_arg(desc_cmd_sett, arg_include_coinbase);
@@ -133,13 +123,6 @@ int main(int argc, char* argv[])
}
}
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
-
// If we wanted to use the memory pool, we would set up a fake_core.
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
@@ -155,13 +138,13 @@ int main(int argc, char* argv[])
std::unique_ptr<Blockchain> core_storage;
tx_memory_pool m_mempool(*core_storage);
core_storage.reset(new Blockchain(m_mempool));
- BlockchainDB *db = new_db(db_type);
+ BlockchainDB *db = new_db();
if (db == NULL)
{
- LOG_ERROR("Attempted to use non-existent database type: " << db_type);
- throw std::runtime_error("Attempting to use non-existent database type");
+ LOG_ERROR("Failed to initialize a database");
+ throw std::runtime_error("Failed to initialize a database");
}
- LOG_PRINT_L0("database: " << db_type);
+ LOG_PRINT_L0("database: LMDB");
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index 85566efca..b180f88de 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -32,7 +32,6 @@
#include "cryptonote_core/tx_pool.h"
#include "cryptonote_core/cryptonote_core.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -47,11 +46,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
uint64_t block_stop = 0;
bool blocks_dat = false;
@@ -65,9 +59,6 @@ int main(int argc, char* argv[])
const command_line::arg_descriptor<std::string> arg_output_file = {"output-file", "Specify output file", "", true};
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<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<bool> arg_blocks_dat = {"blocksdat", "Output in blocks.dat format", blocks_dat};
@@ -76,7 +67,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_block_stop);
command_line::add_arg(desc_cmd_sett, arg_blocks_dat);
@@ -124,13 +114,6 @@ int main(int argc, char* argv[])
m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
-
if (command_line::has_arg(vm, arg_output_file))
output_file_path = boost::filesystem::path(command_line::get_arg(vm, arg_output_file));
else
@@ -153,13 +136,13 @@ int main(int argc, char* argv[])
tx_memory_pool m_mempool(*core_storage);
core_storage = new Blockchain(m_mempool);
- BlockchainDB* db = new_db(db_type);
+ BlockchainDB* db = new_db();
if (db == NULL)
{
- LOG_ERROR("Attempted to use non-existent database type: " << db_type);
- throw std::runtime_error("Attempting to use non-existent database type");
+ LOG_ERROR("Failed to initialize a database");
+ throw std::runtime_error("Failed to initialize a database");
}
- LOG_PRINT_L0("database: " << db_type);
+ LOG_PRINT_L0("database: LMDB");
boost::filesystem::path folder(m_config_folder);
folder /= db->get_db_name();
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index cb9154f29..852e9cf4f 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -42,7 +42,6 @@
#include "serialization/binary_utils.h" // dump_binary(), parse_binary()
#include "serialization/json_utils.h" // dump_json()
#include "include_base_utils.h"
-#include "blockchain_db/db_types.h"
#include "cryptonote_core/cryptonote_core.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -93,44 +92,6 @@ int get_db_flags_from_mode(const std::string& db_mode)
return db_flags;
}
-int parse_db_arguments(const std::string& db_arg_str, std::string& db_type, int& db_flags)
-{
- std::vector<std::string> db_args;
- boost::split(db_args, db_arg_str, boost::is_any_of("#"));
- db_type = db_args.front();
- boost::algorithm::trim(db_type);
-
- if (db_args.size() == 1)
- {
- return 0;
- }
- else if (db_args.size() > 2)
- {
- std::cerr << "unrecognized database argument format: " << db_arg_str << ENDL;
- return 1;
- }
-
- std::string db_arg_str2 = db_args[1];
- boost::split(db_args, db_arg_str2, boost::is_any_of(","));
-
- // optionally use a composite mode instead of individual flags
- const std::unordered_set<std::string> db_modes {"safe", "fast", "fastest"};
- std::string db_mode;
- if (db_args.size() == 1)
- {
- if (db_modes.count(db_args[0]) > 0)
- {
- db_mode = db_args[0];
- }
- }
- if (! db_mode.empty())
- {
- db_flags = get_db_flags_from_mode(db_mode);
- }
- return 0;
-}
-
-
int pop_blocks(cryptonote::core& core, int num_blocks)
{
bool use_batch = opt_batch;
@@ -191,7 +152,7 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
}
hashes.push_back(cryptonote::get_block_hash(block));
}
- core.prevalidate_block_hashes(core.get_blockchain_storage().get_db().height(), hashes);
+ core.prevalidate_block_hashes(core.get_blockchain_storage().get_db().height(), hashes, {});
std::vector<block> pblocks;
if (!core.prepare_handle_incoming_blocks(blocks, pblocks))
@@ -213,11 +174,11 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
for(auto& tx_blob: block_entry.txs)
{
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- core.handle_incoming_tx(tx_blob, tvc, true, true, false);
+ core.handle_incoming_tx(tx_blob, tvc, relay_method::block, true);
if(tvc.m_verifivation_failed)
{
MERROR("transaction verification failed, tx_id = "
- << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob)));
+ << epee::string_tools::pod_to_hex(get_blob_hash(tx_blob.blob)));
core.cleanup_handle_incoming_blocks();
return 1;
}
@@ -225,7 +186,7 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
// process block
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
core.handle_incoming_block(block_entry.block, pblocks.empty() ? NULL : &pblocks[blockidx++], bvc, false); // <--- process block
@@ -468,13 +429,17 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
{
cryptonote::blobdata block;
cryptonote::block_to_blob(bp.block, block);
- std::vector<cryptonote::blobdata> txs;
+ std::vector<tx_blob_entry> txs;
for (const auto &tx: bp.txs)
{
- txs.push_back(cryptonote::blobdata());
- cryptonote::tx_to_blob(tx, txs.back());
+ txs.push_back({cryptonote::blobdata(), crypto::null_hash});
+ cryptonote::tx_to_blob(tx, txs.back().blob);
}
- blocks.push_back({block, txs});
+ block_complete_entry bce;
+ bce.pruned = false;
+ bce.block = std::move(block);
+ bce.txs = std::move(txs);
+ blocks.push_back(bce);
int ret = check_flush(core, blocks, false);
if (ret)
{
@@ -594,11 +559,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
uint64_t num_blocks = 0;
uint64_t block_stop = 0;
@@ -622,9 +582,6 @@ int main(int argc, char* argv[])
, "Count blocks in bootstrap file and exit"
, false
};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<bool> arg_noverify = {"dangerous-unverified-import",
"Blindly trust the import file and use potentially malicious blocks and transactions during import (only enable if you exported the file yourself)", false};
const command_line::arg_descriptor<bool> arg_batch = {"batch",
@@ -634,7 +591,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, arg_input_file);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_batch_size);
command_line::add_arg(desc_cmd_sett, arg_block_stop);
@@ -709,7 +665,6 @@ int main(int argc, char* argv[])
return 1;
}
m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir);
- db_arg_str = command_line::get_arg(vm, arg_database);
mlog_configure(mlog_get_default_log_path("monero-blockchain-import.log"), true);
if (!command_line::is_arg_defaulted(vm, arg_log_level))
@@ -735,25 +690,7 @@ int main(int argc, char* argv[])
return 0;
}
-
- std::string db_type;
- int db_flags = 0;
- int res = 0;
- res = parse_db_arguments(db_arg_str, db_type, db_flags);
- if (res)
- {
- std::cerr << "Error parsing database argument(s)" << ENDL;
- return 1;
- }
-
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
-
- MINFO("database: " << db_type);
- MINFO("database flags: " << db_flags);
+ MINFO("database: LMDB");
MINFO("verify: " << std::boolalpha << opt_verify << std::noboolalpha);
if (opt_batch)
{
diff --git a/src/blockchain_utilities/blockchain_prune.cpp b/src/blockchain_utilities/blockchain_prune.cpp
index 8e13f2c04..9a9d58c46 100644
--- a/src/blockchain_utilities/blockchain_prune.cpp
+++ b/src/blockchain_utilities/blockchain_prune.cpp
@@ -35,7 +35,6 @@
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
#include "blockchain_db/lmdb/db_lmdb.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -441,11 +440,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
tools::on_startup();
@@ -455,9 +449,6 @@ 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_log_level = {"log-level", "0-4 or categories", ""};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<std::string> arg_db_sync_mode = {
"db-sync-mode"
, "Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]."
@@ -469,7 +460,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_db_sync_mode);
command_line::add_arg(desc_cmd_sett, arg_copy_pruned_database);
command_line::add_arg(desc_cmd_only, command_line::arg_help);
@@ -511,18 +501,6 @@ int main(int argc, char* argv[])
while (boost::ends_with(data_dir, "/") || boost::ends_with(data_dir, "\\"))
data_dir.pop_back();
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- MERROR("Invalid database type: " << db_type);
- return 1;
- }
- if (db_type != "lmdb")
- {
- MERROR("Unsupported database type: " << db_type << ". Only lmdb is supported");
- return 1;
- }
-
std::string db_sync_mode = command_line::get_arg(vm, arg_db_sync_mode);
uint64_t db_flags = 0;
if (!parse_db_sync_mode(db_sync_mode, db_flags))
@@ -552,13 +530,12 @@ int main(int argc, char* argv[])
{
core_storage[n].reset(new Blockchain(m_mempool));
- BlockchainDB* db = new_db(db_type);
+ BlockchainDB* db = new_db();
if (db == NULL)
{
- MERROR("Attempted to use non-existent database type: " << db_type);
- throw std::runtime_error("Attempting to use non-existent database type");
+ MERROR("Failed to initialize a database");
+ throw std::runtime_error("Failed to initialize a database");
}
- MDEBUG("database: " << db_type);
if (n == 1)
{
diff --git a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
index 2d49b6ecd..cee24d4da 100644
--- a/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
+++ b/src/blockchain_utilities/blockchain_prune_known_spent_data.cpp
@@ -33,7 +33,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -102,11 +101,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
tools::on_startup();
@@ -114,9 +108,6 @@ 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_log_level = {"log-level", "0-4 or categories", ""};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<bool> arg_verbose = {"verbose", "Verbose output", false};
const command_line::arg_descriptor<bool> arg_dry_run = {"dry-run", "Do not actually prune", false};
const command_line::arg_descriptor<std::string> arg_input = {"input", "Path to the known spent outputs file"};
@@ -125,7 +116,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_verbose);
command_line::add_arg(desc_cmd_sett, arg_dry_run);
command_line::add_arg(desc_cmd_sett, arg_input);
@@ -167,26 +157,18 @@ int main(int argc, char* argv[])
bool opt_verbose = command_line::get_arg(vm, arg_verbose);
bool opt_dry_run = command_line::get_arg(vm, arg_dry_run);
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
-
const std::string input = command_line::get_arg(vm, arg_input);
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
std::unique_ptr<Blockchain> core_storage;
tx_memory_pool m_mempool(*core_storage);
core_storage.reset(new Blockchain(m_mempool));
- BlockchainDB *db = new_db(db_type);
+ BlockchainDB *db = new_db();
if (db == NULL)
{
- LOG_ERROR("Attempted to use non-existent database type: " << db_type);
- throw std::runtime_error("Attempting to use non-existent database type");
+ LOG_ERROR("Failed to initialize a database");
+ throw std::runtime_error("Failed to initialize a database");
}
- LOG_PRINT_L0("database: " << db_type);
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
diff --git a/src/blockchain_utilities/blockchain_stats.cpp b/src/blockchain_utilities/blockchain_stats.cpp
index 33c26277e..2f66d54aa 100644
--- a/src/blockchain_utilities/blockchain_stats.cpp
+++ b/src/blockchain_utilities/blockchain_stats.cpp
@@ -34,7 +34,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -52,11 +51,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
uint64_t block_start = 0;
uint64_t block_stop = 0;
@@ -68,9 +62,6 @@ 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_log_level = {"log-level", "0-4 or categories", ""};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<uint64_t> arg_block_start = {"block-start", "start at block number", block_start};
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_inputs = {"with-inputs", "with input stats", false};
@@ -82,7 +73,6 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_block_start);
command_line::add_arg(desc_cmd_sett, arg_block_stop);
command_line::add_arg(desc_cmd_sett, arg_inputs);
@@ -131,24 +121,16 @@ int main(int argc, char* argv[])
bool do_ringsize = command_line::get_arg(vm, arg_ringsize);
bool do_hours = command_line::get_arg(vm, arg_hours);
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
-
LOG_PRINT_L0("Initializing source blockchain (BlockchainDB)");
std::unique_ptr<Blockchain> core_storage;
tx_memory_pool m_mempool(*core_storage);
core_storage.reset(new Blockchain(m_mempool));
- BlockchainDB *db = new_db(db_type);
+ BlockchainDB *db = new_db();
if (db == NULL)
{
- LOG_ERROR("Attempted to use non-existent database type: " << db_type);
- throw std::runtime_error("Attempting to use non-existent database type");
+ LOG_ERROR("Failed to initialize a database");
+ throw std::runtime_error("Failed to initialize a database");
}
- LOG_PRINT_L0("database: " << db_type);
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp
index bd73350b3..2fa56452b 100644
--- a/src/blockchain_utilities/blockchain_usage.cpp
+++ b/src/blockchain_utilities/blockchain_usage.cpp
@@ -34,7 +34,6 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_core/blockchain.h"
#include "blockchain_db/blockchain_db.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -82,11 +81,6 @@ int main(int argc, char* argv[])
epee::string_tools::set_module_name_and_folder(argv[0]);
- std::string default_db_type = "lmdb";
-
- std::string available_dbs = cryptonote::blockchain_db_types(", ");
- available_dbs = "available: " + available_dbs;
-
uint32_t log_level = 0;
tools::on_startup();
@@ -96,16 +90,12 @@ 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_log_level = {"log-level", "0-4 or categories", ""};
- const command_line::arg_descriptor<std::string> arg_database = {
- "database", available_dbs.c_str(), default_db_type
- };
const command_line::arg_descriptor<bool> arg_rct_only = {"rct-only", "Only work on ringCT outputs", false};
const command_line::arg_descriptor<std::string> arg_input = {"input", ""};
command_line::add_arg(desc_cmd_sett, cryptonote::arg_testnet_on);
command_line::add_arg(desc_cmd_sett, cryptonote::arg_stagenet_on);
command_line::add_arg(desc_cmd_sett, arg_log_level);
- command_line::add_arg(desc_cmd_sett, arg_database);
command_line::add_arg(desc_cmd_sett, arg_rct_only);
command_line::add_arg(desc_cmd_sett, arg_input);
command_line::add_arg(desc_cmd_only, command_line::arg_help);
@@ -147,13 +137,6 @@ int main(int argc, char* argv[])
network_type net_type = opt_testnet ? TESTNET : opt_stagenet ? STAGENET : MAINNET;
bool opt_rct_only = command_line::get_arg(vm, arg_rct_only);
- std::string db_type = command_line::get_arg(vm, arg_database);
- if (!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cerr << "Invalid database type: " << db_type << std::endl;
- return 1;
- }
-
// If we wanted to use the memory pool, we would set up a fake_core.
// Use Blockchain instead of lower-level BlockchainDB for two reasons:
@@ -170,13 +153,13 @@ int main(int argc, char* argv[])
std::unique_ptr<Blockchain> core_storage;
tx_memory_pool m_mempool(*core_storage);
core_storage.reset(new Blockchain(m_mempool));
- BlockchainDB* db = new_db(db_type);
+ BlockchainDB* db = new_db();
if (db == NULL)
{
- LOG_ERROR("Attempted to use non-existent database type: " << db_type);
- throw std::runtime_error("Attempting to use non-existent database type");
+ LOG_ERROR("Failed to initialize a database");
+ throw std::runtime_error("Failed to initialize a database");
}
- LOG_PRINT_L0("database: " << db_type);
+ LOG_PRINT_L0("database: LMDB");
const std::string filename = input;
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
diff --git a/src/blockchain_utilities/blockchain_utilities.h b/src/blockchain_utilities/blockchain_utilities.h
index 78487b995..e8615cf86 100644
--- a/src/blockchain_utilities/blockchain_utilities.h
+++ b/src/blockchain_utilities/blockchain_utilities.h
@@ -33,7 +33,7 @@
// bounds checking is done before writing to buffer, but buffer size
// should be a sensible maximum
-#define BUFFER_SIZE 1000000
+#define BUFFER_SIZE (2 * 1024 * 1024)
#define CHUNK_SIZE_WARNING_THRESHOLD 500000
#define NUM_BLOCKS_PER_CHUNK 1
#define BLOCKCHAIN_RAW "blockchain.raw"
diff --git a/src/blockchain_utilities/blocksdat_file.cpp b/src/blockchain_utilities/blocksdat_file.cpp
index f56ff5f94..df3c6cafc 100644
--- a/src/blockchain_utilities/blocksdat_file.cpp
+++ b/src/blockchain_utilities/blocksdat_file.cpp
@@ -99,17 +99,23 @@ bool BlocksdatFile::initialize_file(uint64_t block_stop)
return true;
}
-void BlocksdatFile::write_block(const crypto::hash& block_hash)
+void BlocksdatFile::write_block(const crypto::hash& block_hash, uint64_t weight)
{
m_hashes.push_back(block_hash);
+ m_weights.push_back(weight);
while (m_hashes.size() >= HASH_OF_HASHES_STEP)
{
crypto::hash hash;
crypto::cn_fast_hash(m_hashes.data(), HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash);
memmove(m_hashes.data(), m_hashes.data() + HASH_OF_HASHES_STEP, (m_hashes.size() - HASH_OF_HASHES_STEP) * sizeof(crypto::hash));
m_hashes.resize(m_hashes.size() - HASH_OF_HASHES_STEP);
- const std::string data(hash.data, sizeof(hash));
- *m_raw_data_file << data;
+ const std::string data_hashes(hash.data, sizeof(hash));
+ *m_raw_data_file << data_hashes;
+ crypto::cn_fast_hash(m_weights.data(), HASH_OF_HASHES_STEP * sizeof(uint64_t), hash);
+ memmove(m_weights.data(), m_weights.data() + HASH_OF_HASHES_STEP, (m_weights.size() - HASH_OF_HASHES_STEP) * sizeof(uint64_t));
+ m_weights.resize(m_weights.size() - HASH_OF_HASHES_STEP);
+ const std::string data_weights(hash.data, sizeof(hash));
+ *m_raw_data_file << data_weights;
}
}
@@ -154,7 +160,8 @@ bool BlocksdatFile::store_blockchain_raw(Blockchain* _blockchain_storage, tx_mem
{
// this method's height refers to 0-based height (genesis block = height 0)
crypto::hash hash = m_blockchain_storage->get_block_id_by_height(m_cur_height);
- write_block(hash);
+ uint64_t weight = m_blockchain_storage->get_db().get_block_weight(m_cur_height);
+ write_block(hash, weight);
if (m_cur_height % NUM_BLOCKS_PER_CHUNK == 0) {
num_blocks_written += NUM_BLOCKS_PER_CHUNK;
}
diff --git a/src/blockchain_utilities/blocksdat_file.h b/src/blockchain_utilities/blocksdat_file.h
index 315713424..72b7afc17 100644
--- a/src/blockchain_utilities/blocksdat_file.h
+++ b/src/blockchain_utilities/blocksdat_file.h
@@ -72,10 +72,11 @@ protected:
bool open_writer(const boost::filesystem::path& file_path, uint64_t block_stop);
bool initialize_file(uint64_t block_stop);
bool close();
- void write_block(const crypto::hash &block_hash);
+ void write_block(const crypto::hash &block_hash, uint64_t weight);
private:
uint64_t m_cur_height; // tracks current height during export
std::vector<crypto::hash> m_hashes;
+ std::vector<uint64_t> m_weights;
};
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index a7d309753..b14f9e8d2 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index 11bbe2e24..4a4b3c5c2 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -210,6 +210,7 @@ namespace cryptonote
ADD_CHECKPOINT(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568");
ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e");
ADD_CHECKPOINT(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29");
+ ADD_CHECKPOINT(1958000, "98a5d6e51afdf3146e0eefb10a66e8648d8d4d5c2742be8835e976ba217c9bb2");
return true;
}
diff --git a/src/common/perf_timer.cpp b/src/common/perf_timer.cpp
index 189eb85eb..4408170d1 100644
--- a/src/common/perf_timer.cpp
+++ b/src/common/perf_timer.cpp
@@ -34,7 +34,7 @@
#define MONERO_DEFAULT_LOG_CATEGORY "perf"
#define PERF_LOG_ALWAYS(level, cat, x) \
- el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::FileOnlyLog).construct(cat) << x
+ el::base::Writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::FileOnlyLog).construct(cat) << x
#define PERF_LOG(level, cat, x) \
do { \
if (ELPP->vRegistry()->allowed(level, cat)) PERF_LOG_ALWAYS(level, cat, x); \
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
index 717391623..29a37e655 100644
--- a/src/common/perf_timer.h
+++ b/src/common/perf_timer.h
@@ -76,14 +76,15 @@ private:
void set_performance_timer_log_level(el::Level level);
-#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, tools::performance_timer_log_level)
-#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer pt_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, l)
+#define PERF_TIMER_NAME(name) pt_##name
+#define PERF_TIMER_UNIT(name, unit) tools::LoggingPerformanceTimer PERF_TIMER_NAME(name)(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, tools::performance_timer_log_level)
+#define PERF_TIMER_UNIT_L(name, unit, l) tools::LoggingPerformanceTimer PERF_TIMER_NAME(name)t_##name(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, l)
#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000000)
#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000000, l)
-#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> pt_##name(new tools::LoggingPerformanceTimer(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, el::Level::Info))
+#define PERF_TIMER_START_UNIT(name, unit) std::unique_ptr<tools::LoggingPerformanceTimer> PERF_TIMER_NAME(name)(new tools::LoggingPerformanceTimer(#name, "perf." MONERO_DEFAULT_LOG_CATEGORY, unit, el::Level::Info))
#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000000)
-#define PERF_TIMER_STOP(name) do { pt_##name.reset(NULL); } while(0)
-#define PERF_TIMER_PAUSE(name) pt_##name->pause()
-#define PERF_TIMER_RESUME(name) pt_##name->resume()
+#define PERF_TIMER_STOP(name) do { PERF_TIMER_NAME(name).reset(NULL); } while(0)
+#define PERF_TIMER_PAUSE(name) PERF_TIMER_NAME(name).pause()
+#define PERF_TIMER_RESUME(name) PERF_TIMER_NAME(name).resume()
}
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index 2748c798c..18204eeee 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -37,16 +37,14 @@ static __thread bool is_leaf = false;
namespace tools
{
threadpool::threadpool(unsigned int max_threads) : running(true), active(0) {
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
- max = max_threads ? max_threads : tools::get_max_concurrency();
- size_t i = max ? max - 1 : 0;
- while(i--) {
- threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false)));
- }
+ create(max_threads);
}
threadpool::~threadpool() {
+ destroy();
+}
+
+void threadpool::destroy() {
try
{
const boost::unique_lock<boost::mutex> lock(mutex);
@@ -64,6 +62,23 @@ threadpool::~threadpool() {
try { threads[i].join(); }
catch (...) { /* ignore */ }
}
+ threads.clear();
+}
+
+void threadpool::recycle() {
+ destroy();
+ create(max);
+}
+
+void threadpool::create(unsigned int max_threads) {
+ boost::thread::attributes attrs;
+ attrs.set_stack_size(THREAD_STACK_SIZE);
+ max = max_threads ? max_threads : tools::get_max_concurrency();
+ size_t i = max ? max - 1 : 0;
+ running = true;
+ while(i--) {
+ threads.push_back(boost::thread(attrs, boost::bind(&threadpool::run, this, false)));
+ }
}
void threadpool::submit(waiter *obj, std::function<void()> f, bool leaf) {
@@ -145,7 +160,7 @@ void threadpool::run(bool flush) {
if (!running) break;
active++;
- e = queue.front();
+ e = std::move(queue.front());
queue.pop_front();
lock.unlock();
++depth;
diff --git a/src/common/threadpool.h b/src/common/threadpool.h
index 5e490ee7d..a49d0e14f 100644
--- a/src/common/threadpool.h
+++ b/src/common/threadpool.h
@@ -69,12 +69,17 @@ public:
// task to finish.
void submit(waiter *waiter, std::function<void()> f, bool leaf = false);
+ // destroy and recreate threads
+ void recycle();
+
unsigned int get_max_concurrency() const;
~threadpool();
private:
threadpool(unsigned int max_threads = 0);
+ void destroy();
+ void create(unsigned int max_threads);
typedef struct entry {
waiter *wo;
std::function<void()> f;
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 3a4d09fd8..80f1e5d7e 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -46,6 +46,7 @@ set(crypto_sources
random.c
skein.c
slow-hash.c
+ rx-slow-hash.c
CryptonightR_JIT.c
tree-hash.c)
@@ -53,6 +54,8 @@ if(ARCH_ID STREQUAL "i386" OR ARCH_ID STREQUAL "x86_64" OR ARCH_ID STREQUAL "x86
list(APPEND crypto_sources CryptonightR_template.S)
endif()
+include_directories(${RANDOMX_INCLUDE})
+
set(crypto_headers)
set(crypto_private_headers
@@ -86,6 +89,7 @@ monero_add_library(cncrypto
target_link_libraries(cncrypto
PUBLIC
epee
+ randomx
${Boost_SYSTEM_LIBRARY}
${SODIUM_LIBRARY}
PRIVATE
diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c
index 1e305b3a6..bb2c5fb40 100644
--- a/src/crypto/blake256.c
+++ b/src/crypto/blake256.c
@@ -40,6 +40,7 @@
#include <string.h>
#include <stdio.h>
#include <stdint.h>
+#include <memwipe.h>
#include "blake256.h"
#define U8TO32(p) \
@@ -277,7 +278,7 @@ void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) {
}
blake256_update(&S->outer, pad, 512);
- memset(keyhash, 0, 32);
+ memwipe(keyhash, sizeof(keyhash));
}
// keylen = number of bytes
@@ -307,7 +308,7 @@ void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) {
}
blake224_update(&S->outer, pad, 512);
- memset(keyhash, 0, 32);
+ memwipe(keyhash, sizeof(keyhash));
}
// datalen = number of bits
@@ -327,7 +328,7 @@ void hmac_blake256_final(hmac_state *S, uint8_t *digest) {
blake256_final(&S->inner, ihash);
blake256_update(&S->outer, ihash, 256);
blake256_final(&S->outer, digest);
- memset(ihash, 0, 32);
+ memwipe(ihash, sizeof(ihash));
}
void hmac_blake224_final(hmac_state *S, uint8_t *digest) {
@@ -335,7 +336,7 @@ void hmac_blake224_final(hmac_state *S, uint8_t *digest) {
blake224_final(&S->inner, ihash);
blake224_update(&S->outer, ihash, 224);
blake224_final(&S->outer, digest);
- memset(ihash, 0, 32);
+ memwipe(ihash, sizeof(ihash));
}
// keylen = number of bytes; inlen = number of bytes
diff --git a/src/crypto/c_threads.h b/src/crypto/c_threads.h
new file mode 100644
index 000000000..56d680ff8
--- /dev/null
+++ b/src/crypto/c_threads.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2019, 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.
+
+/* Brain-dead simple portability wrapper over thread APIs for C */
+#pragma once
+
+#ifdef _WIN32
+#include <windows.h>
+#define CTHR_MUTEX_TYPE HANDLE
+#define CTHR_MUTEX_INIT NULL
+#define CTHR_MUTEX_LOCK(x) do { if (x == NULL) { \
+ HANDLE p = CreateMutex(NULL, FALSE, NULL); \
+ if (InterlockedCompareExchangePointer((PVOID*)&x, (PVOID)p, NULL) != NULL) \
+ CloseHandle(p); \
+ } WaitForSingleObject(x, INFINITE); } while(0)
+#define CTHR_MUTEX_UNLOCK(x) ReleaseMutex(x)
+#define CTHR_THREAD_TYPE HANDLE
+#define CTHR_THREAD_RTYPE void
+#define CTHR_THREAD_RETURN return
+#define CTHR_THREAD_CREATE(thr, func, arg) thr = (HANDLE)_beginthread(func, 0, arg)
+#define CTHR_THREAD_JOIN(thr) WaitForSingleObject(thr, INFINITE)
+#else
+#include <pthread.h>
+#define CTHR_MUTEX_TYPE pthread_mutex_t
+#define CTHR_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
+#define CTHR_MUTEX_LOCK(x) pthread_mutex_lock(&x)
+#define CTHR_MUTEX_UNLOCK(x) pthread_mutex_unlock(&x)
+#define CTHR_THREAD_TYPE pthread_t
+#define CTHR_THREAD_RTYPE void *
+#define CTHR_THREAD_RETURN return NULL
+#define CTHR_THREAD_CREATE(thr, func, arg) pthread_create(&thr, NULL, func, arg)
+#define CTHR_THREAD_JOIN(thr) pthread_join(thr, NULL)
+#endif
diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h
index 859c810bd..d117bb640 100644
--- a/src/crypto/hash-ops.h
+++ b/src/crypto/hash-ops.h
@@ -87,3 +87,11 @@ void hash_extra_jh(const void *data, size_t length, char *hash);
void hash_extra_skein(const void *data, size_t length, char *hash);
void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash);
+
+#define RX_BLOCK_VERSION 12
+void rx_slow_hash_allocate_state(void);
+void rx_slow_hash_free_state(void);
+uint64_t rx_seedheight(const uint64_t height);
+void rx_seedheights(const uint64_t height, uint64_t *seed_height, uint64_t *next_height);
+void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length, char *hash, int miners, int is_alt);
+void rx_reorg(const uint64_t split_height);
diff --git a/src/crypto/hash.h b/src/crypto/hash.h
index 17071923d..27184fa53 100644
--- a/src/crypto/hash.h
+++ b/src/crypto/hash.h
@@ -32,7 +32,6 @@
#include <stddef.h>
#include <iostream>
-#include <boost/utility/value_init.hpp>
#include "common/pod-class.h"
#include "generic-ops.h"
@@ -90,8 +89,8 @@ namespace crypto {
epee::to_hex::formatted(o, epee::as_byte_span(v)); return o;
}
- const static crypto::hash null_hash = boost::value_initialized<crypto::hash>();
- const static crypto::hash8 null_hash8 = boost::value_initialized<crypto::hash8>();
+ constexpr static crypto::hash null_hash = {};
+ constexpr static crypto::hash8 null_hash8 = {};
}
CRYPTO_MAKE_HASHABLE(hash)
diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c
new file mode 100644
index 000000000..a7a459ad3
--- /dev/null
+++ b/src/crypto/rx-slow-hash.c
@@ -0,0 +1,313 @@
+// Copyright (c) 2019, 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 <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "randomx.h"
+#include "c_threads.h"
+#include "hash-ops.h"
+#include "misc_log_ex.h"
+
+#define RX_LOGCAT "randomx"
+
+#if defined(_MSC_VER)
+#define THREADV __declspec(thread)
+#else
+#define THREADV __thread
+#endif
+
+typedef struct rx_state {
+ CTHR_MUTEX_TYPE rs_mutex;
+ char rs_hash[HASH_SIZE];
+ uint64_t rs_height;
+ randomx_cache *rs_cache;
+} rx_state;
+
+static CTHR_MUTEX_TYPE rx_mutex = CTHR_MUTEX_INIT;
+static CTHR_MUTEX_TYPE rx_dataset_mutex = CTHR_MUTEX_INIT;
+
+static rx_state rx_s[2] = {{CTHR_MUTEX_INIT,{0},0,0},{CTHR_MUTEX_INIT,{0},0,0}};
+
+static randomx_dataset *rx_dataset;
+static uint64_t rx_dataset_height;
+static THREADV randomx_vm *rx_vm = NULL;
+
+static void local_abort(const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+#ifdef NDEBUG
+ _exit(1);
+#else
+ abort();
+#endif
+}
+
+static inline int disabled_flags(void) {
+ static int flags = -1;
+
+ if (flags != -1) {
+ return flags;
+ }
+
+ const char *env = getenv("MONERO_RANDOMX_UMASK");
+ if (!env) {
+ flags = 0;
+ }
+ else {
+ char* endptr;
+ long int value = strtol(env, &endptr, 0);
+ if (endptr != env && value >= 0 && value < INT_MAX) {
+ flags = value;
+ }
+ else {
+ flags = 0;
+ }
+ }
+
+ return flags;
+}
+
+static inline int enabled_flags(void) {
+ static int flags = -1;
+
+ if (flags != -1) {
+ return flags;
+ }
+
+ flags = randomx_get_flags();
+
+ return flags;
+}
+
+#define SEEDHASH_EPOCH_BLOCKS 2048 /* Must be same as BLOCKS_SYNCHRONIZING_MAX_COUNT in cryptonote_config.h */
+#define SEEDHASH_EPOCH_LAG 64
+
+void rx_reorg(const uint64_t split_height) {
+ int i;
+ CTHR_MUTEX_LOCK(rx_mutex);
+ for (i=0; i<2; i++) {
+ if (split_height <= rx_s[i].rs_height) {
+ if (rx_s[i].rs_height == rx_dataset_height)
+ rx_dataset_height = 1;
+ rx_s[i].rs_height = 1; /* set to an invalid seed height */
+ }
+ }
+ CTHR_MUTEX_UNLOCK(rx_mutex);
+}
+
+uint64_t rx_seedheight(const uint64_t height) {
+ uint64_t s_height = (height <= SEEDHASH_EPOCH_BLOCKS+SEEDHASH_EPOCH_LAG) ? 0 :
+ (height - SEEDHASH_EPOCH_LAG - 1) & ~(SEEDHASH_EPOCH_BLOCKS-1);
+ return s_height;
+}
+
+void rx_seedheights(const uint64_t height, uint64_t *seedheight, uint64_t *nextheight) {
+ *seedheight = rx_seedheight(height);
+ *nextheight = rx_seedheight(height + SEEDHASH_EPOCH_LAG);
+}
+
+typedef struct seedinfo {
+ randomx_cache *si_cache;
+ unsigned long si_start;
+ unsigned long si_count;
+} seedinfo;
+
+static CTHR_THREAD_RTYPE rx_seedthread(void *arg) {
+ seedinfo *si = arg;
+ randomx_init_dataset(rx_dataset, si->si_cache, si->si_start, si->si_count);
+ CTHR_THREAD_RETURN;
+}
+
+static void rx_initdata(randomx_cache *rs_cache, const int miners, const uint64_t seedheight) {
+ if (miners > 1) {
+ unsigned long delta = randomx_dataset_item_count() / miners;
+ unsigned long start = 0;
+ int i;
+ seedinfo *si;
+ CTHR_THREAD_TYPE *st;
+ si = malloc(miners * sizeof(seedinfo));
+ if (si == NULL)
+ local_abort("Couldn't allocate RandomX mining threadinfo");
+ st = malloc(miners * sizeof(CTHR_THREAD_TYPE));
+ if (st == NULL) {
+ free(si);
+ local_abort("Couldn't allocate RandomX mining threadlist");
+ }
+ for (i=0; i<miners-1; i++) {
+ si[i].si_cache = rs_cache;
+ si[i].si_start = start;
+ si[i].si_count = delta;
+ start += delta;
+ }
+ si[i].si_cache = rs_cache;
+ si[i].si_start = start;
+ si[i].si_count = randomx_dataset_item_count() - start;
+ for (i=1; i<miners; i++) {
+ CTHR_THREAD_CREATE(st[i], rx_seedthread, &si[i]);
+ }
+ randomx_init_dataset(rx_dataset, rs_cache, 0, si[0].si_count);
+ for (i=1; i<miners; i++) {
+ CTHR_THREAD_JOIN(st[i]);
+ }
+ free(st);
+ free(si);
+ } else {
+ randomx_init_dataset(rx_dataset, rs_cache, 0, randomx_dataset_item_count());
+ }
+ rx_dataset_height = seedheight;
+}
+
+void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const char *seedhash, const void *data, size_t length,
+ char *hash, int miners, int is_alt) {
+ uint64_t s_height = rx_seedheight(mainheight);
+ int toggle = (s_height & SEEDHASH_EPOCH_BLOCKS) != 0;
+ randomx_flags flags = enabled_flags() & ~disabled_flags();
+ rx_state *rx_sp;
+ randomx_cache *cache;
+
+ CTHR_MUTEX_LOCK(rx_mutex);
+
+ /* if alt block but with same seed as mainchain, no need for alt cache */
+ if (is_alt) {
+ if (s_height == seedheight && !memcmp(rx_s[toggle].rs_hash, seedhash, HASH_SIZE))
+ is_alt = 0;
+ } else {
+ /* RPC could request an earlier block on mainchain */
+ if (s_height > seedheight)
+ is_alt = 1;
+ /* miner can be ahead of mainchain */
+ else if (s_height < seedheight)
+ toggle ^= 1;
+ }
+
+ toggle ^= (is_alt != 0);
+
+ rx_sp = &rx_s[toggle];
+ CTHR_MUTEX_LOCK(rx_sp->rs_mutex);
+ CTHR_MUTEX_UNLOCK(rx_mutex);
+
+ cache = rx_sp->rs_cache;
+ if (cache == NULL) {
+ if (cache == NULL) {
+ cache = randomx_alloc_cache(flags | RANDOMX_FLAG_LARGE_PAGES);
+ if (cache == NULL) {
+ mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX cache");
+ cache = randomx_alloc_cache(flags);
+ }
+ if (cache == NULL)
+ local_abort("Couldn't allocate RandomX cache");
+ }
+ }
+ if (rx_sp->rs_height != seedheight || rx_sp->rs_cache == NULL || memcmp(seedhash, rx_sp->rs_hash, HASH_SIZE)) {
+ randomx_init_cache(cache, seedhash, HASH_SIZE);
+ rx_sp->rs_cache = cache;
+ rx_sp->rs_height = seedheight;
+ memcpy(rx_sp->rs_hash, seedhash, HASH_SIZE);
+ }
+ if (rx_vm == NULL) {
+ if ((flags & RANDOMX_FLAG_JIT) && !miners) {
+ flags |= RANDOMX_FLAG_SECURE & ~disabled_flags();
+ }
+ if (miners && (disabled_flags() & RANDOMX_FLAG_FULL_MEM)) {
+ miners = 0;
+ }
+ if (miners) {
+ CTHR_MUTEX_LOCK(rx_dataset_mutex);
+ if (rx_dataset == NULL) {
+ rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_LARGE_PAGES);
+ if (rx_dataset == NULL) {
+ mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX dataset");
+ rx_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
+ }
+ if (rx_dataset != NULL)
+ rx_initdata(rx_sp->rs_cache, miners, seedheight);
+ }
+ if (rx_dataset != NULL)
+ flags |= RANDOMX_FLAG_FULL_MEM;
+ else {
+ miners = 0;
+ mwarning(RX_LOGCAT, "Couldn't allocate RandomX dataset for miner");
+ }
+ CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
+ }
+ rx_vm = randomx_create_vm(flags | RANDOMX_FLAG_LARGE_PAGES, rx_sp->rs_cache, rx_dataset);
+ if(rx_vm == NULL) { //large pages failed
+ mdebug(RX_LOGCAT, "Couldn't use largePages for RandomX VM");
+ rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
+ }
+ if(rx_vm == NULL) {//fallback if everything fails
+ flags = RANDOMX_FLAG_DEFAULT | (miners ? RANDOMX_FLAG_FULL_MEM : 0);
+ rx_vm = randomx_create_vm(flags, rx_sp->rs_cache, rx_dataset);
+ }
+ if (rx_vm == NULL)
+ local_abort("Couldn't allocate RandomX VM");
+ } else if (miners) {
+ CTHR_MUTEX_LOCK(rx_dataset_mutex);
+ if (rx_dataset != NULL && rx_dataset_height != seedheight)
+ rx_initdata(cache, miners, seedheight);
+ CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
+ } else {
+ /* this is a no-op if the cache hasn't changed */
+ randomx_vm_set_cache(rx_vm, rx_sp->rs_cache);
+ }
+ /* mainchain users can run in parallel */
+ if (!is_alt)
+ CTHR_MUTEX_UNLOCK(rx_sp->rs_mutex);
+ randomx_calculate_hash(rx_vm, data, length, hash);
+ /* altchain slot users always get fully serialized */
+ if (is_alt)
+ CTHR_MUTEX_UNLOCK(rx_sp->rs_mutex);
+}
+
+void rx_slow_hash_allocate_state(void) {
+}
+
+void rx_slow_hash_free_state(void) {
+ if (rx_vm != NULL) {
+ randomx_destroy_vm(rx_vm);
+ rx_vm = NULL;
+ }
+}
+
+void rx_stop_mining(void) {
+ CTHR_MUTEX_LOCK(rx_dataset_mutex);
+ if (rx_dataset != NULL) {
+ randomx_dataset *rd = rx_dataset;
+ rx_dataset = NULL;
+ randomx_release_dataset(rd);
+ }
+ CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
+}
diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c
index 647471513..88eb751a6 100644
--- a/src/crypto/slow-hash.c
+++ b/src/crypto/slow-hash.c
@@ -742,7 +742,7 @@ BOOL SetLockPagesPrivilege(HANDLE hProcess, BOOL bEnable)
* the allocated buffer.
*/
-void slow_hash_allocate_state(void)
+void cn_slow_hash_allocate_state(void)
{
if(hp_state != NULL)
return;
@@ -804,7 +804,7 @@ void slow_hash_allocate_state(void)
*@brief frees the state allocated by slow_hash_allocate_state
*/
-void slow_hash_free_state(void)
+void cn_slow_hash_free_state(void)
{
if(hp_state == NULL)
return;
@@ -892,7 +892,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
// this isn't supposed to happen, but guard against it for now.
if(hp_state == NULL)
- slow_hash_allocate_state();
+ cn_slow_hash_allocate_state();
// locals to avoid constant TLS dereferencing
uint8_t *local_hp_state = hp_state;
@@ -1009,13 +1009,13 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
}
#elif !defined NO_AES && (defined(__arm__) || defined(__aarch64__))
-void slow_hash_allocate_state(void)
+void cn_slow_hash_allocate_state(void)
{
// Do nothing, this is just to maintain compatibility with the upgraded slow-hash.c
return;
}
-void slow_hash_free_state(void)
+void cn_slow_hash_free_state(void)
{
// As above
return;
@@ -1582,13 +1582,13 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
#define hp_jitfunc ((v4_random_math_JIT_func)NULL)
-void slow_hash_allocate_state(void)
+void cn_slow_hash_allocate_state(void)
{
// Do nothing, this is just to maintain compatibility with the upgraded slow-hash.c
return;
}
-void slow_hash_free_state(void)
+void cn_slow_hash_free_state(void)
{
// As above
return;
@@ -1765,3 +1765,15 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
}
#endif
+
+void slow_hash_allocate_state(void)
+{
+ cn_slow_hash_allocate_state();
+ rx_slow_hash_allocate_state();
+}
+
+void slow_hash_free_state(void)
+{
+ cn_slow_hash_free_state();
+ rx_slow_hash_free_state();
+}
diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h
index 51076e8c0..a682bebf2 100644
--- a/src/cryptonote_basic/connection_context.h
+++ b/src/cryptonote_basic/connection_context.h
@@ -43,7 +43,7 @@ namespace cryptonote
{
cryptonote_connection_context(): m_state(state_before_handshake), m_remote_blockchain_height(0), m_last_response_height(0),
m_last_request_time(boost::date_time::not_a_date_time), m_callback_request_count(0),
- m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_rpc_port(0), m_anchor(false) {}
+ m_last_known_hash(crypto::null_hash), m_pruning_seed(0), m_rpc_port(0), m_rpc_credits_per_hash(0), m_anchor(false) {}
enum state
{
@@ -55,7 +55,7 @@ namespace cryptonote
};
state m_state;
- std::vector<crypto::hash> m_needed_objects;
+ std::vector<std::pair<crypto::hash, uint64_t>> m_needed_objects;
std::unordered_set<crypto::hash> m_requested_objects;
uint64_t m_remote_blockchain_height;
uint64_t m_last_response_height;
@@ -64,6 +64,7 @@ namespace cryptonote
crypto::hash m_last_known_hash;
uint32_t m_pruning_seed;
uint16_t m_rpc_port;
+ uint32_t m_rpc_credits_per_hash;
bool m_anchor;
//size_t m_score; TODO: add score calculations
};
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index 055c4a22b..e2286ae8c 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -195,6 +195,7 @@ namespace cryptonote
private:
// hash cash
mutable std::atomic<bool> hash_valid;
+ mutable std::atomic<bool> prunable_hash_valid;
mutable std::atomic<bool> blob_size_valid;
public:
@@ -203,6 +204,7 @@ namespace cryptonote
// hash cash
mutable crypto::hash hash;
+ mutable crypto::hash prunable_hash;
mutable size_t blob_size;
bool pruned;
@@ -211,22 +213,26 @@ namespace cryptonote
std::atomic<unsigned int> prefix_size;
transaction();
- transaction(const transaction &t): transaction_prefix(t), hash_valid(false), blob_size_valid(false), signatures(t.signatures), rct_signatures(t.rct_signatures), pruned(t.pruned), unprunable_size(t.unprunable_size.load()), prefix_size(t.prefix_size.load()) { 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); } pruned = t.pruned; unprunable_size = t.unprunable_size.load(); prefix_size = t.prefix_size.load(); return *this; }
+ transaction(const transaction &t);
+ transaction &operator=(const transaction &t);
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_prunable_hash_valid() const { return prunable_hash_valid.load(std::memory_order_acquire); }
+ void set_prunable_hash_valid(bool v) const { prunable_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); }
- void set_hash(const crypto::hash &h) { hash = h; set_hash_valid(true); }
- void set_blob_size(size_t sz) { blob_size = sz; set_blob_size_valid(true); }
+ void set_hash(const crypto::hash &h) const { hash = h; set_hash_valid(true); }
+ void set_prunable_hash(const crypto::hash &h) const { prunable_hash = h; set_prunable_hash_valid(true); }
+ void set_blob_size(size_t sz) const { blob_size = sz; set_blob_size_valid(true); }
BEGIN_SERIALIZE_OBJECT()
if (!typename Archive<W>::is_saving())
{
set_hash_valid(false);
+ set_prunable_hash_valid(false);
set_blob_size_valid(false);
}
@@ -327,6 +333,63 @@ namespace cryptonote
static size_t get_signature_size(const txin_v& tx_in);
};
+ inline transaction::transaction(const transaction &t):
+ transaction_prefix(t),
+ hash_valid(false),
+ prunable_hash_valid(false),
+ blob_size_valid(false),
+ signatures(t.signatures),
+ rct_signatures(t.rct_signatures),
+ pruned(t.pruned),
+ unprunable_size(t.unprunable_size.load()),
+ prefix_size(t.prefix_size.load())
+ {
+ 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);
+ }
+ if (t.is_prunable_hash_valid())
+ {
+ prunable_hash = t.prunable_hash;
+ set_prunable_hash_valid(true);
+ }
+ }
+
+ inline transaction &transaction::operator=(const transaction &t)
+ {
+ transaction_prefix::operator=(t);
+
+ set_hash_valid(false);
+ set_prunable_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_prunable_hash_valid())
+ {
+ prunable_hash = t.prunable_hash;
+ set_prunable_hash_valid(true);
+ }
+ if (t.is_blob_size_valid())
+ {
+ blob_size = t.blob_size;
+ set_blob_size_valid(true);
+ }
+ pruned = t.pruned;
+ unprunable_size = t.unprunable_size.load();
+ prefix_size = t.prefix_size.load();
+ return *this;
+ }
inline
transaction::transaction()
@@ -346,6 +409,7 @@ namespace cryptonote
signatures.clear();
rct_signatures.type = rct::RCTTypeNull;
set_hash_valid(false);
+ set_prunable_hash_valid(false);
set_blob_size_valid(false);
pruned = false;
unprunable_size = 0;
@@ -356,6 +420,7 @@ namespace cryptonote
void transaction::invalidate_hashes()
{
set_hash_valid(false);
+ set_prunable_hash_valid(false);
set_blob_size_valid(false);
}
@@ -408,6 +473,7 @@ namespace cryptonote
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); }
+ void set_hash(const crypto::hash &h) const { hash = h; set_hash_valid(true); }
transaction miner_tx;
std::vector<crypto::hash> tx_hashes;
diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp
index d8de65b81..9bafcfc86 100644
--- a/src/cryptonote_basic/cryptonote_basic_impl.cpp
+++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp
@@ -110,9 +110,6 @@ namespace cryptonote {
return false;
}
- assert(median_weight < std::numeric_limits<uint32_t>::max());
- assert(current_block_weight < std::numeric_limits<uint32_t>::max());
-
uint64_t product_hi;
// BUGFIX: 32-bit saturation bug (e.g. ARM7), the result was being
// treated as 32-bit by default.
@@ -122,8 +119,8 @@ namespace cryptonote {
uint64_t reward_hi;
uint64_t reward_lo;
- div128_32(product_hi, product_lo, static_cast<uint32_t>(median_weight), &reward_hi, &reward_lo);
- div128_32(reward_hi, reward_lo, static_cast<uint32_t>(median_weight), &reward_hi, &reward_lo);
+ div128_64(product_hi, product_lo, median_weight, &reward_hi, &reward_lo, NULL, NULL);
+ div128_64(reward_hi, reward_lo, median_weight, &reward_hi, &reward_lo, NULL, NULL);
assert(0 == reward_hi);
assert(reward_lo < base_reward);
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 7d7de416d..138cf49f4 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -103,6 +103,26 @@ namespace cryptonote
ge_p1p1_to_p3(&A2, &tmp3);
ge_p3_tobytes(&AB, &A2);
}
+
+ uint64_t get_transaction_weight_clawback(const transaction &tx, size_t n_padded_outputs)
+ {
+ const rct::rctSig &rv = tx.rct_signatures;
+ const uint64_t bp_base = 368;
+ const size_t n_outputs = tx.vout.size();
+ if (n_padded_outputs <= 2)
+ return 0;
+ size_t nlr = 0;
+ while ((1u << nlr) < n_padded_outputs)
+ ++nlr;
+ nlr += 6;
+ const size_t bp_size = 32 * (9 + 2 * nlr);
+ CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(BULLETPROOF_MAX_OUTPUTS) + " per transaction");
+ CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback: bp_base " + std::to_string(bp_base) + ", n_padded_outputs "
+ + std::to_string(n_padded_outputs) + ", bp_size " + std::to_string(bp_size));
+ const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
+ return bp_clawback;
+ }
+ //---------------------------------------------------------------
}
namespace cryptonote
@@ -386,27 +406,61 @@ namespace cryptonote
//---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
{
+ CHECK_AND_ASSERT_MES(!tx.pruned, std::numeric_limits<uint64_t>::max(), "get_transaction_weight does not support pruned txes");
if (tx.version < 2)
return blob_size;
const rct::rctSig &rv = tx.rct_signatures;
if (!rct::is_rct_bulletproof(rv.type))
return blob_size;
- const size_t n_outputs = tx.vout.size();
- if (n_outputs <= 2)
- return blob_size;
- const uint64_t bp_base = 368;
const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
- size_t nlr = 0;
- for (const auto &bp: rv.p.bulletproofs)
- nlr += bp.L.size() * 2;
- const size_t bp_size = 32 * (9 + nlr);
- CHECK_AND_ASSERT_THROW_MES_L1(n_outputs <= BULLETPROOF_MAX_OUTPUTS, "maximum number of outputs is " + std::to_string(BULLETPROOF_MAX_OUTPUTS) + " per transaction");
- CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback");
- const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
+ uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs);
CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow");
return blob_size + bp_clawback;
}
//---------------------------------------------------------------
+ uint64_t get_pruned_transaction_weight(const transaction &tx)
+ {
+ CHECK_AND_ASSERT_MES(tx.pruned, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support non pruned txes");
+ CHECK_AND_ASSERT_MES(tx.version >= 2, std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support v1 txes");
+ CHECK_AND_ASSERT_MES(tx.rct_signatures.type >= rct::RCTTypeBulletproof2,
+ std::numeric_limits<uint64_t>::max(), "get_pruned_transaction_weight does not support older range proof types");
+ CHECK_AND_ASSERT_MES(!tx.vin.empty(), std::numeric_limits<uint64_t>::max(), "empty vin");
+ CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), std::numeric_limits<uint64_t>::max(), "empty vin");
+
+ // get pruned data size
+ std::ostringstream s;
+ binary_archive<true> a(s);
+ ::serialization::serialize(a, const_cast<transaction&>(tx));
+ uint64_t weight = s.str().size(), extra;
+
+ // nbps (technically varint)
+ weight += 1;
+
+ // calculate deterministic bulletproofs size (assumes canonical BP format)
+ size_t nrl = 0, n_padded_outputs;
+ while ((n_padded_outputs = (1u << nrl)) < tx.vout.size())
+ ++nrl;
+ nrl += 6;
+ extra = 32 * (9 + 2 * nrl) + 2;
+ weight += extra;
+
+ // calculate deterministic MLSAG data size
+ const size_t ring_size = boost::get<cryptonote::txin_to_key>(tx.vin[0]).key_offsets.size();
+ extra = tx.vin.size() * (ring_size * (1 + 1) * 32 + 32 /* cc */);
+ weight += extra;
+
+ // calculate deterministic pseudoOuts size
+ extra = 32 * (tx.vin.size());
+ weight += extra;
+
+ // clawback
+ uint64_t bp_clawback = get_transaction_weight_clawback(tx, n_padded_outputs);
+ CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - weight, "Weight overflow");
+ weight += bp_clawback;
+
+ return weight;
+ }
+ //---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx)
{
size_t blob_size;
@@ -1011,7 +1065,19 @@ namespace cryptonote
crypto::hash get_transaction_prunable_hash(const transaction& t, const cryptonote::blobdata *blobdata)
{
crypto::hash res;
+ if (t.is_prunable_hash_valid())
+ {
+#ifdef ENABLE_HASH_CASH_INTEGRITY_CHECK
+ CHECK_AND_ASSERT_THROW_MES(!calculate_transaction_prunable_hash(t, blobdata, res) || t.hash == res, "tx hash cash integrity failure");
+#endif
+ res = t.prunable_hash;
+ ++tx_hashes_cached_count;
+ return res;
+ }
+
+ ++tx_hashes_calculated_count;
CHECK_AND_ASSERT_THROW_MES(calculate_transaction_prunable_hash(t, blobdata, res), "Failed to calculate tx prunable hash");
+ t.set_prunable_hash(res);
return res;
}
//---------------------------------------------------------------
@@ -1047,11 +1113,14 @@ namespace cryptonote
// the tx hash is the hash of the 3 hashes
crypto::hash res = cn_fast_hash(hashes, sizeof(hashes));
+ t.set_hash(res);
return res;
}
//---------------------------------------------------------------
bool calculate_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size)
{
+ CHECK_AND_ASSERT_MES(!t.pruned, false, "Cannot calculate the hash of a pruned transaction");
+
// v1 transactions hash the entire blob
if (t.version == 1)
{
@@ -1091,8 +1160,7 @@ namespace cryptonote
{
if (!t.is_blob_size_valid())
{
- t.blob_size = blob.size();
- t.set_blob_size_valid(true);
+ t.set_blob_size(blob.size());
}
*blob_size = t.blob_size;
}
@@ -1112,8 +1180,7 @@ namespace cryptonote
{
if (!t.is_blob_size_valid())
{
- t.blob_size = get_object_blobsize(t);
- t.set_blob_size_valid(true);
+ t.set_blob_size(get_object_blobsize(t));
}
*blob_size = t.blob_size;
}
@@ -1124,12 +1191,10 @@ namespace cryptonote
bool ret = calculate_transaction_hash(t, res, blob_size);
if (!ret)
return false;
- t.hash = res;
- t.set_hash_valid(true);
+ t.set_hash(res);
if (blob_size)
{
- t.blob_size = *blob_size;
- t.set_blob_size_valid(true);
+ t.set_blob_size(*blob_size);
}
return true;
}
@@ -1206,8 +1271,7 @@ namespace cryptonote
bool ret = calculate_block_hash(b, res);
if (!ret)
return false;
- b.hash = res;
- b.set_hash_valid(true);
+ b.set_hash(res);
return true;
}
//---------------------------------------------------------------
@@ -1218,21 +1282,6 @@ namespace cryptonote
return p;
}
//---------------------------------------------------------------
- bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height)
- {
- // block 202612 bug workaround
- if (height == 202612)
- {
- static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000";
- string_tools::hex_to_pod(longhash_202612, res);
- return true;
- }
- blobdata bd = get_block_hashing_blob(b);
- const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0;
- crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant, height);
- return true;
- }
- //---------------------------------------------------------------
std::vector<uint64_t> relative_output_offsets_to_absolute(const std::vector<uint64_t>& off)
{
std::vector<uint64_t> res = off;
@@ -1253,13 +1302,6 @@ namespace cryptonote
return res;
}
//---------------------------------------------------------------
- crypto::hash get_block_longhash(const block& b, uint64_t height)
- {
- crypto::hash p = null_hash;
- get_block_longhash(b, p, height);
- return p;
- }
- //---------------------------------------------------------------
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash *block_hash)
{
std::stringstream ss;
@@ -1273,8 +1315,7 @@ namespace cryptonote
{
calculate_block_hash(b, *block_hash, &b_blob);
++block_hashes_calculated_count;
- b.hash = *block_hash;
- b.set_hash_valid(true);
+ b.set_hash(*block_hash);
}
return true;
}
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index c9de2a56e..29e4def64 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -117,8 +117,6 @@ namespace cryptonote
bool calculate_block_hash(const block& b, crypto::hash& res, const blobdata *blob = NULL);
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 parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash *block_hash);
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b);
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b, crypto::hash &block_hash);
@@ -129,6 +127,7 @@ namespace cryptonote
bool parse_amount(uint64_t& amount, const std::string& str_amount);
uint64_t get_transaction_weight(const transaction &tx);
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size);
+ uint64_t get_pruned_transaction_weight(const transaction &tx);
bool check_money_overflow(const transaction& tx);
bool check_outs_overflow(const transaction& tx);
diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp
index 98158a513..dfeca27b4 100644
--- a/src/cryptonote_basic/hardfork.cpp
+++ b/src/cryptonote_basic/hardfork.cpp
@@ -87,7 +87,7 @@ bool HardFork::add_fork(uint8_t version, uint64_t height, uint8_t threshold, tim
}
if (threshold > 100)
return false;
- heights.push_back(Params(version, height, threshold, time));
+ heights.push_back(hardfork_t(version, height, threshold, time));
return true;
}
@@ -171,7 +171,7 @@ void HardFork::init()
// add a placeholder for the default version, to avoid special cases
if (heights.empty())
- heights.push_back(Params(original_version, 0, 0, 0));
+ heights.push_back(hardfork_t(original_version, 0, 0, 0));
versions.clear();
for (size_t n = 0; n < 256; ++n)
diff --git a/src/cryptonote_basic/hardfork.h b/src/cryptonote_basic/hardfork.h
index 123978b12..987dcc75a 100644
--- a/src/cryptonote_basic/hardfork.h
+++ b/src/cryptonote_basic/hardfork.h
@@ -29,6 +29,7 @@
#pragma once
#include "syncobj.h"
+#include "hardforks/hardforks.h"
#include "cryptonote_basic/cryptonote_basic.h"
namespace cryptonote
@@ -230,14 +231,6 @@ namespace cryptonote
*/
uint64_t get_window_size() const { return window_size; }
- struct Params {
- uint8_t version;
- uint8_t threshold;
- uint64_t height;
- time_t time;
- Params(uint8_t version, uint64_t height, uint8_t threshold, time_t time): version(version), threshold(threshold), height(height), time(time) {}
- };
-
private:
uint8_t get_block_version(uint64_t height) const;
@@ -262,7 +255,7 @@ namespace cryptonote
uint8_t original_version;
uint64_t original_version_till_height;
- std::vector<Params> heights;
+ std::vector<hardfork_t> heights;
std::deque<uint8_t> versions; /* rolling window of the last N blocks' versions */
unsigned int last_versions[256]; /* count of the block versions in the last N blocks */
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index 2dad2795e..688aeaea3 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -30,15 +30,16 @@
#include <sstream>
#include <numeric>
-#include <boost/utility/value_init.hpp>
#include <boost/interprocess/detail/atomic.hpp>
#include <boost/algorithm/string.hpp>
#include "misc_language.h"
#include "syncobj.h"
#include "cryptonote_basic_impl.h"
#include "cryptonote_format_utils.h"
+#include "cryptonote_core/cryptonote_tx_utils.h"
#include "file_io_utils.h"
#include "common/command_line.h"
+#include "common/util.h"
#include "string_coding.h"
#include "string_tools.h"
#include "storages/portable_storage_template_helper.h"
@@ -61,7 +62,9 @@
#include <devstat.h>
#include <errno.h>
#include <fcntl.h>
+#if defined(__amd64__) || defined(__i386__) || defined(__x86_64__)
#include <machine/apm_bios.h>
+#endif
#include <stdio.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
@@ -99,12 +102,13 @@ namespace cryptonote
}
- miner::miner(i_miner_handler* phandler):m_stop(1),
- m_template(boost::value_initialized<block>()),
+ miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):m_stop(1),
+ m_template{},
m_template_no(0),
m_diffic(0),
m_thread_index(0),
m_phandler(phandler),
+ m_gbh(gbh),
m_height(0),
m_threads_active(0),
m_pausers_count(0),
@@ -430,6 +434,7 @@ namespace cryptonote
{
boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1);
}
+ extern "C" void rx_stop_mining(void);
//-----------------------------------------------------------------------------------------------------
bool miner::stop()
{
@@ -462,15 +467,16 @@ namespace cryptonote
MINFO("Mining has been stopped, " << m_threads.size() << " finished" );
m_threads.clear();
m_threads_autodetect.clear();
+ rx_stop_mining();
return true;
}
//-----------------------------------------------------------------------------------------------------
- bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height)
+ bool miner::find_nonce_for_given_block(const get_block_hash_t &gbh, 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);
+ gbh(bl, height, tools::get_max_concurrency(), h);
if(check_hash(h, diffic))
{
@@ -566,7 +572,7 @@ namespace cryptonote
b.nonce = nonce;
crypto::hash h;
- get_block_longhash(b, h, height);
+ m_gbh(b, height, tools::get_max_concurrency(), h);
if(check_hash(h, local_diff))
{
@@ -1082,6 +1088,7 @@ namespace cryptonote
return boost::logic::tribool(boost::logic::indeterminate);
}
+#if defined(__amd64__) || defined(__i386__) || defined(__x86_64__)
apm_info info;
if( ioctl(fd, APMIO_GETINFO, &info) == -1 ) {
close(fd);
@@ -1122,6 +1129,7 @@ namespace cryptonote
LOG_ERROR("sysctlbyname(\"hw.acpi.acline\") output is unexpectedly "
<< n << " bytes instead of the expected " << sizeof(ac) << " bytes.");
return boost::logic::tribool(boost::logic::indeterminate);
+#endif
}
return boost::logic::tribool(ac == 0);
#endif
diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h
index ac7a0381c..ce50d674e 100644
--- a/src/cryptonote_basic/miner.h
+++ b/src/cryptonote_basic/miner.h
@@ -52,13 +52,15 @@ namespace cryptonote
~i_miner_handler(){};
};
+ typedef std::function<bool(const cryptonote::block&, uint64_t, unsigned int, crypto::hash&)> get_block_hash_t;
+
/************************************************************************/
/* */
/************************************************************************/
class miner
{
public:
- miner(i_miner_handler* phandler);
+ miner(i_miner_handler* phandler, const get_block_hash_t& gbh);
~miner();
bool init(const boost::program_options::variables_map& vm, network_type nettype);
static void init_options(boost::program_options::options_description& desc);
@@ -74,7 +76,7 @@ namespace cryptonote
bool on_idle();
void on_synchronized();
//synchronous analog (for fast calls)
- static bool find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height);
+ static bool find_nonce_for_given_block(const get_block_hash_t &gbh, block& bl, const difficulty_type& diffic, uint64_t height);
void pause();
void resume();
void do_print_hashrate(bool do_hr);
@@ -133,6 +135,7 @@ namespace cryptonote
std::list<boost::thread> m_threads;
epee::critical_section m_threads_lock;
i_miner_handler* m_phandler;
+ get_block_hash_t m_gbh;
account_public_address m_mine_address;
epee::math_helper::once_a_time_seconds<5> m_update_block_template_interval;
epee::math_helper::once_a_time_seconds<2> m_update_merge_hr_interval;
diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h
index 3d7200fae..f5f663464 100644
--- a/src/cryptonote_basic/verification_context.h
+++ b/src/cryptonote_basic/verification_context.h
@@ -58,5 +58,6 @@ namespace cryptonote
bool m_marked_as_orphaned;
bool m_already_exists;
bool m_partial_block_reward;
+ bool m_bad_pow; // if bad pow, bad peer outright for DoS protection
};
}
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 173b454f6..134b630f7 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -96,10 +96,14 @@
#define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing
#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 100 //by default, blocks count in blocks downloading
#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading
+#define BLOCKS_SYNCHRONIZING_MAX_COUNT 2048 //must be a power of 2, greater than 128, equal to SEEDHASH_EPOCH_BLOCKS
#define CRYPTONOTE_MEMPOOL_TX_LIVETIME (86400*3) //seconds, three days
#define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week
+
+#define CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE 5 // seconds
+
// see src/cryptonote_protocol/levin_notify.cpp
#define CRYPTONOTE_NOISE_MIN_EPOCH 5 // minutes
#define CRYPTONOTE_NOISE_EPOCH_RANGE 30 // seconds
@@ -147,6 +151,7 @@
#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb"
#define CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME "lock.mdb"
#define P2P_NET_DATA_FILENAME "p2pstate.bin"
+#define RPC_PAYMENTS_DATA_FILENAME "rpcpayments.bin"
#define MINER_CONFIG_FILE_NAME "miner_conf.json"
#define THREAD_STACK_SIZE 5 * 1024 * 1024
@@ -160,10 +165,15 @@
#define HF_VERSION_SMALLER_BP 10
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10
#define HF_VERSION_MIN_2_OUTPUTS 12
+#define HF_VERSION_MIN_V2_COINBASE_TX 12
+#define HF_VERSION_SAME_MIXIN 12
+#define HF_VERSION_REJECT_SIGS_IN_COINBASE 12
+#define HF_VERSION_ENFORCE_MIN_AGE 12
+#define HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY 12
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
-#define HASH_OF_HASHES_STEP 256
+#define HASH_OF_HASHES_STEP 512
#define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes
@@ -174,6 +184,8 @@
#define CRYPTONOTE_PRUNING_TIP_BLOCKS 5500 // the smaller, the more space saved
//#define CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED
+#define RPC_CREDITS_PER_HASH_SCALE ((float)(1<<24))
+
// New constants are intended to go here
namespace config
{
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index 2cbe89b01..cb3875878 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -58,6 +58,7 @@ target_link_libraries(cryptonote_core
multisig
ringct
device
+ hardforks
${Boost_DATE_TIME_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_SERIALIZATION_LIBRARY}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 8ed0e526a..5070cc169 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -41,6 +41,7 @@
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
#include "cryptonote_basic/miner.h"
+#include "hardforks/hardforks.h"
#include "misc_language.h"
#include "profile_tools.h"
#include "file_io_utils.h"
@@ -83,95 +84,6 @@ DISABLE_VS_WARNINGS(4267)
// used to overestimate the block reward when estimating a per kB to use
#define BLOCK_REWARD_OVERESTIMATE (10 * 1000000000000)
-static const struct {
- uint8_t version;
- uint64_t height;
- uint8_t threshold;
- time_t time;
-} mainnet_hard_forks[] = {
- // version 1 from the start of the blockchain
- { 1, 1, 0, 1341378000 },
-
- // version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork.
- { 2, 1009827, 0, 1442763710 },
-
- // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21.
- { 3, 1141317, 0, 1458558528 },
-
- // 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 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14.
- { 5, 1288616, 0, 1489520158 },
-
- // version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18.
- { 6, 1400000, 0, 1503046577 },
-
- // version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17.
- { 7, 1546000, 0, 1521303150 },
-
- // version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02.
- { 8, 1685555, 0, 1535889547 },
-
- // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02.
- { 9, 1686275, 0, 1535889548 },
-
- // version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10.
- { 10, 1788000, 0, 1549792439 },
-
- // version 11 starts from block 1788720, which is on or around the 10th of March, 2019. Fork time finalised on 2019-02-15.
- { 11, 1788720, 0, 1550225678 },
-};
-static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
-
-static const struct {
- uint8_t version;
- uint64_t height;
- uint8_t threshold;
- time_t time;
-} testnet_hard_forks[] = {
- // version 1 from the start of the blockchain
- { 1, 1, 0, 1341378000 },
-
- // version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork.
- { 2, 624634, 0, 1445355000 },
-
- // versions 3-5 were passed in rapid succession from September 18th, 2016
- { 3, 800500, 0, 1472415034 },
- { 4, 801219, 0, 1472415035 },
- { 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
-
- { 6, 971400, 0, 1501709789 },
- { 7, 1057027, 0, 1512211236 },
- { 8, 1057058, 0, 1533211200 },
- { 9, 1057778, 0, 1533297600 },
- { 10, 1154318, 0, 1550153694 },
- { 11, 1155038, 0, 1550225678 },
-};
-static const uint64_t testnet_hard_fork_version_1_till = 624633;
-
-static const struct {
- uint8_t version;
- uint64_t height;
- uint8_t threshold;
- time_t time;
-} stagenet_hard_forks[] = {
- // version 1 from the start of the blockchain
- { 1, 1, 0, 1341378000 },
-
- // versions 2-7 in rapid succession from March 13th, 2018
- { 2, 32000, 0, 1521000000 },
- { 3, 33000, 0, 1521120000 },
- { 4, 34000, 0, 1521240000 },
- { 5, 35000, 0, 1521360000 },
- { 6, 36000, 0, 1521480000 },
- { 7, 37000, 0, 1521600000 },
- { 8, 176456, 0, 1537821770 },
- { 9, 177176, 0, 1537821771 },
- { 10, 269000, 0, 1550153694 },
- { 11, 269720, 0, 1550225678 },
-};
-
//------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
@@ -183,7 +95,8 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1),
m_btc_valid(false),
- m_batch_success(true)
+ m_batch_success(true),
+ m_prepare_height(0)
{
LOG_PRINT_L3("Blockchain::" << __func__);
}
@@ -403,17 +316,17 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
}
else if (m_nettype == TESTNET)
{
- for (size_t n = 0; n < sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]); ++n)
+ for (size_t n = 0; n < num_testnet_hard_forks; ++n)
m_hardfork->add_fork(testnet_hard_forks[n].version, testnet_hard_forks[n].height, testnet_hard_forks[n].threshold, testnet_hard_forks[n].time);
}
else if (m_nettype == STAGENET)
{
- for (size_t n = 0; n < sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]); ++n)
+ for (size_t n = 0; n < num_stagenet_hard_forks; ++n)
m_hardfork->add_fork(stagenet_hard_forks[n].version, stagenet_hard_forks[n].height, stagenet_hard_forks[n].threshold, stagenet_hard_forks[n].time);
}
else
{
- for (size_t n = 0; n < sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]); ++n)
+ for (size_t n = 0; n < num_mainnet_hard_forks; ++n)
m_hardfork->add_fork(mainnet_hard_forks[n].version, mainnet_hard_forks[n].height, mainnet_hard_forks[n].threshold, mainnet_hard_forks[n].time);
}
m_hardfork->init();
@@ -428,7 +341,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
{
MINFO("Blockchain not loaded, generating genesis block.");
block bl;
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
db_wtxn_guard wtxn_guard(m_db);
add_new_block(bl, bvc);
@@ -616,7 +529,7 @@ bool Blockchain::deinit()
// It starts a batch and calls private method pop_block_from_blockchain().
void Blockchain::pop_blocks(uint64_t nblocks)
{
- uint64_t i;
+ uint64_t i = 0;
CRITICAL_REGION_LOCAL(m_tx_pool);
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
@@ -627,9 +540,10 @@ void Blockchain::pop_blocks(uint64_t nblocks)
const uint64_t blockchain_height = m_db->height();
if (blockchain_height > 0)
nblocks = std::min(nblocks, blockchain_height - 1);
- for (i=0; i < nblocks; ++i)
+ while (i < nblocks)
{
pop_block_from_blockchain();
+ ++i;
}
}
catch (const std::exception& e)
@@ -702,7 +616,7 @@ block Blockchain::pop_block_from_blockchain()
// that might not be always true. Unlikely though, and always relaying
// these again might cause a spike of traffic as many nodes re-relay
// all the transactions in a popped block when a reorg happens.
- bool r = m_tx_pool.add_tx(tx, tvc, true, true, false, version);
+ bool r = m_tx_pool.add_tx(tx, tvc, relay_method::block, true, version);
if (!r)
{
LOG_ERROR("Error returning transaction to tx_pool");
@@ -736,7 +650,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
m_hardfork->init();
db_wtxn_guard wtxn_guard(m_db);
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
add_new_block(b, bvc);
if (!update_next_cumulative_weight_limit())
return false;
@@ -841,6 +755,13 @@ crypto::hash Blockchain::get_block_id_by_height(uint64_t height) const
return null_hash;
}
//------------------------------------------------------------------
+crypto::hash Blockchain::get_pending_block_id_by_height(uint64_t height) const
+{
+ if (m_prepare_height && height >= m_prepare_height && height - m_prepare_height < m_prepare_nblocks)
+ return (*m_prepare_blocks)[height - m_prepare_height].hash;
+ return get_block_id_by_height(height);
+}
+//------------------------------------------------------------------
bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orphan) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1006,7 +927,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
//return back original chain
for (auto& bl : original_chain)
{
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
bool r = handle_block_to_main_chain(bl, bvc);
CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!");
}
@@ -1055,7 +976,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
{
const auto &bei = *alt_ch_iter;
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
// add block to main chain
bool r = handle_block_to_main_chain(bei.bl, bvc);
@@ -1098,7 +1019,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
//pushing old chain as alternative chain
for (auto& old_ch_ent : disconnected_chain)
{
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc);
if(!r)
{
@@ -1116,6 +1037,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
}
m_hardfork->reorganize_from_chain_height(split_height);
+ get_block_longhash_reorg(split_height);
std::shared_ptr<tools::Notify> reorg_notify = m_reorg_notify;
if (reorg_notify)
@@ -1201,11 +1123,19 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
// one input, of type txin_gen, with height set to the block's height
// correct miner tx unlock time
// a non-overflowing tx amount (dubious necessity on this check)
-bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height)
+bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs");
CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
+ CHECK_AND_ASSERT_MES(b.miner_tx.version > 1 || hf_version < HF_VERSION_MIN_V2_COINBASE_TX, false, "Invalid coinbase transaction version");
+
+ // for v2 txes (ringct), we only accept empty rct signatures for miner transactions,
+ if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE && b.miner_tx.version >= 2)
+ {
+ CHECK_AND_ASSERT_MES(b.miner_tx.rct_signatures.type == rct::RCTTypeNull, false, "RingCT signatures not allowed in coinbase transactions");
+ }
+
if(boost::get<txin_gen>(b.miner_tx.vin[0]).height != height)
{
MWARNING("The miner transaction in block has invalid height: " << boost::get<txin_gen>(b.miner_tx.vin[0]).height << ", expected: " << height);
@@ -1246,16 +1176,25 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
}
}
- std::vector<uint64_t> last_blocks_weights;
- get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
- if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version))
+ uint64_t median_weight;
+ if (version >= HF_VERSION_EFFECTIVE_SHORT_TERM_MEDIAN_IN_PENALTY)
+ {
+ median_weight = m_current_block_cumul_weight_median;
+ }
+ else
+ {
+ std::vector<uint64_t> last_blocks_weights;
+ get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
+ median_weight = epee::misc_utils::median(last_blocks_weights);
+ }
+ if (!get_block_reward(median_weight, cumulative_block_weight, already_generated_coins, base_reward, version))
{
MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain");
return false;
}
if(base_reward + fee < money_in_use)
{
- MERROR_VER("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")");
+ MERROR_VER("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << "), cumulative_block_weight " << cumulative_block_weight);
return false;
}
// From hard fork 2, we allow a miner to claim less block reward than is allowed, in case a miner wants less dust
@@ -1385,7 +1324,9 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce
&& m_btc_pool_cookie == m_tx_pool.cookie() && m_btc.prev_id == get_tail_id()) {
MDEBUG("Using cached template");
- m_btc.timestamp = time(NULL); // update timestamp unconditionally
+ const uint64_t now = time(NULL);
+ if (m_btc.timestamp < now) // ensures it can't get below the median of the last few blocks
+ m_btc.timestamp = now;
b = m_btc;
diffic = m_btc_difficulty;
height = m_btc_height;
@@ -1412,7 +1353,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
//we have new block in alternative chain
std::list<block_extended_info> alt_chain;
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
std::vector<uint64_t> timestamps;
if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
return false;
@@ -1446,7 +1387,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
}
// FIXME: consider moving away from block_extended_info at some point
- block_extended_info bei = boost::value_initialized<block_extended_info>();
+ block_extended_info bei = {};
bei.bl = b;
bei.height = alt_chain.size() ? prev_data.height + 1 : m_db->get_block_height(*from_block) + 1;
@@ -1712,6 +1653,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
}
// this is a cheap test
+ const uint8_t hf_version = m_hardfork->get_ideal_version(block_height);
if (!m_hardfork->check_for_height(b, block_height))
{
LOG_PRINT_L1("Block with id: " << id << std::endl << "has old version for height " << block_height);
@@ -1733,7 +1675,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
return false;
// FIXME: consider moving away from block_extended_info at some point
- block_extended_info bei = boost::value_initialized<block_extended_info>();
+ block_extended_info bei = {};
bei.bl = b;
const uint64_t prev_height = alt_chain.size() ? prev_data.height : m_db->get_block_height(b.prev_id);
bei.height = prev_height + 1;
@@ -1761,16 +1703,41 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
// Check the block's hash against the difficulty target for its alt chain
difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei);
CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
- crypto::hash proof_of_work = null_hash;
- get_block_longhash(bei.bl, proof_of_work, bei.height);
+ crypto::hash proof_of_work;
+ memset(proof_of_work.data, 0xff, sizeof(proof_of_work.data));
+ if (b.major_version >= RX_BLOCK_VERSION)
+ {
+ crypto::hash seedhash = null_hash;
+ uint64_t seedheight = rx_seedheight(bei.height);
+ // seedblock is on the alt chain somewhere
+ if (alt_chain.size() && alt_chain.front().height <= seedheight)
+ {
+ for (auto it=alt_chain.begin(); it != alt_chain.end(); it++)
+ {
+ if (it->height == seedheight+1)
+ {
+ seedhash = it->bl.prev_id;
+ break;
+ }
+ }
+ } else
+ {
+ seedhash = get_block_id_by_height(seedheight);
+ }
+ get_altblock_longhash(bei.bl, proof_of_work, get_current_blockchain_height(), bei.height, seedheight, seedhash);
+ } else
+ {
+ get_block_longhash(this, bei.bl, proof_of_work, bei.height, 0);
+ }
if(!check_hash(proof_of_work, current_diff))
{
MERROR_VER("Block with id: " << id << std::endl << " for alternative chain, does not have enough proof of work: " << proof_of_work << std::endl << " expected difficulty: " << current_diff);
bvc.m_verifivation_failed = true;
+ bvc.m_bad_pow = true;
return false;
}
- if(!prevalidate_miner_transaction(b, bei.height))
+ if(!prevalidate_miner_transaction(b, bei.height, hf_version))
{
MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has incorrect miner transaction.");
bvc.m_verifivation_failed = true;
@@ -1793,6 +1760,43 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
}
bei.cumulative_difficulty += current_diff;
+ bei.block_cumulative_weight = cryptonote::get_transaction_weight(b.miner_tx);
+ for (const crypto::hash &txid: b.tx_hashes)
+ {
+ cryptonote::tx_memory_pool::tx_details td;
+ cryptonote::blobdata blob;
+ if (m_tx_pool.have_tx(txid, relay_category::legacy))
+ {
+ if (m_tx_pool.get_transaction_info(txid, td))
+ {
+ bei.block_cumulative_weight += td.weight;
+ }
+ else
+ {
+ MERROR_VER("Transaction is in the txpool, but metadata not found");
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+ }
+ else if (m_db->get_pruned_tx_blob(txid, blob))
+ {
+ cryptonote::transaction tx;
+ if (!cryptonote::parse_and_validate_tx_base_from_blob(blob, tx))
+ {
+ MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) refers to unparsable transaction hash " << txid << ".");
+ bvc.m_verifivation_failed = true;
+ return false;
+ }
+ bei.block_cumulative_weight += cryptonote::get_pruned_transaction_weight(tx);
+ }
+ else
+ {
+ // we can't determine the block weight, set it to 0 and break out of the loop
+ bei.block_cumulative_weight = 0;
+ break;
+ }
+ }
+
// add block to alternate blocks storage,
// as well as the current "alt chain" container
CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists");
@@ -1907,8 +1911,9 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
std::vector<std::pair<cryptonote::blobdata,block>> blocks;
get_blocks(arg.blocks, blocks, rsp.missed_ids);
- for (auto& bl: blocks)
+ for (size_t i = 0; i < blocks.size(); ++i)
{
+ auto& bl = blocks[i];
std::vector<crypto::hash> missed_tx_ids;
rsp.blocks.push_back(block_complete_entry());
@@ -1916,8 +1921,8 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
// FIXME: s/rsp.missed_ids/missed_tx_id/ ? Seems like rsp.missed_ids
// is for missed blocks, not missed transactions as well.
- get_transactions_blobs(bl.second.tx_hashes, e.txs, missed_tx_ids);
-
+ e.pruned = arg.prune;
+ get_transactions_blobs(bl.second.tx_hashes, e.txs, missed_tx_ids, arg.prune);
if (missed_tx_ids.size() != 0)
{
// do not display an error if the peer asked for an unpruned block which we are not meant to have
@@ -1938,6 +1943,9 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
//pack block
e.block = std::move(bl.first);
+ e.block_weight = 0;
+ if (arg.prune && m_db->block_exists(arg.blocks[i]))
+ e.block_weight = m_db->get_block_weight(m_db->get_block_height(arg.blocks[i]));
}
return true;
@@ -2216,23 +2224,95 @@ bool Blockchain::get_blocks(const t_ids_container& block_ids, t_blocks_container
return true;
}
//------------------------------------------------------------------
+static bool fill(BlockchainDB *db, const crypto::hash &tx_hash, cryptonote::blobdata &tx, bool pruned)
+{
+ if (pruned)
+ {
+ if (!db->get_pruned_tx_blob(tx_hash, tx))
+ {
+ MDEBUG("Pruned transaction blob not found for " << tx_hash);
+ return false;
+ }
+ }
+ else
+ {
+ if (!db->get_tx_blob(tx_hash, tx))
+ {
+ MDEBUG("Transaction blob not found for " << tx_hash);
+ return false;
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
+static bool fill(BlockchainDB *db, const crypto::hash &tx_hash, tx_blob_entry &tx, bool pruned)
+{
+ if (!fill(db, tx_hash, tx.blob, pruned))
+ return false;
+ if (pruned)
+ {
+ if (is_v1_tx(tx.blob))
+ {
+ // v1 txes aren't pruned, so fetch the whole thing
+ cryptonote::blobdata prunable_blob;
+ if (!db->get_prunable_tx_blob(tx_hash, prunable_blob))
+ {
+ MDEBUG("Prunable transaction blob not found for " << tx_hash);
+ return false;
+ }
+ tx.blob.append(prunable_blob);
+ tx.prunable_hash = crypto::null_hash;
+ }
+ else
+ {
+ if (!db->get_prunable_tx_hash(tx_hash, tx.prunable_hash))
+ {
+ MDEBUG("Prunable transaction data hash not found for " << tx_hash);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
//TODO: return type should be void, throw on exception
// alternatively, return true only if no transactions missed
-template<class t_ids_container, class t_tx_container, class t_missed_container>
-bool Blockchain::get_transactions_blobs(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs, bool pruned) const
+bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs, bool pruned) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- reserve_container(txs, txs_ids.size());
+ txs.reserve(txs_ids.size());
for (const auto& tx_hash : txs_ids)
{
try
{
cryptonote::blobdata tx;
- if (pruned && m_db->get_pruned_tx_blob(tx_hash, tx))
+ if (fill(m_db, tx_hash, tx, pruned))
txs.push_back(std::move(tx));
- else if (!pruned && m_db->get_tx_blob(tx_hash, tx))
+ else
+ missed_txs.push_back(tx_hash);
+ }
+ catch (const std::exception& e)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+//------------------------------------------------------------------
+bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<tx_blob_entry>& txs, std::vector<crypto::hash>& missed_txs, bool pruned) const
+{
+ LOG_PRINT_L3("Blockchain::" << __func__);
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
+ txs.reserve(txs_ids.size());
+ for (const auto& tx_hash : txs_ids)
+ {
+ try
+ {
+ tx_blob_entry tx;
+ if (fill(m_db, tx_hash, tx, pruned))
txs.push_back(std::move(tx));
else
missed_txs.push_back(tx_hash);
@@ -2325,7 +2405,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
// Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
-bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, std::vector<uint64_t>* weights, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2342,25 +2422,34 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
if (clip_pruned)
{
const uint32_t pruning_seed = get_blockchain_pruning_seed();
- start_height = tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed);
+ if (start_height < tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed))
+ {
+ MDEBUG("We only have a pruned version of the common ancestor");
+ return false;
+ }
stop_height = tools::get_next_pruned_block_height(start_height, current_height, pruning_seed);
}
size_t count = 0;
- hashes.reserve(std::min((size_t)(stop_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT));
+ const size_t reserve = std::min((size_t)(stop_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT);
+ hashes.reserve(reserve);
+ if (weights)
+ weights->reserve(reserve);
for(size_t i = start_height; i < stop_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
{
hashes.push_back(m_db->get_block_hash_from_height(i));
+ if (weights)
+ weights->push_back(m_db->get_block_weight(i));
}
return true;
}
-bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height, true);
+ bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, &resp.m_block_weights, resp.start_height, resp.total_height, clip_pruned);
if (result)
{
cryptonote::difficulty_type wide_cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1);
@@ -2799,18 +2888,24 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
// II
if (rv.type == rct::RCTTypeFull)
{
- rv.p.MGs.resize(1);
- rv.p.MGs[0].II.resize(tx.vin.size());
- for (size_t n = 0; n < tx.vin.size(); ++n)
- rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ if (!tx.pruned)
+ {
+ rv.p.MGs.resize(1);
+ rv.p.MGs[0].II.resize(tx.vin.size());
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ }
}
else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2)
{
- CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size");
- for (size_t n = 0; n < tx.vin.size(); ++n)
+ if (!tx.pruned)
{
- rv.p.MGs[n].II.resize(1);
- rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size");
+ for (size_t n = 0; n < tx.vin.size(); ++n)
+ {
+ rv.p.MGs[n].II.resize(1);
+ rv.p.MGs[n].II[0] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
+ }
}
}
else
@@ -2836,6 +2931,10 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
if(pmax_used_block_height)
*pmax_used_block_height = 0;
+ // pruned txes are skipped, as they're only allowed in sync-pruned-blocks mode, which is within the builtin hashes
+ if (tx.pruned)
+ return true;
+
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
const uint8_t hf_version = m_hardfork->get_current_version();
@@ -2858,7 +2957,8 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
if (hf_version >= 2)
{
size_t n_unmixable = 0, n_mixable = 0;
- size_t mixin = std::numeric_limits<size_t>::max();
+ size_t min_actual_mixin = std::numeric_limits<size_t>::max();
+ size_t max_actual_mixin = 0;
const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2;
for (const auto& txin : tx.vin)
{
@@ -2883,29 +2983,43 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
else
++n_mixable;
}
- if (in_to_key.key_offsets.size() - 1 < mixin)
- mixin = in_to_key.key_offsets.size() - 1;
+ size_t ring_mixin = in_to_key.key_offsets.size() - 1;
+ if (ring_mixin < min_actual_mixin)
+ min_actual_mixin = ring_mixin;
+ if (ring_mixin > max_actual_mixin)
+ max_actual_mixin = ring_mixin;
}
}
+ MDEBUG("Mixin: " << min_actual_mixin << "-" << max_actual_mixin);
- if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && mixin > 10))
+ if (hf_version >= HF_VERSION_SAME_MIXIN)
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11");
+ if (min_actual_mixin != max_actual_mixin)
+ {
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has varying ring size (" << (min_actual_mixin + 1) << "-" << (max_actual_mixin + 1) << "), it should be constant");
+ tvc.m_low_mixin = true;
+ return false;
+ }
+ }
+
+ if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && min_actual_mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && min_actual_mixin > 10))
+ {
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (min_actual_mixin + 1) << "), it should be 11");
tvc.m_low_mixin = true;
return false;
}
- if (mixin < min_mixin)
+ if (min_actual_mixin < min_mixin)
{
if (n_unmixable == 0)
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and no unmixable inputs");
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (min_actual_mixin + 1) << "), and no unmixable inputs");
tvc.m_low_mixin = true;
return false;
}
if (n_mixable > 1)
{
- MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and more than one mixable input with unmixable inputs");
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (min_actual_mixin + 1) << "), and more than one mixable input with unmixable inputs");
tvc.m_low_mixin = true;
return false;
}
@@ -2957,6 +3071,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const auto waiter_guard = epee::misc_utils::create_scope_leave_handler([&]() { waiter.wait(&tpool); });
int threads = tpool.get_max_concurrency();
+ uint64_t max_used_block_height = 0;
+ if (!pmax_used_block_height)
+ pmax_used_block_height = &max_used_block_height;
for (const auto& txin : tx.vin)
{
// make sure output being spent is of type txin_to_key, rather than
@@ -3023,6 +3140,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
if (tx.version == 1 && threads > 1)
waiter.wait(&tpool);
+ // enforce min output age
+ if (hf_version >= HF_VERSION_ENFORCE_MIN_AGE)
+ {
+ CHECK_AND_ASSERT_MES(*pmax_used_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE <= m_db->height(),
+ false, "Transaction spends at least one output which is too young");
+ }
+
if (tx.version == 1)
{
if (threads > 1)
@@ -3242,8 +3366,8 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b
if (version >= HF_VERSION_PER_BYTE_FEE)
{
lo = mul128(block_reward, DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT, &hi);
- div128_32(hi, lo, min_block_weight, &hi, &lo);
- div128_32(hi, lo, median_block_weight, &hi, &lo);
+ div128_64(hi, lo, min_block_weight, &hi, &lo, NULL, NULL);
+ div128_64(hi, lo, median_block_weight, &hi, &lo, NULL, NULL);
assert(hi == 0);
lo /= 5;
return lo;
@@ -3253,12 +3377,7 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b
uint64_t unscaled_fee_base = (fee_base * min_block_weight / median_block_weight);
lo = mul128(unscaled_fee_base, 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);
+ div128_64(hi, lo, DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD, &hi, &lo, NULL, NULL);
assert(hi == 0);
// quantize fee up to 8 decimals
@@ -3355,7 +3474,8 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
}
const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT;
- uint64_t fee = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version);
+ const uint64_t use_median_value = use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median;
+ const uint64_t fee = get_dynamic_base_fee(base_reward, use_median_value, version);
const bool per_byte = version < HF_VERSION_PER_BYTE_FEE;
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB"));
return fee;
@@ -3521,7 +3641,7 @@ void Blockchain::return_tx_to_pool(std::vector<std::pair<transaction, blobdata>>
// all the transactions in a popped block when a reorg happens.
const size_t weight = get_transaction_weight(tx.first, tx.second.size());
const crypto::hash tx_hash = get_transaction_hash(tx.first);
- if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, true, true, false, version))
+ if (!m_tx_pool.add_tx(tx.first, tx_hash, tx.second, weight, tvc, relay_method::block, true, version))
{
MERROR("Failed to return taken transaction with hash: " << get_transaction_hash(tx.first) << " to tx_pool");
}
@@ -3539,9 +3659,9 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
cryptonote::blobdata txblob;
size_t tx_weight;
uint64_t fee;
- bool relayed, do_not_relay, double_spend_seen;
+ bool relayed, do_not_relay, double_spend_seen, pruned;
MINFO("Removing txid " << txid << " from the pool");
- if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
+ if(!m_tx_pool.have_tx(txid, relay_category::all) && !m_tx_pool.take_tx(txid, tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned))
{
MERROR("Failed to remove txid " << txid << " from the pool");
res = false;
@@ -3588,9 +3708,10 @@ leave:
}
// this is a cheap test
+ const uint8_t hf_version = get_current_hard_fork_version();
if (!m_hardfork->check(bl))
{
- MERROR_VER("Block with id: " << id << std::endl << "has old version: " << (unsigned)bl.major_version << std::endl << "current: " << (unsigned)m_hardfork->get_current_version());
+ MERROR_VER("Block with id: " << id << std::endl << "has old version: " << (unsigned)bl.major_version << std::endl << "current: " << (unsigned)hf_version);
bvc.m_verifivation_failed = true;
goto leave;
}
@@ -3623,7 +3744,8 @@ leave:
TIME_MEASURE_START(longhash_calculating_time);
- crypto::hash proof_of_work = null_hash;
+ crypto::hash proof_of_work;
+ memset(proof_of_work.data, 0xff, sizeof(proof_of_work.data));
// Formerly the code below contained an if loop with the following condition
// !m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())
@@ -3639,7 +3761,7 @@ leave:
#if defined(PER_BLOCK_CHECKPOINT)
if (blockchain_height < m_blocks_hash_check.size())
{
- const auto &expected_hash = m_blocks_hash_check[blockchain_height];
+ const auto &expected_hash = m_blocks_hash_check[blockchain_height].first;
if (expected_hash != crypto::null_hash)
{
if (memcmp(&id, &expected_hash, sizeof(hash)) != 0)
@@ -3665,13 +3787,14 @@ leave:
proof_of_work = it->second;
}
else
- proof_of_work = get_block_longhash(bl, blockchain_height);
+ proof_of_work = get_block_longhash(this, bl, blockchain_height, 0);
// validate proof_of_work versus difficulty target
if(!check_hash(proof_of_work, current_diffic))
{
MERROR_VER("Block with id: " << id << std::endl << "does not have enough proof of work: " << proof_of_work << " at height " << blockchain_height << ", unexpected difficulty: " << current_diffic);
bvc.m_verifivation_failed = true;
+ bvc.m_bad_pow = true;
goto leave;
}
}
@@ -3695,7 +3818,7 @@ leave:
TIME_MEASURE_START(t3);
// sanity check basic miner tx properties;
- if(!prevalidate_miner_transaction(bl, blockchain_height))
+ if(!prevalidate_miner_transaction(bl, blockchain_height, hf_version))
{
MERROR_VER("Block with id: " << id << " failed to pass prevalidation");
bvc.m_verifivation_failed = true;
@@ -3713,6 +3836,7 @@ leave:
uint64_t t_exists = 0;
uint64_t t_pool = 0;
uint64_t t_dblspnd = 0;
+ uint64_t n_pruned = 0;
TIME_MEASURE_FINISH(t3);
// XXX old code adds miner tx here
@@ -3728,7 +3852,7 @@ leave:
blobdata txblob;
size_t tx_weight = 0;
uint64_t fee = 0;
- bool relayed = false, do_not_relay = false, double_spend_seen = false;
+ bool relayed = false, do_not_relay = false, double_spend_seen = false, pruned = false;
TIME_MEASURE_START(aa);
// XXX old code does not check whether tx exists
@@ -3745,13 +3869,15 @@ leave:
TIME_MEASURE_START(bb);
// get transaction with hash <tx_id> from tx_pool
- if(!m_tx_pool.take_tx(tx_id, tx_tmp, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
+ if(!m_tx_pool.take_tx(tx_id, tx_tmp, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned))
{
MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
bvc.m_verifivation_failed = true;
return_tx_to_pool(txs);
goto leave;
}
+ if (pruned)
+ ++n_pruned;
TIME_MEASURE_FINISH(bb);
t_pool += bb;
@@ -3822,6 +3948,17 @@ leave:
cumulative_block_weight += tx_weight;
}
+ // if we were syncing pruned blocks
+ if (n_pruned > 0)
+ {
+ if (blockchain_height >= m_blocks_hash_check.size() || m_blocks_hash_check[blockchain_height].second == 0)
+ {
+ MERROR("Block at " << blockchain_height << " is pruned, but we do not have a weight for it");
+ goto leave;
+ }
+ cumulative_block_weight = m_blocks_hash_check[blockchain_height].second;
+ }
+
m_blocks_txs_check.clear();
TIME_MEASURE_START(vmt);
@@ -4166,7 +4303,7 @@ void Blockchain::block_longhash_worker(uint64_t height, const epee::span<const b
if (m_cancel)
break;
crypto::hash id = get_block_hash(block);
- crypto::hash pow = get_block_longhash(block, height++);
+ crypto::hash pow = get_block_longhash(this, block, height++, 0);
map.emplace(id, pow);
}
@@ -4262,11 +4399,13 @@ void Blockchain::output_scan_worker(const uint64_t amount, const std::vector<uin
}
}
-uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes)
+uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights)
{
// new: . . . . . X X X X X . . . . . .
// pre: A A A A B B B B C C C C D D D D
+ CHECK_AND_ASSERT_MES(weights.empty() || weights.size() == hashes.size(), 0, "Unexpected weights size");
+
// easy case: height >= hashes
if (height >= m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP)
return hashes.size();
@@ -4285,8 +4424,11 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
return hashes.size();
// build hashes vector to hash hashes together
- std::vector<crypto::hash> data;
- data.reserve(hashes.size() + HASH_OF_HASHES_STEP - 1); // may be a bit too much
+ std::vector<crypto::hash> data_hashes;
+ std::vector<uint64_t> data_weights;
+ data_hashes.reserve(hashes.size() + HASH_OF_HASHES_STEP - 1); // may be a bit too much
+ if (!weights.empty())
+ data_weights.reserve(data_hashes.size());
// we expect height to be either equal or a bit below db height
bool disconnected = (height > m_db->height());
@@ -4301,18 +4443,24 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
// we might need some already in the chain for the first part of the first hash
for (uint64_t h = first_index * HASH_OF_HASHES_STEP; h < height; ++h)
{
- data.push_back(m_db->get_block_hash_from_height(h));
+ data_hashes.push_back(m_db->get_block_hash_from_height(h));
+ if (!weights.empty())
+ data_weights.push_back(m_db->get_block_weight(h));
}
pop = 0;
}
// push the data to check
- for (const auto &h: hashes)
+ for (size_t i = 0; i < hashes.size(); ++i)
{
if (pop)
--pop;
else
- data.push_back(h);
+ {
+ data_hashes.push_back(hashes[i]);
+ if (!weights.empty())
+ data_weights.push_back(weights[i]);
+ }
}
// hash and check
@@ -4322,12 +4470,17 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
if (n < m_blocks_hash_of_hashes.size())
{
// if the last index isn't fully filled, we can't tell if valid
- if (data.size() < (n - first_index) * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP)
+ if (data_hashes.size() < (n - first_index) * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP)
break;
crypto::hash hash;
- cn_fast_hash(data.data() + (n - first_index) * HASH_OF_HASHES_STEP, HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash);
- bool valid = hash == m_blocks_hash_of_hashes[n];
+ cn_fast_hash(data_hashes.data() + (n - first_index) * HASH_OF_HASHES_STEP, HASH_OF_HASHES_STEP * sizeof(crypto::hash), hash);
+ bool valid = hash == m_blocks_hash_of_hashes[n].first;
+ if (valid && !weights.empty())
+ {
+ cn_fast_hash(data_weights.data() + (n - first_index) * HASH_OF_HASHES_STEP, HASH_OF_HASHES_STEP * sizeof(uint64_t), hash);
+ valid &= hash == m_blocks_hash_of_hashes[n].second;
+ }
// add to the known hashes array
if (!valid)
@@ -4339,9 +4492,15 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
size_t end = n * HASH_OF_HASHES_STEP + HASH_OF_HASHES_STEP;
for (size_t i = n * HASH_OF_HASHES_STEP; i < end; ++i)
{
- CHECK_AND_ASSERT_MES(m_blocks_hash_check[i] == crypto::null_hash || m_blocks_hash_check[i] == data[i - first_index * HASH_OF_HASHES_STEP],
+ CHECK_AND_ASSERT_MES(m_blocks_hash_check[i].first == crypto::null_hash || m_blocks_hash_check[i].first == data_hashes[i - first_index * HASH_OF_HASHES_STEP],
0, "Consistency failure in m_blocks_hash_check construction");
- m_blocks_hash_check[i] = data[i - first_index * HASH_OF_HASHES_STEP];
+ m_blocks_hash_check[i].first = data_hashes[i - first_index * HASH_OF_HASHES_STEP];
+ if (!weights.empty())
+ {
+ CHECK_AND_ASSERT_MES(m_blocks_hash_check[i].second == 0 || m_blocks_hash_check[i].second == data_weights[i - first_index * HASH_OF_HASHES_STEP],
+ 0, "Consistency failure in m_blocks_hash_check construction");
+ m_blocks_hash_check[i].second = data_weights[i - first_index * HASH_OF_HASHES_STEP];
+ }
}
usable += HASH_OF_HASHES_STEP;
}
@@ -4358,6 +4517,18 @@ uint64_t Blockchain::prevalidate_block_hashes(uint64_t height, const std::vector
return usable;
}
+bool Blockchain::has_block_weights(uint64_t height, uint64_t nblocks) const
+{
+ CHECK_AND_ASSERT_MES(nblocks > 0, false, "nblocks is 0");
+ uint64_t last_block_height = height + nblocks - 1;
+ if (last_block_height >= m_blocks_hash_check.size())
+ return false;
+ for (uint64_t h = height; h <= last_block_height; ++h)
+ if (m_blocks_hash_check[h].second == 0)
+ return false;
+ return true;
+}
+
//------------------------------------------------------------------
// ND: Speedups:
// 1. Thread long_hash computations if possible (m_max_prepare_blocks_threads = nthreads, default = 4)
@@ -4399,7 +4570,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
bytes += entry.block.size();
for (const auto &tx_blob : entry.txs)
{
- bytes += tx_blob.size();
+ bytes += tx_blob.blob.size();
}
total_txs += entry.txs.size();
}
@@ -4482,6 +4653,9 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
m_blocks_longhash_table.clear();
uint64_t thread_height = height;
tools::threadpool::waiter waiter;
+ m_prepare_height = height;
+ m_prepare_nblocks = blocks_entry.size();
+ m_prepare_blocks = &blocks;
for (unsigned int i = 0; i < threads; i++)
{
unsigned nblocks = batches;
@@ -4492,6 +4666,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
waiter.wait(&tpool);
+ m_prepare_height = 0;
if (m_cancel)
return false;
@@ -4555,7 +4730,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
crypto::hash &tx_prefix_hash = txes[tx_index].second;
++tx_index;
- if (!parse_and_validate_tx_base_from_blob(tx_blob, tx))
+ if (!parse_and_validate_tx_base_from_blob(tx_blob.blob, tx))
SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash);
@@ -4720,9 +4895,9 @@ void Blockchain::remove_txpool_tx(const crypto::hash &txid)
m_db->remove_txpool_tx(txid);
}
-uint64_t Blockchain::get_txpool_tx_count(bool include_unrelayed_txes) const
+uint64_t Blockchain::get_txpool_tx_count(bool include_sensitive) const
{
- return m_db->get_txpool_tx_count(include_unrelayed_txes);
+ return m_db->get_txpool_tx_count(include_sensitive ? relay_category::all : relay_category::broadcasted);
}
bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const
@@ -4730,19 +4905,24 @@ bool Blockchain::get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &
return m_db->get_txpool_tx_meta(txid, meta);
}
-bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const
+bool Blockchain::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const
{
- return m_db->get_txpool_tx_blob(txid, bd);
+ return m_db->get_txpool_tx_blob(txid, bd, tx_category);
}
-cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid) const
+cryptonote::blobdata Blockchain::get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const
{
- return m_db->get_txpool_tx_blob(txid);
+ return m_db->get_txpool_tx_blob(txid, tx_category);
}
-bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, bool include_unrelayed_txes) const
+bool Blockchain::for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob, relay_category tx_category) const
{
- return m_db->for_all_txpool_txes(f, include_blob, include_unrelayed_txes);
+ return m_db->for_all_txpool_txes(f, include_blob, tx_category);
+}
+
+bool Blockchain::txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category)
+{
+ return m_db->txpool_tx_matches_category(tx_hash, category);
}
void Blockchain::set_user_options(uint64_t maxthreads, bool sync_on_blocks, uint64_t sync_threshold, blockchain_db_sync_mode sync_mode, bool fast_sync)
@@ -4776,39 +4956,6 @@ HardFork::State Blockchain::get_hard_fork_state() const
return m_hardfork->get_state();
}
-const std::vector<HardFork::Params>& Blockchain::get_hard_fork_heights(network_type nettype)
-{
- static const std::vector<HardFork::Params> mainnet_heights = []()
- {
- std::vector<HardFork::Params> heights;
- for (const auto& i : mainnet_hard_forks)
- heights.emplace_back(i.version, i.height, i.threshold, i.time);
- return heights;
- }();
- static const std::vector<HardFork::Params> testnet_heights = []()
- {
- std::vector<HardFork::Params> heights;
- for (const auto& i : testnet_hard_forks)
- heights.emplace_back(i.version, i.height, i.threshold, i.time);
- return heights;
- }();
- static const std::vector<HardFork::Params> stagenet_heights = []()
- {
- std::vector<HardFork::Params> heights;
- for (const auto& i : stagenet_hard_forks)
- heights.emplace_back(i.version, i.height, i.threshold, i.time);
- return heights;
- }();
- static const std::vector<HardFork::Params> dummy;
- switch (nettype)
- {
- case MAINNET: return mainnet_heights;
- case TESTNET: return testnet_heights;
- case STAGENET: return stagenet_heights;
- default: return dummy;
- }
-}
-
bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint64_t &earliest_height, uint8_t &voting) const
{
return m_hardfork->get_voting_info(version, window, votes, threshold, earliest_height, voting);
@@ -4887,7 +5034,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "7dafb40b414a0e59bfced6682ef519f0b416bc914dd3d622b72e0dd1a47117c2";
+static const char expected_block_hashes_hash[] = "fce1dc7c17f7679f5f447df206b8f5fe2ef6b1a2845e59f650850a0ef00d265f";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
@@ -4931,19 +5078,26 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get
MERROR("Block hash data is too large");
return;
}
- const size_t size_needed = 4 + nblocks * sizeof(crypto::hash);
- if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP && checkpoints.size() >= size_needed)
+ const size_t size_needed = 4 + nblocks * (sizeof(crypto::hash) * 2);
+ if(checkpoints.size() != size_needed)
+ {
+ MERROR("Failed to load hashes - unexpected data size");
+ return;
+ }
+ else if(nblocks > 0 && nblocks > (m_db->height() + HASH_OF_HASHES_STEP - 1) / HASH_OF_HASHES_STEP)
{
p += sizeof(uint32_t);
m_blocks_hash_of_hashes.reserve(nblocks);
for (uint32_t i = 0; i < nblocks; i++)
{
- crypto::hash hash;
- memcpy(hash.data, p, sizeof(hash.data));
- p += sizeof(hash.data);
- m_blocks_hash_of_hashes.push_back(hash);
+ crypto::hash hash_hashes, hash_weights;
+ memcpy(hash_hashes.data, p, sizeof(hash_hashes.data));
+ p += sizeof(hash_hashes.data);
+ memcpy(hash_weights.data, p, sizeof(hash_weights.data));
+ p += sizeof(hash_weights.data);
+ m_blocks_hash_of_hashes.push_back(std::make_pair(hash_hashes, hash_weights));
}
- m_blocks_hash_check.resize(m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP, crypto::null_hash);
+ m_blocks_hash_check.resize(m_blocks_hash_of_hashes.size() * HASH_OF_HASHES_STEP, std::make_pair(crypto::null_hash, 0));
MINFO(nblocks << " block hashes loaded");
// FIXME: clear tx_pool because the process might have been
@@ -4954,17 +5108,17 @@ void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get
CRITICAL_REGION_LOCAL(m_tx_pool);
std::vector<transaction> txs;
- m_tx_pool.get_transactions(txs);
+ m_tx_pool.get_transactions(txs, true);
size_t tx_weight;
uint64_t fee;
- bool relayed, do_not_relay, double_spend_seen;
+ bool relayed, do_not_relay, double_spend_seen, pruned;
transaction pool_tx;
blobdata txblob;
for(const transaction &tx : txs)
{
crypto::hash tx_hash = get_transaction_hash(tx);
- m_tx_pool.take_tx(tx_hash, pool_tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen);
+ m_tx_pool.take_tx(tx_hash, pool_tx, txblob, tx_weight, fee, relayed, do_not_relay, double_spend_seen, pruned);
}
}
}
@@ -5037,6 +5191,5 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
namespace cryptonote {
template bool Blockchain::get_transactions(const std::vector<crypto::hash>&, std::vector<transaction>&, std::vector<crypto::hash>&) const;
-template bool Blockchain::get_transactions_blobs(const std::vector<crypto::hash>&, std::vector<cryptonote::blobdata>&, std::vector<crypto::hash>&, bool) const;
template bool Blockchain::get_split_transactions_blobs(const std::vector<crypto::hash>&, std::vector<std::tuple<crypto::hash, cryptonote::blobdata, crypto::hash, cryptonote::blobdata>>&, std::vector<crypto::hash>&) const;
}
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index f58059a6d..0aecdcb57 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -206,6 +206,18 @@ namespace cryptonote
crypto::hash get_block_id_by_height(uint64_t height) const;
/**
+ * @brief gets a block's hash given a height
+ *
+ * Used only by prepare_handle_incoming_blocks. Will look in the list of incoming blocks
+ * if the height is contained there.
+ *
+ * @param height the height of the block
+ *
+ * @return the hash of the block at the requested height, or a zeroed hash if there is no such block
+ */
+ crypto::hash get_pending_block_id_by_height(uint64_t height) const;
+
+ /**
* @brief gets the block with a given hash
*
* @param h the hash to look for
@@ -381,13 +393,14 @@ namespace cryptonote
*
* @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
* @param hashes the hashes to be returned, return-by-reference
+ * @param weights the block weights to be returned, return-by-reference
* @param start_height the start height, return-by-reference
* @param current_height the current blockchain height, return-by-reference
* @param clip_pruned whether to constrain results to unpruned data
*
* @return true if a block found in common, else false
*/
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, std::vector<uint64_t>* weights, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const;
/**
* @brief get recent block hashes for a foreign chain
@@ -397,11 +410,12 @@ namespace cryptonote
* BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
*
* @param qblock_ids the foreign chain's "short history" (see get_short_chain_history)
+ * @param clip_pruned clip pruned blocks if true, include them otherwise
* @param resp return-by-reference the split height and subsequent blocks' hashes
*
* @return true if a block found in common, else false
*/
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
/**
* @brief find the most recent common point between ours and a foreign chain
@@ -675,8 +689,8 @@ 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, bool pruned = false) const;
+ bool get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<cryptonote::blobdata>& txs, std::vector<crypto::hash>& missed_txs, bool pruned = false) const;
+ bool get_transactions_blobs(const std::vector<crypto::hash>& txs_ids, std::vector<tx_blob_entry>& txs, std::vector<crypto::hash>& missed_txs, bool pruned = false) const;
template<class t_ids_container, class t_tx_container, class t_missed_container>
bool get_split_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>
@@ -763,13 +777,6 @@ namespace cryptonote
HardFork::State get_hard_fork_state() const;
/**
- * @brief gets the hardfork heights of given network
- *
- * @return the HardFork object
- */
- static const std::vector<HardFork::Params>& get_hard_fork_heights(network_type nettype);
-
- /**
* @brief gets the current hardfork version in use/voted for
*
* @return the version
@@ -957,15 +964,15 @@ namespace cryptonote
void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta);
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
void remove_txpool_tx(const crypto::hash &txid);
- uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const;
+ uint64_t get_txpool_tx_count(bool include_sensitive = false) const;
bool get_txpool_tx_meta(const crypto::hash& txid, txpool_tx_meta_t &meta) const;
- bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const;
- cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const;
- bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = true) const;
+ bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd, relay_category tx_category) const;
+ cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const;
+ bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, relay_category tx_category = relay_category::broadcasted) const;
+ bool txpool_tx_matches_category(const crypto::hash& tx_hash, relay_category category);
- bool is_within_compiled_block_hash_area(uint64_t height) const;
bool is_within_compiled_block_hash_area() const { return is_within_compiled_block_hash_area(m_db->height()); }
- uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes);
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights);
uint32_t get_blockchain_pruning_seed() const { return m_db->get_blockchain_pruning_seed(); }
bool prune_blockchain(uint32_t pruning_seed = 0);
bool update_blockchain_pruning();
@@ -995,6 +1002,21 @@ namespace cryptonote
*/
void pop_blocks(uint64_t nblocks);
+ /**
+ * @brief checks whether a given block height is included in the precompiled block hash area
+ *
+ * @param height the height to check for
+ */
+ bool is_within_compiled_block_hash_area(uint64_t height) const;
+
+ /**
+ * @brief checks whether we have known weights for the given block heights
+ *
+ * @param height the start height to check for
+ * @param nblocks how many blocks to check from that height
+ */
+ bool has_block_weights(uint64_t height, uint64_t nblocks) const;
+
#ifndef IN_UNIT_TESTS
private:
#endif
@@ -1021,9 +1043,9 @@ namespace cryptonote
std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table;
std::unordered_map<crypto::hash, crypto::hash> m_blocks_longhash_table;
- // SHA-3 hashes for each block and for fast pow checking
- std::vector<crypto::hash> m_blocks_hash_of_hashes;
- std::vector<crypto::hash> m_blocks_hash_check;
+ // Keccak hashes for each block and for fast pow checking
+ std::vector<std::pair<crypto::hash, crypto::hash>> m_blocks_hash_of_hashes;
+ std::vector<std::pair<crypto::hash, uint64_t>> m_blocks_hash_check;
std::vector<crypto::hash> m_blocks_txs_check;
blockchain_db_sync_mode m_db_sync_mode;
@@ -1084,6 +1106,11 @@ namespace cryptonote
std::shared_ptr<tools::Notify> m_block_notify;
std::shared_ptr<tools::Notify> m_reorg_notify;
+ // for prepare_handle_incoming_blocks
+ uint64_t m_prepare_height;
+ uint64_t m_prepare_nblocks;
+ std::vector<block> *m_prepare_blocks;
+
/**
* @brief collects the keys for all outputs being "spent" as an input
*
@@ -1245,10 +1272,11 @@ namespace cryptonote
*
* @param b the block containing the miner transaction
* @param height the height at which the block will be added
+ * @param hf_version the consensus rules to apply
*
* @return false if anything is found wrong with the miner transaction, otherwise true
*/
- bool prevalidate_miner_transaction(const block& b, uint64_t height);
+ bool prevalidate_miner_transaction(const block& b, uint64_t height, uint8_t hf_version);
/**
* @brief validates a miner (coinbase) transaction
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index a3a92ab60..23f13000c 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <boost/algorithm/string.hpp>
+#include <boost/uuid/nil_generator.hpp>
#include "string_tools.h"
using namespace epee;
@@ -51,6 +52,7 @@ using namespace epee;
#include "blockchain_db/blockchain_db.h"
#include "ringct/rctSigs.h"
#include "common/notify.h"
+#include "hardforks/hardforks.h"
#include "version.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -113,6 +115,10 @@ namespace cryptonote
, "Set maximum size of block download queue in bytes (0 for default)"
, 0
};
+ const command_line::arg_descriptor<bool> arg_sync_pruned_blocks = {
+ "sync-pruned-blocks"
+ , "Allow syncing from nodes with only pruned blocks"
+ };
static const command_line::arg_descriptor<bool> arg_test_drop_download = {
"test-drop-download"
@@ -168,11 +174,6 @@ namespace cryptonote
, "Relay blocks as normal blocks"
, false
};
- static const command_line::arg_descriptor<bool> arg_pad_transactions = {
- "pad-transactions"
- , "Pad relayed transactions to help defend against traffic volume analysis"
- , false
- };
static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
"max-txpool-weight"
, "Set maximum txpool weight in bytes."
@@ -218,7 +219,9 @@ namespace cryptonote
core::core(i_cryptonote_protocol* pprotocol):
m_mempool(m_blockchain_storage),
m_blockchain_storage(m_mempool),
- m_miner(this),
+ m_miner(this, [this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) {
+ return cryptonote::get_block_longhash(&m_blockchain_storage, b, hash, height, threads);
+ }),
m_starter_message_showed(false),
m_target_blockchain_height(0),
m_checkpoints_path(""),
@@ -227,8 +230,7 @@ namespace cryptonote
m_disable_dns_checkpoints(false),
m_update_download(0),
m_nettype(UNDEFINED),
- m_update_available(false),
- m_pad_transactions(false)
+ m_update_available(false)
{
m_checkpoints_updating.clear();
set_cryptonote_protocol(pprotocol);
@@ -323,8 +325,8 @@ namespace cryptonote
command_line::add_arg(desc, arg_offline);
command_line::add_arg(desc, arg_disable_dns_checkpoints);
command_line::add_arg(desc, arg_block_download_max_size);
+ command_line::add_arg(desc, arg_sync_pruned_blocks);
command_line::add_arg(desc, arg_max_txpool_weight);
- command_line::add_arg(desc, arg_pad_transactions);
command_line::add_arg(desc, arg_block_notify);
command_line::add_arg(desc, arg_prune_blockchain);
command_line::add_arg(desc, arg_reorg_notify);
@@ -367,7 +369,6 @@ namespace cryptonote
set_enforce_dns_checkpoints(command_line::get_arg(vm, arg_dns_checkpoints));
test_drop_download_height(command_line::get_arg(vm, arg_test_drop_download_height));
m_fluffy_blocks_enabled = !get_arg(vm, arg_no_fluffy_blocks);
- m_pad_transactions = get_arg(vm, arg_pad_transactions);
m_offline = get_arg(vm, arg_offline);
m_disable_dns_checkpoints = get_arg(vm, arg_disable_dns_checkpoints);
if (!command_line::is_arg_defaulted(vm, arg_fluffy_blocks))
@@ -454,7 +455,6 @@ namespace cryptonote
bool r = handle_command_line(vm);
CHECK_AND_ASSERT_MES(r, false, "Failed to handle command line");
- std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode);
bool db_salvage = command_line::get_arg(vm, cryptonote::arg_db_salvage) != 0;
bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0;
@@ -488,10 +488,10 @@ namespace cryptonote
// folder might not be a directory, etc, etc
catch (...) { }
- std::unique_ptr<BlockchainDB> db(new_db(db_type));
+ std::unique_ptr<BlockchainDB> db(new_db());
if (db == NULL)
{
- LOG_ERROR("Attempted to use non-existent database type");
+ LOG_ERROR("Failed to initialize a database");
return false;
}
@@ -634,7 +634,7 @@ namespace cryptonote
MERROR("Failed to parse block rate notify spec: " << e.what());
}
- const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(Blockchain::get_hard_fork_heights(MAINNET).back().version, 1), std::make_pair(0, 0)};
+ const std::pair<uint8_t, uint64_t> regtest_hard_forks[3] = {std::make_pair(1, 0), std::make_pair(mainnet_hard_forks[num_mainnet_hard_forks-1].version, 1), std::make_pair(0, 0)};
const cryptonote::test_options regtest_test_options = {
regtest_hard_forks,
0
@@ -655,6 +655,8 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
block_sync_size = command_line::get_arg(vm, arg_block_sync_size);
+ if (block_sync_size > BLOCKS_SYNCHRONIZING_MAX_COUNT)
+ MERROR("Error --block-sync-size cannot be greater than " << BLOCKS_SYNCHRONIZING_MAX_COUNT);
MGINFO("Loading checkpoints");
@@ -744,13 +746,13 @@ namespace cryptonote
return false;
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash)
{
- tvc = boost::value_initialized<tx_verification_context>();
+ tvc = {};
- if(tx_blob.size() > get_max_tx_size())
+ if(tx_blob.blob.size() > get_max_tx_size())
{
- LOG_PRINT_L1("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected");
+ LOG_PRINT_L1("WRONG TRANSACTION BLOB, too big size " << tx_blob.blob.size() << ", rejected");
tvc.m_verifivation_failed = true;
tvc.m_too_big = true;
return false;
@@ -758,7 +760,23 @@ namespace cryptonote
tx_hash = crypto::null_hash;
- if(!parse_tx_from_blob(tx, tx_hash, tx_blob))
+ bool r;
+ if (tx_blob.prunable_hash == crypto::null_hash)
+ {
+ r = parse_tx_from_blob(tx, tx_hash, tx_blob.blob);
+ }
+ else
+ {
+ r = parse_and_validate_tx_base_from_blob(tx_blob.blob, tx);
+ if (r)
+ {
+ tx.set_prunable_hash(tx_blob.prunable_hash);
+ tx_hash = cryptonote::get_pruned_transaction_hash(tx, tx_blob.prunable_hash);
+ tx.set_hash(tx_hash);
+ }
+ }
+
+ if (!r)
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to parse, rejected");
tvc.m_verifivation_failed = true;
@@ -784,6 +802,7 @@ namespace cryptonote
if (tx.version == 0 || tx.version > max_tx_version)
{
// v2 is the latest one we know
+ MERROR_VER("Bad tx version (" << tx.version << ", max is " << max_tx_version << ")");
tvc.m_verifivation_failed = true;
return false;
}
@@ -791,7 +810,7 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash)
{
if(!check_tx_syntax(tx))
{
@@ -920,23 +939,29 @@ namespace cryptonote
return ret;
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::handle_incoming_txs(const epee::span<const tx_blob_entry> tx_blobs, epee::span<tx_verification_context> tvc, relay_method tx_relay, bool relayed)
{
TRY_ENTRY();
- CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
+
+ if (tx_blobs.size() != tvc.size())
+ {
+ MERROR("tx_blobs and tx_verification_context spans must have equal size");
+ return false;
+ }
struct result { bool res; cryptonote::transaction tx; crypto::hash hash; };
std::vector<result> results(tx_blobs.size());
- tvc.resize(tx_blobs.size());
+ CRITICAL_REGION_LOCAL(m_incoming_tx_lock);
+
tools::threadpool& tpool = tools::threadpool::getInstance();
tools::threadpool::waiter waiter;
- std::vector<blobdata>::const_iterator it = tx_blobs.begin();
+ epee::span<tx_blob_entry>::const_iterator it = tx_blobs.begin();
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
tpool.submit(&waiter, [&, i, it] {
try
{
- results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay);
+ results[i].res = handle_incoming_tx_pre(*it, tvc[i], results[i].tx, results[i].hash);
}
catch (const std::exception &e)
{
@@ -952,7 +977,7 @@ namespace cryptonote
for (size_t i = 0; i < tx_blobs.size(); i++, ++it) {
if (!results[i].res)
continue;
- if(m_mempool.have_tx(results[i].hash))
+ if(m_mempool.have_tx(results[i].hash, relay_category::legacy))
{
LOG_PRINT_L2("tx " << results[i].hash << "already have transaction in tx_pool");
already_have[i] = true;
@@ -967,7 +992,7 @@ namespace cryptonote
tpool.submit(&waiter, [&, i, it] {
try
{
- results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash, keeped_by_block, relayed, do_not_relay);
+ results[i].res = handle_incoming_tx_post(*it, tvc[i], results[i].tx, results[i].hash);
}
catch (const std::exception &e)
{
@@ -988,7 +1013,7 @@ namespace cryptonote
tx_info.push_back({&results[i].tx, results[i].hash, tvc[i], results[i].res});
}
if (!tx_info.empty())
- handle_incoming_tx_accumulated_batch(tx_info, keeped_by_block);
+ handle_incoming_tx_accumulated_batch(tx_info, tx_relay == relay_method::block);
bool ok = true;
it = tx_blobs.begin();
@@ -998,13 +1023,14 @@ namespace cryptonote
ok = false;
continue;
}
- if (keeped_by_block)
+ if (tx_relay == relay_method::block)
get_blockchain_storage().on_new_tx_from_block(results[i].tx);
if (already_have[i])
continue;
- const size_t weight = get_transaction_weight(results[i].tx, it->size());
- ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i], weight, tvc[i], keeped_by_block, relayed, do_not_relay);
+ const uint64_t weight = results[i].tx.pruned ? get_pruned_transaction_weight(results[i].tx) : get_transaction_weight(results[i].tx, it->blob.size());
+ ok &= add_new_tx(results[i].tx, results[i].hash, tx_blobs[i].blob, weight, tvc[i], tx_relay, relayed);
+
if(tvc[i].m_verifivation_failed)
{MERROR_VER("Transaction verification failed: " << results[i].hash);}
else if(tvc[i].m_verifivation_impossible)
@@ -1018,14 +1044,9 @@ namespace cryptonote
CATCH_ENTRY_L0("core::handle_incoming_txs()", false);
}
//-----------------------------------------------------------------------------------------------
- bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, relay_method tx_relay, bool relayed)
{
- std::vector<cryptonote::blobdata> tx_blobs;
- tx_blobs.push_back(tx_blob);
- std::vector<tx_verification_context> tvcv(1);
- bool r = handle_incoming_txs(tx_blobs, tvcv, keeped_by_block, relayed, do_not_relay);
- tvc = tvcv[0];
- return r;
+ return handle_incoming_txs({std::addressof(tx_blob), 1}, {std::addressof(tvc), 1}, tx_relay, relayed);
}
//-----------------------------------------------------------------------------------------------
bool core::get_stat_info(core_stat_info& st_inf) const
@@ -1215,13 +1236,13 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::add_new_tx(transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::add_new_tx(transaction& tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed)
{
crypto::hash tx_hash = get_transaction_hash(tx);
blobdata bl;
t_serializable_object_to_blob(tx, bl);
size_t tx_weight = get_transaction_weight(tx, bl.size());
- return add_new_tx(tx, tx_hash, bl, tx_weight, tvc, keeped_by_block, relayed, do_not_relay);
+ return add_new_tx(tx, tx_hash, bl, tx_weight, tvc, tx_relay, relayed);
}
//-----------------------------------------------------------------------------------------------
size_t core::get_blockchain_total_transactions() const
@@ -1229,9 +1250,9 @@ namespace cryptonote
return m_blockchain_storage.get_total_transactions();
}
//-----------------------------------------------------------------------------------------------
- bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
+ bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed)
{
- if(m_mempool.have_tx(tx_hash))
+ if(m_mempool.have_tx(tx_hash, relay_category::legacy))
{
LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool");
return true;
@@ -1244,40 +1265,62 @@ namespace cryptonote
}
uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
- return m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version);
+ return m_mempool.add_tx(tx, tx_hash, blob, tx_weight, tvc, tx_relay, relayed, version);
}
//-----------------------------------------------------------------------------------------------
bool core::relay_txpool_transactions()
{
// we attempt to relay txes that should be relayed, but were not
- std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs;
+ std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> 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);
- NOTIFY_NEW_TRANSACTIONS::request r;
- for (auto it = txs.begin(); it != txs.end(); ++it)
+ NOTIFY_NEW_TRANSACTIONS::request public_req{};
+ NOTIFY_NEW_TRANSACTIONS::request private_req{};
+ for (auto& tx : txs)
{
- r.txs.push_back(it->second);
+ switch (std::get<2>(tx))
+ {
+ default:
+ case relay_method::none:
+ break;
+ case relay_method::local:
+ private_req.txs.push_back(std::move(std::get<1>(tx)));
+ break;
+ case relay_method::block:
+ case relay_method::fluff:
+ public_req.txs.push_back(std::move(std::get<1>(tx)));
+ break;
+ }
}
- get_protocol()->relay_transactions(r, fake_context);
- m_mempool.set_relayed(txs);
+
+ /* All txes are sent on randomized timers per connection in
+ `src/cryptonote_protocol/levin_notify.cpp.` They are either sent with
+ "white noise" delays or via diffusion (Dandelion++ fluff). So
+ re-relaying public and private _should_ be acceptable here. */
+ const boost::uuids::uuid source = boost::uuids::nil_uuid();
+ if (!public_req.txs.empty())
+ get_protocol()->relay_transactions(public_req, source, epee::net_utils::zone::public_);
+ if (!private_req.txs.empty())
+ get_protocol()->relay_transactions(private_req, source, epee::net_utils::zone::invalid);
}
return true;
}
//-----------------------------------------------------------------------------------------------
- void core::on_transaction_relayed(const cryptonote::blobdata& tx_blob)
+ void core::on_transactions_relayed(const epee::span<const cryptonote::blobdata> tx_blobs, const relay_method tx_relay)
{
- std::vector<std::pair<crypto::hash, cryptonote::blobdata>> txs;
- cryptonote::transaction tx;
- crypto::hash tx_hash;
- if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash))
+ std::vector<crypto::hash> tx_hashes{};
+ tx_hashes.resize(tx_blobs.size());
+
+ cryptonote::transaction tx{};
+ for (std::size_t i = 0; i < tx_blobs.size(); ++i)
{
- LOG_ERROR("Failed to parse relayed transaction");
- return;
+ if (!parse_and_validate_tx_from_blob(tx_blobs[i], tx, tx_hashes[i]))
+ {
+ LOG_ERROR("Failed to parse relayed transaction");
+ return;
+ }
}
- txs.push_back(std::make_pair(tx_hash, std::move(tx_blob)));
- m_mempool.set_relayed(txs);
+ m_mempool.set_relayed(epee::to_span(tx_hashes), tx_relay);
}
//-----------------------------------------------------------------------------------------------
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)
@@ -1290,9 +1333,9 @@ namespace cryptonote
return m_blockchain_storage.create_block_template(b, prev_block, 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
+ bool core::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const
{
- return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp);
+ return m_blockchain_storage.find_blockchain_supplement(qblock_ids, clip_pruned, resp);
}
//-----------------------------------------------------------------------------------------------
bool core::find_blockchain_supplement(const uint64_t req_start_block, const std::list<crypto::hash>& qblock_ids, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > >& blocks, uint64_t& total_height, uint64_t& start_height, bool pruned, bool get_miner_tx_hash, size_t max_count) const
@@ -1334,18 +1377,19 @@ namespace cryptonote
{
block_complete_entry bce;
bce.block = cryptonote::block_to_blob(b);
+ bce.block_weight = 0; // we can leave it to 0, those txes aren't pruned
for (const auto &tx_hash: b.tx_hashes)
{
cryptonote::blobdata txblob;
- CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, txblob), "Transaction not found in pool");
- bce.txs.push_back(txblob);
+ CHECK_AND_ASSERT_THROW_MES(pool.get_transaction(tx_hash, txblob, relay_category::all), "Transaction not found in pool");
+ bce.txs.push_back({txblob, crypto::null_hash});
}
return bce;
}
//-----------------------------------------------------------------------------------------------
bool core::handle_block_found(block& b, block_verification_context &bvc)
{
- bvc = boost::value_initialized<block_verification_context>();
+ bvc = {};
m_miner.pause();
std::vector<block_complete_entry> blocks;
try
@@ -1374,7 +1418,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "mined block failed verification");
if(bvc.m_added_to_main_chain)
{
- cryptonote_connection_context exclude_context = boost::value_initialized<cryptonote_connection_context>();
+ cryptonote_connection_context exclude_context = {};
NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg);
arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height();
std::vector<crypto::hash> missed_txs;
@@ -1391,7 +1435,7 @@ namespace cryptonote
block_to_blob(b, arg.b.block);
//pack transactions
for(auto& tx: txs)
- arg.b.txs.push_back(tx);
+ arg.b.txs.push_back({tx, crypto::null_hash});
m_pprotocol->relay_block(arg, exclude_context);
}
@@ -1442,7 +1486,7 @@ namespace cryptonote
{
TRY_ENTRY();
- bvc = boost::value_initialized<block_verification_context>();
+ bvc = {};
if (!check_incoming_block_size(block_blob))
{
@@ -1541,14 +1585,14 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
- bool core::get_pool_transaction(const crypto::hash &id, cryptonote::blobdata& tx) const
+ bool core::get_pool_transaction(const crypto::hash &id, cryptonote::blobdata& tx, relay_category tx_category) const
{
- return m_mempool.get_transaction(id, tx);
+ return m_mempool.get_transaction(id, tx, tx_category);
}
//-----------------------------------------------------------------------------------------------
bool core::pool_has_tx(const crypto::hash &id) const
{
- return m_mempool.have_tx(id);
+ return m_mempool.have_tx(id, relay_category::legacy);
}
//-----------------------------------------------------------------------------------------------
bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data) const
@@ -1630,18 +1674,18 @@ namespace cryptonote
return true;
HardFork::State state = m_blockchain_storage.get_hard_fork_state();
- const el::Level level = el::Level::Warning;
+ el::Level level;
switch (state) {
case HardFork::LikelyForked:
+ level = el::Level::Warning;
MCLOG_RED(level, "global", "**********************************************************************");
MCLOG_RED(level, "global", "Last scheduled hard fork is too far in the past.");
MCLOG_RED(level, "global", "We are most likely forked from the network. Daemon update needed now.");
MCLOG_RED(level, "global", "**********************************************************************");
break;
case HardFork::UpdateNeeded:
- MCLOG_RED(level, "global", "**********************************************************************");
- MCLOG_RED(level, "global", "Last scheduled hard fork time shows a daemon update is needed soon.");
- MCLOG_RED(level, "global", "**********************************************************************");
+ level = el::Level::Info;
+ MCLOG(level, "global", el::Color::Default, "Last scheduled hard fork time suggests a daemon update will be released within the next couple months.");
break;
default:
break;
@@ -1881,6 +1925,14 @@ namespace cryptonote
return true;
}
//-----------------------------------------------------------------------------------------------
+ void core::flush_bad_txs_cache()
+ {
+ bad_semantics_txes_lock.lock();
+ for (int idx = 0; idx < 2; ++idx)
+ bad_semantics_txes[idx].clear();
+ bad_semantics_txes_lock.unlock();
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::update_blockchain_pruning()
{
return m_blockchain_storage.update_blockchain_pruning();
@@ -1901,9 +1953,9 @@ namespace cryptonote
return m_target_blockchain_height;
}
//-----------------------------------------------------------------------------------------------
- uint64_t core::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes)
+ uint64_t core::prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights)
{
- return get_blockchain_storage().prevalidate_block_hashes(height, hashes);
+ return get_blockchain_storage().prevalidate_block_hashes(height, hashes, weights);
}
//-----------------------------------------------------------------------------------------------
uint64_t core::get_free_space() const
@@ -1923,6 +1975,16 @@ namespace cryptonote
return get_blockchain_storage().prune_blockchain(pruning_seed);
}
//-----------------------------------------------------------------------------------------------
+ bool core::is_within_compiled_block_hash_area(uint64_t height) const
+ {
+ return get_blockchain_storage().is_within_compiled_block_hash_area(height);
+ }
+ //-----------------------------------------------------------------------------------------------
+ bool core::has_block_weights(uint64_t height, uint64_t nblocks) const
+ {
+ return get_blockchain_storage().has_block_weights(height, nblocks);
+ }
+ //-----------------------------------------------------------------------------------------------
std::time_t core::get_start_time() const
{
return start_time;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index badbaf936..55efb566c 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -35,7 +35,9 @@
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
+#include "cryptonote_core/i_core_events.h"
#include "cryptonote_protocol/cryptonote_protocol_handler_common.h"
+#include "cryptonote_protocol/enums.h"
#include "storages/portable_storage_template_helper.h"
#include "common/download.h"
#include "common/command_line.h"
@@ -46,6 +48,7 @@
#include "cryptonote_basic/cryptonote_stat_info.h"
#include "warnings.h"
#include "crypto/hash.h"
+#include "span.h"
PUSH_WARNINGS
DISABLE_VS_WARNINGS(4355)
@@ -64,6 +67,7 @@ namespace cryptonote
extern const command_line::arg_descriptor<difficulty_type> arg_fixed_difficulty;
extern const command_line::arg_descriptor<bool> arg_offline;
extern const command_line::arg_descriptor<size_t> arg_block_download_max_size;
+ extern const command_line::arg_descriptor<bool> arg_sync_pruned_blocks;
/************************************************************************/
/* */
@@ -76,7 +80,7 @@ namespace cryptonote
* limited to, communication among the Blockchain, the transaction pool,
* any miners, and the network.
*/
- class core: public i_miner_handler
+ class core final: public i_miner_handler, public i_core_events
{
public:
@@ -114,13 +118,12 @@ namespace cryptonote
*
* @param tx_blob the tx to handle
* @param tvc metadata about the transaction's validity
- * @param keeped_by_block if the transaction has been in a block
+ * @param tx_relay how the transaction was received
* @param relayed whether or not the transaction was relayed to us
- * @param do_not_relay whether to prevent the transaction from being relayed
*
* @return true if the transaction was accepted, false otherwise
*/
- bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool handle_incoming_tx(const tx_blob_entry& tx_blob, tx_verification_context& tvc, relay_method tx_relay, bool relayed);
/**
* @brief handles a list of incoming transactions
@@ -128,15 +131,35 @@ namespace cryptonote
* Parses incoming transactions and, if nothing is obviously wrong,
* passes them along to the transaction pool
*
+ * @pre `tx_blobs.size() == tvc.size()`
+ *
* @param tx_blobs the txs to handle
* @param tvc metadata about the transactions' validity
- * @param keeped_by_block if the transactions have been in a block
+ * @param tx_relay how the transaction was received.
* @param relayed whether or not the transactions were relayed to us
- * @param do_not_relay whether to prevent the transactions from being relayed
*
* @return true if the transactions were accepted, false otherwise
*/
- bool handle_incoming_txs(const std::vector<blobdata>& tx_blobs, std::vector<tx_verification_context>& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool handle_incoming_txs(epee::span<const tx_blob_entry> tx_blobs, epee::span<tx_verification_context> tvc, relay_method tx_relay, bool relayed);
+
+ /**
+ * @brief handles a list of incoming transactions
+ *
+ * Parses incoming transactions and, if nothing is obviously wrong,
+ * passes them along to the transaction pool
+ *
+ * @param tx_blobs the txs to handle
+ * @param tvc metadata about the transactions' validity
+ * @param tx_relay how the transaction was received.
+ * @param relayed whether or not the transactions were relayed to us
+ *
+ * @return true if the transactions were accepted, false otherwise
+ */
+ bool handle_incoming_txs(const std::vector<tx_blob_entry>& tx_blobs, std::vector<tx_verification_context>& tvc, relay_method tx_relay, bool relayed)
+ {
+ tvc.resize(tx_blobs.size());
+ return handle_incoming_txs(epee::to_span(tx_blobs), epee::to_mut_span(tvc), tx_relay, relayed);
+ }
/**
* @brief handles an incoming block
@@ -210,9 +233,10 @@ namespace cryptonote
virtual bool get_block_template(block& b, const crypto::hash *prev_block, 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
+ * @brief called when a transaction is relayed.
+ * @note Should only be invoked from `levin_notify`.
*/
- virtual void on_transaction_relayed(const cryptonote::blobdata& tx);
+ virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, relay_method tx_relay) final;
/**
@@ -438,11 +462,11 @@ namespace cryptonote
/**
* @copydoc tx_memory_pool::get_transactions
- * @param include_unrelayed_txes include unrelayed txes in result
+ * @param include_sensitive_txes include private transactions
*
* @note see tx_memory_pool::get_transactions
*/
- bool get_pool_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes = true) const;
+ bool get_pool_transactions(std::vector<transaction>& txs, bool include_sensitive_txes = false) const;
/**
* @copydoc tx_memory_pool::get_txpool_backlog
@@ -453,34 +477,34 @@ namespace cryptonote
/**
* @copydoc tx_memory_pool::get_transactions
- * @param include_unrelayed_txes include unrelayed txes in result
+ * @param include_sensitive_txes include private transactions
*
* @note see tx_memory_pool::get_transactions
*/
- bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const;
+ bool get_pool_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive_txes = false) const;
/**
* @copydoc tx_memory_pool::get_transactions
- * @param include_unrelayed_txes include unrelayed txes in result
+ * @param include_sensitive_txes include private transactions
*
* @note see tx_memory_pool::get_transactions
*/
- bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const;
+ bool get_pool_transaction_stats(struct txpool_stats& stats, bool include_sensitive_txes = false) const;
/**
* @copydoc tx_memory_pool::get_transaction
*
* @note see tx_memory_pool::get_transaction
*/
- bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx) const;
+ bool get_pool_transaction(const crypto::hash& id, cryptonote::blobdata& tx, relay_category tx_category) const;
/**
* @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info
- * @param include_unrelayed_txes include unrelayed txes in result
+ * @param include_sensitive_txes include private transactions
*
* @note see tx_memory_pool::get_pool_transactions_and_spent_keys_info
*/
- bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_unrelayed_txes = true) const;
+ bool get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_txes = false) const;
/**
* @copydoc tx_memory_pool::get_pool_for_rpc
@@ -522,7 +546,7 @@ namespace cryptonote
*
* @note see Blockchain::find_blockchain_supplement(const std::list<crypto::hash>&, NOTIFY_RESPONSE_CHAIN_ENTRY::request&) const
*/
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, bool clip_pruned, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) const;
/**
* @copydoc Blockchain::find_blockchain_supplement(const uint64_t, const std::list<crypto::hash>&, std::vector<std::pair<cryptonote::blobdata, std::vector<cryptonote::blobdata> > >&, uint64_t&, uint64_t&, size_t) const
@@ -768,18 +792,11 @@ namespace cryptonote
bool fluffy_blocks_enabled() const { return m_fluffy_blocks_enabled; }
/**
- * @brief get whether transaction relay should be padded
- *
- * @return whether transaction relay should be padded
- */
- bool pad_transactions() const { return m_pad_transactions; }
-
- /**
* @brief check a set of hashes against the precompiled hash set
*
* @return number of usable blocks
*/
- uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes);
+ uint64_t prevalidate_block_hashes(uint64_t height, const std::vector<crypto::hash> &hashes, const std::vector<uint64_t> &weights);
/**
* @brief get free disk space on the blockchain partition
@@ -825,6 +842,23 @@ namespace cryptonote
*/
bool check_blockchain_pruning();
+ /**
+ * @brief checks whether a given block height is included in the precompiled block hash area
+ *
+ * @param height the height to check for
+ */
+ bool is_within_compiled_block_hash_area(uint64_t height) const;
+
+ /**
+ * @brief checks whether block weights are known for the given range
+ */
+ bool has_block_weights(uint64_t height, uint64_t nblocks) const;
+
+ /**
+ * @brief flushes the bad txs cache
+ */
+ void flush_bad_txs_cache();
+
private:
/**
@@ -833,11 +867,11 @@ namespace cryptonote
* @param tx_hash the transaction's hash
* @param blob the transaction as a blob
* @param tx_weight the weight of the transaction
+ * @param tx_relay how the transaction was received
* @param relayed whether or not the transaction was relayed to us
- * @param do_not_relay whether to prevent the transaction from being relayed
*
*/
- bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed);
/**
* @brief add a new transaction to the transaction pool
@@ -846,15 +880,14 @@ namespace cryptonote
*
* @param tx the transaction to add
* @param tvc return-by-reference metadata about the transaction's verification process
- * @param keeped_by_block whether or not the transaction has been in a block
+ * @param tx_relay how the transaction was received
* @param relayed whether or not the transaction was relayed to us
- * @param do_not_relay whether to prevent the transaction from being relayed
*
* @return true if the transaction is already in the transaction pool,
* is already in a block on the Blockchain, or is successfully added
* to the transaction pool
*/
- bool add_new_tx(transaction& tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool add_new_tx(transaction& tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed);
/**
* @copydoc Blockchain::add_new_block
@@ -910,8 +943,8 @@ namespace cryptonote
bool check_tx_semantic(const transaction& tx, bool keeped_by_block) const;
void set_semantics_failed(const crypto::hash &tx_hash);
- bool handle_incoming_tx_pre(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay);
- bool handle_incoming_tx_post(const blobdata& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash, bool keeped_by_block, bool relayed, bool do_not_relay);
+ bool handle_incoming_tx_pre(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash);
+ bool handle_incoming_tx_post(const tx_blob_entry& tx_blob, tx_verification_context& tvc, cryptonote::transaction &tx, crypto::hash &tx_hash);
struct tx_verification_batch_info { const cryptonote::transaction *tx; crypto::hash tx_hash; tx_verification_context &tvc; bool &result; };
bool handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block);
@@ -1062,7 +1095,6 @@ namespace cryptonote
bool m_fluffy_blocks_enabled;
bool m_offline;
- bool m_pad_transactions;
std::shared_ptr<tools::Notify> m_block_rate_notify;
};
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 4cf71e558..f50fc61a5 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -37,6 +37,7 @@ using namespace epee;
#include "common/apply_permutation.h"
#include "cryptonote_tx_utils.h"
#include "cryptonote_config.h"
+#include "blockchain.h"
#include "cryptonote_basic/miner.h"
#include "cryptonote_basic/tx_extra.h"
#include "crypto/crypto.h"
@@ -611,23 +612,27 @@ namespace cryptonote
{
hw::device &hwdev = sender_account_keys.get_device();
hwdev.open_tx(tx_key);
+ try {
+ // figure out if we need to make additional tx pubkeys
+ size_t num_stdaddresses = 0;
+ size_t num_subaddresses = 0;
+ account_public_address single_dest_subaddress;
+ classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
+ bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
+ if (need_additional_txkeys)
+ {
+ additional_tx_keys.clear();
+ for (const auto &d: destinations)
+ additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec);
+ }
- // figure out if we need to make additional tx pubkeys
- size_t num_stdaddresses = 0;
- size_t num_subaddresses = 0;
- account_public_address single_dest_subaddress;
- classify_addresses(destinations, change_addr, num_stdaddresses, num_subaddresses, single_dest_subaddress);
- bool need_additional_txkeys = num_subaddresses > 0 && (num_stdaddresses > 0 || num_subaddresses > 1);
- if (need_additional_txkeys)
- {
- additional_tx_keys.clear();
- for (const auto &d: destinations)
- additional_tx_keys.push_back(keypair::generate(sender_account_keys.get_device()).sec);
+ bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout);
+ hwdev.close_tx();
+ return r;
+ } catch(...) {
+ hwdev.close_tx();
+ throw;
}
-
- bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout);
- hwdev.close_tx();
- return r;
}
//---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, const std::vector<uint8_t> &extra, transaction& tx, uint64_t unlock_time)
@@ -647,7 +652,7 @@ namespace cryptonote
)
{
//genesis block
- bl = boost::value_initialized<block>();
+ bl = {};
blobdata tx_bl;
bool r = string_tools::parse_hexstr_to_binbuff(genesis_tx, tx_bl);
@@ -658,9 +663,61 @@ namespace cryptonote
bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
bl.timestamp = 0;
bl.nonce = nonce;
- miner::find_nonce_for_given_block(bl, 1, 0);
+ miner::find_nonce_for_given_block([](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash){
+ return cryptonote::get_block_longhash(NULL, b, hash, height, threads);
+ }, bl, 1, 0);
bl.invalidate_hashes();
return true;
}
//---------------------------------------------------------------
+ void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height, const uint64_t seed_height, const crypto::hash& seed_hash)
+ {
+ blobdata bd = get_block_hashing_blob(b);
+ rx_slow_hash(main_height, seed_height, seed_hash.data, bd.data(), bd.size(), res.data, 0, 1);
+ }
+
+ bool get_block_longhash(const Blockchain *pbc, const block& b, crypto::hash& res, const uint64_t height, const int miners)
+ {
+ // block 202612 bug workaround
+ if (height == 202612)
+ {
+ static const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000";
+ epee::string_tools::hex_to_pod(longhash_202612, res);
+ return true;
+ }
+ blobdata bd = get_block_hashing_blob(b);
+ if (b.major_version >= RX_BLOCK_VERSION)
+ {
+ uint64_t seed_height, main_height;
+ crypto::hash hash;
+ if (pbc != NULL)
+ {
+ seed_height = rx_seedheight(height);
+ hash = pbc->get_pending_block_id_by_height(seed_height);
+ main_height = pbc->get_current_blockchain_height();
+ } else
+ {
+ memset(&hash, 0, sizeof(hash)); // only happens when generating genesis block
+ seed_height = 0;
+ main_height = 0;
+ }
+ rx_slow_hash(main_height, seed_height, hash.data, bd.data(), bd.size(), res.data, miners, 0);
+ } else {
+ const int pow_variant = b.major_version >= 7 ? b.major_version - 6 : 0;
+ crypto::cn_slow_hash(bd.data(), bd.size(), res, pow_variant, height);
+ }
+ return true;
+ }
+
+ crypto::hash get_block_longhash(const Blockchain *pbc, const block& b, const uint64_t height, const int miners)
+ {
+ crypto::hash p = crypto::null_hash;
+ get_block_longhash(pbc, b, p, height, miners);
+ return p;
+ }
+
+ void get_block_longhash_reorg(const uint64_t split_height)
+ {
+ rx_reorg(split_height);
+ }
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index b03eb6e70..309e4177f 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -132,6 +132,13 @@ namespace cryptonote
, uint32_t nonce
);
+ class Blockchain;
+ bool get_block_longhash(const Blockchain *pb, const block& b, crypto::hash& res, const uint64_t height, const int miners);
+ void get_altblock_longhash(const block& b, crypto::hash& res, const uint64_t main_height, const uint64_t height,
+ const uint64_t seed_height, const crypto::hash& seed_hash);
+ crypto::hash get_block_longhash(const Blockchain *pb, const block& b, const uint64_t height, const int miners);
+ void get_block_longhash_reorg(const uint64_t split_height);
+
}
BOOST_CLASS_VERSION(cryptonote::tx_source_entry, 1)
diff --git a/src/cryptonote_core/i_core_events.h b/src/cryptonote_core/i_core_events.h
new file mode 100644
index 000000000..f49fbdf07
--- /dev/null
+++ b/src/cryptonote_core/i_core_events.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2019, 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 "cryptonote_basic/blobdatatype.h"
+#include "cryptonote_protocol/enums.h"
+#include "span.h"
+
+namespace cryptonote
+{
+ struct i_core_events
+ {
+ virtual ~i_core_events() noexcept
+ {}
+
+ virtual void on_transactions_relayed(epee::span<const cryptonote::blobdata> tx_blobs, relay_method tx_relay) = 0;
+ };
+}
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 49d5a8ccc..1bc475879 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -115,8 +115,10 @@ namespace cryptonote
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version)
+ bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version)
{
+ const bool kept_by_block = (tx_relay == relay_method::block);
+
// this should already be called with that lock, but let's make it explicit for clarity
CRITICAL_REGION_LOCAL(m_transactions_lock);
@@ -227,7 +229,7 @@ namespace cryptonote
crypto::hash max_used_block_id = null_hash;
uint64_t max_used_block_height = 0;
- cryptonote::txpool_tx_meta_t meta;
+ cryptonote::txpool_tx_meta_t meta{};
bool ch_inp_res = check_tx_inputs([&tx]()->cryptonote::transaction&{ return tx; }, id, max_used_block_height, max_used_block_id, tvc, kept_by_block);
if(!ch_inp_res)
{
@@ -241,12 +243,12 @@ namespace cryptonote
meta.max_used_block_height = 0;
meta.last_failed_height = 0;
meta.last_failed_id = null_hash;
- meta.kept_by_block = kept_by_block;
meta.receive_time = receive_time;
meta.last_relayed_time = time(NULL);
meta.relayed = relayed;
- meta.do_not_relay = do_not_relay;
+ meta.set_relay_method(tx_relay);
meta.double_spend_seen = have_tx_keyimges_as_spent(tx);
+ meta.pruned = tx.pruned;
meta.bf_padding = 0;
memset(meta.padding, 0, sizeof(meta.padding));
try
@@ -255,15 +257,16 @@ namespace cryptonote
m_parsed_tx_cache.insert(std::make_pair(id, tx));
CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain);
- m_blockchain.add_txpool_tx(id, blob, meta);
- if (!insert_key_images(tx, id, kept_by_block))
+ if (!insert_key_images(tx, id, tx_relay))
return false;
- m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
+
+ m_blockchain.add_txpool_tx(id, blob, meta);
+ m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id);
lock.commit();
}
catch (const std::exception &e)
{
- MERROR("transaction already exists at inserting in memory pool: " << e.what());
+ MERROR("Error adding transaction to txpool: " << e.what());
return false;
}
tvc.m_verifivation_impossible = true;
@@ -279,7 +282,6 @@ namespace cryptonote
{
//update transactions container
meta.weight = tx_weight;
- meta.kept_by_block = kept_by_block;
meta.fee = fee;
meta.max_used_block_id = max_used_block_id;
meta.max_used_block_height = max_used_block_height;
@@ -288,8 +290,9 @@ namespace cryptonote
meta.receive_time = receive_time;
meta.last_relayed_time = time(NULL);
meta.relayed = relayed;
- meta.do_not_relay = do_not_relay;
+ meta.set_relay_method(tx_relay);
meta.double_spend_seen = false;
+ meta.pruned = tx.pruned;
meta.bf_padding = 0;
memset(meta.padding, 0, sizeof(meta.padding));
@@ -300,20 +303,21 @@ namespace cryptonote
CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain);
m_blockchain.remove_txpool_tx(id);
- m_blockchain.add_txpool_tx(id, blob, meta);
- if (!insert_key_images(tx, id, kept_by_block))
+ if (!insert_key_images(tx, id, tx_relay))
return false;
- m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
+
+ m_blockchain.add_txpool_tx(id, blob, meta);
+ m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)(tx_weight ? tx_weight : 1), receive_time), id);
lock.commit();
}
catch (const std::exception &e)
{
- MERROR("internal error: transaction already exists at inserting in memory pool: " << e.what());
+ MERROR("internal error: error adding transaction to txpool: " << e.what());
return false;
}
tvc.m_added_to_pool = true;
- if(meta.fee > 0 && !do_not_relay)
+ if(meta.fee > 0 && tx_relay != relay_method::none)
tvc.m_should_be_relayed = true;
}
@@ -322,14 +326,14 @@ namespace cryptonote
++m_cookie;
- MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)tx_weight));
+ MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)(tx_weight ? tx_weight : 1)));
prune(m_txpool_max_weight);
return true;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay, uint8_t version)
+ bool tx_memory_pool::add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version)
{
crypto::hash h = null_hash;
size_t blob_size = 0;
@@ -337,7 +341,7 @@ namespace cryptonote
t_serializable_object_to_blob(tx, bl);
if (bl.size() == 0 || !get_transaction_hash(tx, h))
return false;
- return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, keeped_by_block, relayed, do_not_relay, version);
+ return add_tx(tx, h, bl, get_transaction_weight(tx, bl.size()), tvc, tx_relay, relayed, version);
}
//---------------------------------------------------------------------------------
size_t tx_memory_pool::get_txpool_weight() const
@@ -373,7 +377,7 @@ namespace cryptonote
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(txid, meta))
{
- MERROR("Failed to find tx in txpool");
+ MERROR("Failed to find tx_meta in txpool");
return;
}
// don't prune the kept_by_block ones, they're likely added because we're adding a block with those
@@ -382,7 +386,7 @@ namespace cryptonote
--it;
continue;
}
- cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid);
+ cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all);
cryptonote::transaction_prefix tx;
if (!parse_and_validate_tx_prefix_from_blob(txblob, tx))
{
@@ -411,17 +415,38 @@ namespace cryptonote
MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes);
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, bool kept_by_block)
+ bool tx_memory_pool::insert_key_images(const transaction_prefix &tx, const crypto::hash &id, relay_method tx_relay)
{
for(const auto& in: tx.vin)
{
CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false);
std::unordered_set<crypto::hash>& kei_image_set = m_spent_key_images[txin.k_image];
- CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: kept_by_block=" << kept_by_block
- << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL
- << "tx_id=" << id );
- auto ins_res = kei_image_set.insert(id);
- CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set");
+
+ /* If any existing key-image in the set is publicly visible AND this is
+ not forcibly "kept_by_block", then fail (duplicate key image). If all
+ existing key images are supposed to be hidden, we silently allow so
+ that the node doesn't leak knowledge of a local/stem tx. */
+ bool visible = false;
+ if (tx_relay != relay_method::block)
+ {
+ for (const crypto::hash& other_id : kei_image_set)
+ visible |= m_blockchain.txpool_tx_matches_category(other_id, relay_category::legacy);
+ }
+
+ CHECK_AND_ASSERT_MES(!visible, false, "internal error: tx_relay=" << unsigned(tx_relay)
+ << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL
+ << "tx_id=" << id);
+
+ /* If adding a tx (hash) that already exists, fail only if the tx has
+ been publicly "broadcast" previously. This way, when a private tx is
+ received for the first time from a remote node, "this" node will
+ respond as-if it were seen for the first time. LMDB does the
+ "hard-check" on key-images, so the effect is overwriting the existing
+ tx_pool metadata and "first seen" time. */
+ const bool new_or_previously_private =
+ kei_image_set.insert(id).second ||
+ !m_blockchain.txpool_tx_matches_category(id, relay_category::legacy);
+ CHECK_AND_ASSERT_MES(new_or_previously_private, false, "internal error: try to insert duplicate iterator in key_image set");
}
++m_cookie;
return true;
@@ -460,7 +485,7 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen)
+ bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen, bool &pruned)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -473,16 +498,16 @@ namespace cryptonote
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(id, meta))
{
- MERROR("Failed to find tx in txpool");
+ MERROR("Failed to find tx_meta in txpool");
return false;
}
- txblob = m_blockchain.get_txpool_tx_blob(id);
+ txblob = m_blockchain.get_txpool_tx_blob(id, relay_category::all);
auto ci = m_parsed_tx_cache.find(id);
if (ci != m_parsed_tx_cache.end())
{
tx = ci->second;
}
- else if (!parse_and_validate_tx_from_blob(txblob, tx))
+ else if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(txblob, tx) : parse_and_validate_tx_from_blob(txblob, tx)))
{
MERROR("Failed to parse tx from txpool");
return false;
@@ -496,6 +521,7 @@ namespace cryptonote
relayed = meta.relayed;
do_not_relay = meta.do_not_relay;
double_spend_seen = meta.double_spend_seen;
+ pruned = meta.pruned;
// remove first, in case this throws, so key images aren't removed
m_blockchain.remove_txpool_tx(id);
@@ -515,6 +541,59 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ bool tx_memory_pool::get_transaction_info(const crypto::hash &txid, tx_details &td) const
+ {
+ PERF_TIMER(get_transaction_info);
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+
+ try
+ {
+ LockedTXN lock(m_blockchain);
+ txpool_tx_meta_t meta;
+ if (!m_blockchain.get_txpool_tx_meta(txid, meta))
+ {
+ MERROR("Failed to find tx in txpool");
+ return false;
+ }
+ cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all);
+ auto ci = m_parsed_tx_cache.find(txid);
+ if (ci != m_parsed_tx_cache.end())
+ {
+ td.tx = ci->second;
+ }
+ else if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(txblob, td.tx) : parse_and_validate_tx_from_blob(txblob, td.tx)))
+ {
+ MERROR("Failed to parse tx from txpool");
+ return false;
+ }
+ else
+ {
+ td.tx.set_hash(txid);
+ }
+ td.blob_size = txblob.size();
+ td.weight = meta.weight;
+ td.fee = meta.fee;
+ td.max_used_block_id = meta.max_used_block_id;
+ td.max_used_block_height = meta.max_used_block_height;
+ td.kept_by_block = meta.kept_by_block;
+ td.last_failed_height = meta.last_failed_height;
+ td.last_failed_id = meta.last_failed_id;
+ td.receive_time = meta.receive_time;
+ td.last_relayed_time = meta.last_relayed_time;
+ td.relayed = meta.relayed;
+ td.do_not_relay = meta.do_not_relay;
+ td.double_spend_seen = meta.double_spend_seen;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to get tx from txpool: " << e.what());
+ return false;
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------------
void tx_memory_pool::on_idle()
{
m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();});
@@ -555,7 +634,7 @@ namespace cryptonote
remove.push_back(std::make_pair(txid, meta.weight));
}
return true;
- }, false);
+ }, false, relay_category::all);
if (!remove.empty())
{
@@ -565,7 +644,7 @@ namespace cryptonote
const crypto::hash &txid = entry.first;
try
{
- cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid);
+ cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid, relay_category::all);
cryptonote::transaction_prefix tx;
if (!parse_and_validate_tx_prefix_from_blob(bd, tx))
{
@@ -593,7 +672,7 @@ namespace cryptonote
}
//---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
- bool tx_memory_pool::get_relayable_transactions(std::vector<std::pair<crypto::hash, cryptonote::blobdata>> &txs) const
+ bool tx_memory_pool::get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>> &txs) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -601,7 +680,7 @@ namespace cryptonote
txs.reserve(m_blockchain.get_txpool_tx_count());
m_blockchain.for_all_txpool_txes([this, now, &txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *){
// 0 fee transactions are never relayed
- if(meta.fee > 0 && !meta.do_not_relay && now - meta.last_relayed_time > get_relay_delay(now, meta.receive_time))
+ if(!meta.pruned && meta.fee > 0 && !meta.do_not_relay && now - meta.last_relayed_time > get_relay_delay(now, meta.receive_time))
{
// if the tx is older than half the max lifetime, we don't re-relay it, to avoid a problem
// mentioned by smooth where nodes would flush txes at slightly different times, causing
@@ -611,8 +690,7 @@ namespace cryptonote
{
try
{
- cryptonote::blobdata bd = m_blockchain.get_txpool_tx_blob(txid);
- txs.push_back(std::make_pair(txid, bd));
+ txs.emplace_back(txid, m_blockchain.get_txpool_tx_blob(txid, relay_category::all), meta.get_relay_method());
}
catch (const std::exception &e)
{
@@ -622,26 +700,27 @@ namespace cryptonote
}
}
return true;
- }, false);
+ }, false, relay_category::relayable);
return true;
}
//---------------------------------------------------------------------------------
- void tx_memory_pool::set_relayed(const std::vector<std::pair<crypto::hash, cryptonote::blobdata>> &txs)
+ void tx_memory_pool::set_relayed(const epee::span<const crypto::hash> hashes, const relay_method method)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const time_t now = time(NULL);
LockedTXN lock(m_blockchain);
- for (auto it = txs.begin(); it != txs.end(); ++it)
+ for (const auto& hash : hashes)
{
try
{
txpool_tx_meta_t meta;
- if (m_blockchain.get_txpool_tx_meta(it->first, meta))
+ if (m_blockchain.get_txpool_tx_meta(hash, meta))
{
meta.relayed = true;
meta.last_relayed_time = now;
- m_blockchain.update_txpool_tx(it->first, meta);
+ meta.set_relay_method(method);
+ m_blockchain.update_txpool_tx(hash, meta);
}
}
catch (const std::exception &e)
@@ -653,21 +732,22 @@ namespace cryptonote
lock.commit();
}
//---------------------------------------------------------------------------------
- size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const
+ size_t tx_memory_pool::get_transactions_count(bool include_sensitive) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
- return m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
+ return m_blockchain.get_txpool_tx_count(include_sensitive);
}
//---------------------------------------------------------------------------------
- void tx_memory_pool::get_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes) const
+ void tx_memory_pool::get_transactions(std::vector<transaction>& txs, bool include_sensitive) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
- txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
+ const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted;
+ txs.reserve(m_blockchain.get_txpool_tx_count(include_sensitive));
m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
transaction tx;
- if (!parse_and_validate_tx_from_blob(*bd, tx))
+ if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx)))
{
MERROR("Failed to parse tx from txpool");
// continue
@@ -676,39 +756,42 @@ namespace cryptonote
tx.set_hash(txid);
txs.push_back(std::move(tx));
return true;
- }, true, include_unrelayed_txes);
+ }, true, category);
}
//------------------------------------------------------------------
- void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes) const
+ void tx_memory_pool::get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
- txs.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
+ const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted;
+ txs.reserve(m_blockchain.get_txpool_tx_count(include_sensitive));
m_blockchain.for_all_txpool_txes([&txs](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
txs.push_back(txid);
return true;
- }, false, include_unrelayed_txes);
+ }, false, category);
}
//------------------------------------------------------------------
- void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes) const
+ void tx_memory_pool::get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const uint64_t now = time(NULL);
- backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
+ const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted;
+ backlog.reserve(m_blockchain.get_txpool_tx_count(include_sensitive));
m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
backlog.push_back({meta.weight, meta.fee, meta.receive_time - now});
return true;
- }, false, include_unrelayed_txes);
+ }, false, category);
}
//------------------------------------------------------------------
- void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes) const
+ void tx_memory_pool::get_transaction_stats(struct txpool_stats& stats, bool include_sensitive) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const uint64_t now = time(NULL);
+ const relay_category category = include_sensitive ? relay_category::all : relay_category::broadcasted;
std::map<uint64_t, txpool_histo> agebytes;
- stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
+ stats.txs_total = m_blockchain.get_txpool_tx_count(include_sensitive);
std::vector<uint32_t> weights;
weights.reserve(stats.txs_total);
m_blockchain.for_all_txpool_txes([&stats, &weights, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
@@ -733,7 +816,8 @@ namespace cryptonote
if (meta.double_spend_seen)
++stats.num_double_spends;
return true;
- }, false, include_unrelayed_txes);
+ }, false, category);
+
stats.bytes_med = epee::misc_utils::median(weights);
if (stats.txs_total > 1)
{
@@ -746,8 +830,15 @@ namespace cryptonote
/* If enough txs, spread the first 98% of results across
* the first 9 bins, drop final 2% in last bin.
*/
- it=agebytes.end();
- for (size_t n=0; n <= end; n++, it--);
+ it = agebytes.end();
+ size_t cumulative_num = 0;
+ /* Since agebytes is not empty and end is nonzero, the
+ * below loop can always run at least once.
+ */
+ do {
+ --it;
+ cumulative_num += it->second.txs;
+ } while (it != agebytes.begin() && cumulative_num < end);
stats.histo_98pc = it->first;
factor = 9;
delta = it->first;
@@ -784,14 +875,16 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
- tx_infos.reserve(m_blockchain.get_txpool_tx_count());
- key_image_infos.reserve(m_blockchain.get_txpool_tx_count());
+ const relay_category category = include_sensitive_data ? relay_category::all : relay_category::broadcasted;
+ const size_t count = m_blockchain.get_txpool_tx_count(include_sensitive_data);
+ tx_infos.reserve(count);
+ key_image_infos.reserve(count);
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos, include_sensitive_data](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
tx_info txi;
txi.id_hash = epee::string_tools::pod_to_hex(txid);
txi.tx_blob = *bd;
transaction tx;
- if (!parse_and_validate_tx_from_blob(*bd, tx))
+ if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, tx) : parse_and_validate_tx_from_blob(*bd, tx)))
{
MERROR("Failed to parse tx from txpool");
// continue
@@ -816,7 +909,7 @@ namespace cryptonote
txi.double_spend_seen = meta.double_spend_seen;
tx_infos.push_back(std::move(txi));
return true;
- }, true, include_sensitive_data);
+ }, true, category);
txpool_tx_meta_t meta;
for (const key_images_container::value_type& kee : m_spent_key_images) {
@@ -826,30 +919,13 @@ namespace cryptonote
ki.id_hash = epee::string_tools::pod_to_hex(k_image);
for (const crypto::hash& tx_id_hash : kei_image_set)
{
- if (!include_sensitive_data)
- {
- try
- {
- if (!m_blockchain.get_txpool_tx_meta(tx_id_hash, meta))
- {
- MERROR("Failed to get tx meta from txpool");
- return false;
- }
- if (!meta.relayed)
- // Do not include that transaction if in restricted mode and it's not relayed
- continue;
- }
- catch (const std::exception &e)
- {
- MERROR("Failed to get tx meta from txpool: " << e.what());
- return false;
- }
- }
- ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash));
+ if (m_blockchain.txpool_tx_matches_category(tx_id_hash, category))
+ ki.txs_hashes.push_back(epee::string_tools::pod_to_hex(tx_id_hash));
}
+
// Only return key images for which we have at least one tx that we can show for them
if (!ki.txs_hashes.empty())
- key_image_infos.push_back(ki);
+ key_image_infos.push_back(std::move(ki));
}
return true;
}
@@ -863,7 +939,7 @@ namespace cryptonote
m_blockchain.for_all_txpool_txes([&tx_infos, key_image_infos](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
cryptonote::rpc::tx_in_pool txi;
txi.tx_hash = txid;
- if (!parse_and_validate_tx_from_blob(*bd, txi.tx))
+ if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*bd, txi.tx) : parse_and_validate_tx_from_blob(*bd, txi.tx)))
{
MERROR("Failed to parse tx from txpool");
// continue
@@ -885,23 +961,24 @@ namespace cryptonote
txi.double_spend_seen = meta.double_spend_seen;
tx_infos.push_back(txi);
return true;
- }, true, false);
+ }, true, relay_category::broadcasted);
for (const key_images_container::value_type& kee : m_spent_key_images) {
std::vector<crypto::hash> tx_hashes;
const std::unordered_set<crypto::hash>& kei_image_set = kee.second;
for (const crypto::hash& tx_id_hash : kei_image_set)
{
- tx_hashes.push_back(tx_id_hash);
+ if (m_blockchain.txpool_tx_matches_category(tx_id_hash, relay_category::broadcasted))
+ tx_hashes.push_back(tx_id_hash);
}
- const crypto::key_image& k_image = kee.first;
- key_image_infos[k_image] = std::move(tx_hashes);
+ if (!tx_hashes.empty())
+ key_image_infos[kee.first] = std::move(tx_hashes);
}
return true;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const
+ bool tx_memory_pool::check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool>& spent) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -910,19 +987,26 @@ namespace cryptonote
for (const auto& image : key_images)
{
- spent.push_back(m_spent_key_images.find(image) == m_spent_key_images.end() ? false : true);
+ bool is_spent = false;
+ const auto found = m_spent_key_images.find(image);
+ if (found != m_spent_key_images.end())
+ {
+ for (const crypto::hash& tx_hash : found->second)
+ is_spent |= m_blockchain.txpool_tx_matches_category(tx_hash, relay_category::broadcasted);
+ }
+ spent.push_back(is_spent);
}
return true;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob) const
+ bool tx_memory_pool::get_transaction(const crypto::hash& id, cryptonote::blobdata& txblob, relay_category tx_category) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
try
{
- return m_blockchain.get_txpool_tx_blob(id, txblob);
+ return m_blockchain.get_txpool_tx_blob(id, txblob, tx_category);
}
catch (const std::exception &e)
{
@@ -946,11 +1030,11 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
- bool tx_memory_pool::have_tx(const crypto::hash &id) const
+ bool tx_memory_pool::have_tx(const crypto::hash &id, relay_category tx_category) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
- return m_blockchain.get_db().txpool_has_tx(id);
+ return m_blockchain.get_db().txpool_has_tx(id, tx_category);
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) const
@@ -969,7 +1053,14 @@ namespace cryptonote
bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) const
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
- return m_spent_key_images.end() != m_spent_key_images.find(key_im);
+ bool spent = false;
+ const auto found = m_spent_key_images.find(key_im);
+ if (found != m_spent_key_images.end())
+ {
+ for (const crypto::hash& tx_hash : found->second)
+ spent |= m_blockchain.txpool_tx_matches_category(tx_hash, relay_category::broadcasted);
+ }
+ return spent;
}
//---------------------------------------------------------------------------------
void tx_memory_pool::lock() const
@@ -1143,7 +1234,7 @@ namespace cryptonote
ss << "id: " << txid << std::endl;
if (!short_format) {
cryptonote::transaction tx;
- if (!parse_and_validate_tx_from_blob(*txblob, tx))
+ if (!(meta.pruned ? parse_and_validate_tx_base_from_blob(*txblob, tx) : parse_and_validate_tx_from_blob(*txblob, tx)))
{
MERROR("Failed to parse tx from txpool");
return true; // continue
@@ -1154,13 +1245,14 @@ namespace cryptonote
<< "weight: " << meta.weight << std::endl
<< "fee: " << print_money(meta.fee) << std::endl
<< "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl
+ << "is_local" << (meta.is_local ? 'T' : 'F') << std::endl
<< "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl
<< "max_used_block_height: " << meta.max_used_block_height << std::endl
<< "max_used_block_id: " << meta.max_used_block_id << std::endl
<< "last_failed_height: " << meta.last_failed_height << std::endl
<< "last_failed_id: " << meta.last_failed_id << std::endl;
return true;
- }, !short_format);
+ }, !short_format, relay_category::all);
return ss.str();
}
@@ -1192,13 +1284,19 @@ namespace cryptonote
for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it)
{
txpool_tx_meta_t meta;
- if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta))
+ if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta) && !meta.matches(relay_category::legacy))
{
MERROR(" failed to find tx meta");
continue;
}
LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase));
+ if (meta.pruned)
+ {
+ LOG_PRINT_L2(" tx is pruned");
+ continue;
+ }
+
// Can not exceed maximum block weight
if (max_total_weight < total_weight + meta.weight)
{
@@ -1235,7 +1333,9 @@ namespace cryptonote
}
}
- cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second);
+ // "local" and "stem" txes are filtered above
+ cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(sorted_it->second, relay_category::all);
+
cryptonote::transaction tx;
// Skip transactions that are not ready to be
@@ -1310,7 +1410,7 @@ namespace cryptonote
remove.insert(txid);
}
return true;
- }, false);
+ }, false, relay_category::all);
size_t n_removed = 0;
if (!remove.empty())
@@ -1320,9 +1420,9 @@ namespace cryptonote
{
try
{
- cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid);
+ cryptonote::blobdata txblob = m_blockchain.get_txpool_tx_blob(txid, relay_category::all);
cryptonote::transaction tx;
- if (!parse_and_validate_tx_from_blob(txblob, tx))
+ if (!parse_and_validate_tx_from_blob(txblob, tx)) // remove pruned ones on startup, they're meant to be temporary
{
MERROR("Failed to parse tx from txpool");
continue;
@@ -1381,7 +1481,7 @@ namespace cryptonote
remove.push_back(txid);
return true;
}
- if (!insert_key_images(tx, txid, meta.kept_by_block))
+ if (!insert_key_images(tx, txid, meta.get_relay_method()))
{
MFATAL("Failed to insert key images from txpool tx");
return false;
@@ -1389,7 +1489,7 @@ namespace cryptonote
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.weight, meta.receive_time), txid);
m_txpool_weight += meta.weight;
return true;
- }, true);
+ }, true, relay_category::all);
if (!r)
return false;
}
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index 877f2b82f..f716440ad 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -32,17 +32,20 @@
#include "include_base_utils.h"
#include <set>
+#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <boost/serialization/version.hpp>
#include <boost/utility.hpp>
+#include "span.h"
#include "string_tools.h"
#include "syncobj.h"
#include "math_helper.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/verification_context.h"
+#include "cryptonote_protocol/enums.h"
#include "blockchain_db/blockchain_db.h"
#include "crypto/hash.h"
#include "rpc/core_rpc_server_commands_defs.h"
@@ -105,9 +108,10 @@ namespace cryptonote
* @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t)
*
* @param id the transaction's hash
+ * @tx_relay how the transaction was received
* @param tx_weight the transaction's weight
*/
- bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version);
+ bool add_tx(transaction &tx, const crypto::hash &id, const cryptonote::blobdata &blob, size_t tx_weight, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version);
/**
* @brief add a transaction to the transaction pool
@@ -119,14 +123,13 @@ namespace cryptonote
*
* @param tx the transaction to be added
* @param tvc return-by-reference status about the transaction verification
- * @param kept_by_block has this transaction been in a block?
+ * @tx_relay how the transaction was received
* @param relayed was this transaction from the network or a local client?
- * @param do_not_relay to avoid relaying the transaction to the network
* @param version the version used to create the transaction
*
* @return true if the transaction passes validations, otherwise false
*/
- bool add_tx(transaction &tx, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version);
+ bool add_tx(transaction &tx, tx_verification_context& tvc, relay_method tx_relay, bool relayed, uint8_t version);
/**
* @brief takes a transaction with the given hash from the pool
@@ -139,19 +142,21 @@ namespace cryptonote
* @param relayed return-by-reference was transaction relayed to us by the network?
* @param do_not_relay return-by-reference is transaction not to be relayed to the network?
* @param double_spend_seen return-by-reference was a double spend seen for that transaction?
+ * @param pruned return-by-reference is the tx pruned
*
* @return true unless the transaction cannot be found in the pool
*/
- bool take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen);
+ bool take_tx(const crypto::hash &id, transaction &tx, cryptonote::blobdata &txblob, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen, bool &pruned);
/**
* @brief checks if the pool has a transaction with the given hash
*
* @param id the hash to look for
+ * @param tx_category a filter for txes
*
- * @return true if the transaction is in the pool, otherwise false
+ * @return true if the transaction is in the pool and meets tx_category requirements
*/
- bool have_tx(const crypto::hash &id) const;
+ bool have_tx(const crypto::hash &id, relay_category tx_category) const;
/**
* @brief action to take when notified of a block added to the blockchain
@@ -235,37 +240,37 @@ namespace cryptonote
* @brief get a list of all transactions in the pool
*
* @param txs return-by-reference the list of transactions
- * @param include_unrelayed_txes include unrelayed txes in the result
+ * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
*
*/
- void get_transactions(std::vector<transaction>& txs, bool include_unrelayed_txes = true) const;
+ void get_transactions(std::vector<transaction>& txs, bool include_sensitive = false) const;
/**
* @brief get a list of all transaction hashes in the pool
*
* @param txs return-by-reference the list of transactions
- * @param include_unrelayed_txes include unrelayed txes in the result
+ * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
*
*/
- void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const;
+ void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_sensitive = false) const;
/**
* @brief get (weight, fee, receive time) for all transaction in the pool
*
* @param txs return-by-reference that data
- * @param include_unrelayed_txes include unrelayed txes in the result
+ * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
*
*/
- void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_unrelayed_txes = true) const;
+ void get_transaction_backlog(std::vector<tx_backlog_entry>& backlog, bool include_sensitive = false) const;
/**
* @brief get a summary statistics of all transaction hashes in the pool
*
* @param stats return-by-reference the pool statistics
- * @param include_unrelayed_txes include unrelayed txes in the result
+ * @param include_sensitive return stempool, anonymity-pool, and unrelayed txes
*
*/
- void get_transaction_stats(struct txpool_stats& stats, bool include_unrelayed_txes = true) const;
+ void get_transaction_stats(struct txpool_stats& stats, bool include_sensitive = false) const;
/**
* @brief get information about all transactions and key images in the pool
@@ -274,11 +279,12 @@ namespace cryptonote
*
* @param tx_infos return-by-reference the transactions' information
* @param key_image_infos return-by-reference the spent key images' information
- * @param include_sensitive_data include unrelayed txes and fields that are sensitive to the node privacy
+ * @param include_sensitive_data return stempool, anonymity-pool, and unrelayed
+ * txes and fields that are sensitive to the node privacy
*
* @return true
*/
- bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = true) const;
+ bool get_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos, bool include_sensitive_data = false) const;
/**
* @brief get information about all transactions and key images in the pool
@@ -300,17 +306,18 @@ namespace cryptonote
*
* @return true
*/
- bool check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool> spent) const;
+ bool check_for_key_images(const std::vector<crypto::key_image>& key_images, std::vector<bool>& spent) const;
/**
* @brief get a specific transaction from the pool
*
* @param h the hash of the transaction to get
* @param tx return-by-reference the transaction blob requested
+ * @param tx_relay last relay method us
*
* @return true if the transaction is found, otherwise false
*/
- bool get_transaction(const crypto::hash& h, cryptonote::blobdata& txblob) const;
+ bool get_transaction(const crypto::hash& h, cryptonote::blobdata& txblob, relay_category tx_category) const;
/**
* @brief get a list of all relayable transactions and their hashes
@@ -325,21 +332,22 @@ namespace cryptonote
*
* @return true
*/
- bool get_relayable_transactions(std::vector<std::pair<crypto::hash, cryptonote::blobdata>>& txs) const;
+ bool get_relayable_transactions(std::vector<std::tuple<crypto::hash, cryptonote::blobdata, relay_method>>& txs) const;
/**
* @brief tell the pool that certain transactions were just relayed
*
- * @param txs the list of transactions (and their hashes)
+ * @param hashes list of tx hashes that are about to be relayed
+ * @param tx_relay update how the tx left this node
*/
- void set_relayed(const std::vector<std::pair<crypto::hash, cryptonote::blobdata>>& txs);
+ void set_relayed(epee::span<const crypto::hash> hashes, relay_method tx_relay);
/**
* @brief get the total number of transactions in the pool
*
* @return the number of transactions in the pool
*/
- size_t get_transactions_count(bool include_unrelayed_txes = true) const;
+ size_t get_transactions_count(bool include_sensitive = false) const;
/**
* @brief get a string containing human-readable pool information
@@ -428,6 +436,11 @@ namespace cryptonote
bool double_spend_seen; //!< true iff another tx was seen double spending this one
};
+ /**
+ * @brief get infornation about a single transaction
+ */
+ bool get_transaction_info(const crypto::hash &txid, tx_details &td) const;
+
private:
/**
@@ -435,7 +448,7 @@ namespace cryptonote
*
* @return true on success, false on error
*/
- bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, bool kept_by_block);
+ bool insert_key_images(const transaction_prefix &tx, const crypto::hash &txid, relay_method tx_relay);
/**
* @brief remove old transactions from the pool
@@ -538,7 +551,7 @@ namespace cryptonote
* transaction on the assumption that the original will not be in a
* block again.
*/
- typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash> > key_images_container;
+ typedef std::unordered_map<crypto::key_image, std::unordered_set<crypto::hash>> key_images_container;
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
public:
diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp
index b4f9daa74..67f0b3e5d 100644
--- a/src/cryptonote_protocol/block_queue.cpp
+++ b/src/cryptonote_protocol/block_queue.cpp
@@ -228,13 +228,14 @@ bool block_queue::have(const crypto::hash &hash) const
return have_blocks.find(hash) != have_blocks.end();
}
-std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<crypto::hash> &block_hashes, boost::posix_time::ptime time)
+std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, bool sync_pruned_blocks, uint32_t local_pruning_seed, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<std::pair<crypto::hash, uint64_t>> &block_hashes, boost::posix_time::ptime time)
{
boost::unique_lock<boost::recursive_mutex> lock(mutex);
MDEBUG("reserve_span: first_block_height " << first_block_height << ", last_block_height " << last_block_height
- << ", max " << max_blocks << ", seed " << epee::string_tools::to_string_hex(pruning_seed) << ", blockchain_height " <<
- blockchain_height << ", block hashes size " << block_hashes.size());
+ << ", max " << max_blocks << ", peer seed " << epee::string_tools::to_string_hex(pruning_seed) << ", blockchain_height " <<
+ blockchain_height << ", block hashes size " << block_hashes.size() << ", local seed " << epee::string_tools::to_string_hex(local_pruning_seed)
+ << ", sync_pruned_blocks " << sync_pruned_blocks);
if (last_block_height < first_block_height || max_blocks == 0)
{
MDEBUG("reserve_span: early out: first_block_height " << first_block_height << ", last_block_height " << last_block_height << ", max_blocks " << max_blocks);
@@ -248,22 +249,25 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei
// skip everything we've already requested
uint64_t span_start_height = last_block_height - block_hashes.size() + 1;
- std::vector<crypto::hash>::const_iterator i = block_hashes.begin();
- while (i != block_hashes.end() && requested_internal(*i))
+ std::vector<std::pair<crypto::hash, uint64_t>>::const_iterator i = block_hashes.begin();
+ while (i != block_hashes.end() && requested_internal((*i).first))
{
++i;
++span_start_height;
}
- // if the peer's pruned for the starting block and its unpruned stripe comes next, start downloading from there
- const uint32_t next_unpruned_height = tools::get_next_unpruned_block_height(span_start_height, blockchain_height, pruning_seed);
- MDEBUG("reserve_span: next_unpruned_height " << next_unpruned_height << " from " << span_start_height << " and seed "
- << epee::string_tools::to_string_hex(pruning_seed) << ", limit " << span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE);
- if (next_unpruned_height > span_start_height && next_unpruned_height < span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE)
+ if (!sync_pruned_blocks)
{
- MDEBUG("We can download from next span: ideal height " << span_start_height << ", next unpruned height " << next_unpruned_height <<
- "(+" << next_unpruned_height - span_start_height << "), current seed " << pruning_seed);
- span_start_height = next_unpruned_height;
+ // if the peer's pruned for the starting block and its unpruned stripe comes next, start downloading from there
+ const uint32_t next_unpruned_height = tools::get_next_unpruned_block_height(span_start_height, blockchain_height, pruning_seed);
+ MDEBUG("reserve_span: next_unpruned_height " << next_unpruned_height << " from " << span_start_height << " and seed "
+ << epee::string_tools::to_string_hex(pruning_seed) << ", limit " << span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE);
+ if (next_unpruned_height > span_start_height && next_unpruned_height < span_start_height + CRYPTONOTE_PRUNING_STRIPE_SIZE)
+ {
+ MDEBUG("We can download from next span: ideal height " << span_start_height << ", next unpruned height " << next_unpruned_height <<
+ "(+" << next_unpruned_height - span_start_height << "), current seed " << pruning_seed);
+ span_start_height = next_unpruned_height;
+ }
}
MDEBUG("span_start_height: " <<span_start_height);
const uint64_t block_hashes_start_height = last_block_height - block_hashes.size() + 1;
@@ -274,7 +278,7 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei
}
i = block_hashes.begin() + span_start_height - block_hashes_start_height;
- while (i != block_hashes.end() && requested_internal(*i))
+ while (i != block_hashes.end() && requested_internal((*i).first))
{
++i;
++span_start_height;
@@ -282,9 +286,16 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei
uint64_t span_length = 0;
std::vector<crypto::hash> hashes;
- while (i != block_hashes.end() && span_length < max_blocks && tools::has_unpruned_block(span_start_height + span_length, blockchain_height, pruning_seed))
+ bool first_is_pruned = sync_pruned_blocks && !tools::has_unpruned_block(span_start_height + span_length, blockchain_height, local_pruning_seed);
+ while (i != block_hashes.end() && span_length < max_blocks && (sync_pruned_blocks || tools::has_unpruned_block(span_start_height + span_length, blockchain_height, pruning_seed)))
{
- hashes.push_back(*i);
+ // if we want to sync pruned blocks, stop at the first block for which we need full data
+ if (sync_pruned_blocks && first_is_pruned == tools::has_unpruned_block(span_start_height + span_length, blockchain_height, local_pruning_seed))
+ {
+ MDEBUG("Stopping at " << span_start_height + span_length << " for peer on stripe " << tools::get_pruning_stripe(pruning_seed) << " as we need full data for " << tools::get_pruning_stripe(local_pruning_seed));
+ break;
+ }
+ hashes.push_back((*i).first);
++i;
++span_length;
}
diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h
index 1bef01d67..93c6532e7 100644
--- a/src/cryptonote_protocol/block_queue.h
+++ b/src/cryptonote_protocol/block_queue.h
@@ -78,7 +78,7 @@ namespace cryptonote
void print() const;
std::string get_overview(uint64_t blockchain_height) const;
bool has_unpruned_height(uint64_t block_height, uint64_t blockchain_height, uint32_t pruning_seed) const;
- std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<crypto::hash> &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time());
+ std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, bool sync_pruned_blocks, uint32_t local_pruning_seed, uint32_t pruning_seed, uint64_t blockchain_height, const std::vector<std::pair<crypto::hash, uint64_t>> &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time());
uint64_t get_next_needed_height(uint64_t blockchain_height) const;
std::pair<uint64_t, uint64_t> get_next_span_if_scheduled(std::vector<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const;
void reset_next_span_time(boost::posix_time::ptime t = boost::posix_time::microsec_clock::universal_time());
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index b2f8da399..201001c8e 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -56,6 +56,7 @@ namespace cryptonote
std::string ip;
std::string port;
uint16_t rpc_port;
+ uint32_t rpc_credits_per_hash;
std::string peer_id;
@@ -94,6 +95,7 @@ namespace cryptonote
KV_SERIALIZE(ip)
KV_SERIALIZE(port)
KV_SERIALIZE(rpc_port)
+ KV_SERIALIZE(rpc_credits_per_hash)
KV_SERIALIZE(peer_id)
KV_SERIALIZE(recv_count)
KV_SERIALIZE(recv_idle_time)
@@ -116,14 +118,51 @@ namespace cryptonote
/************************************************************************/
/* */
/************************************************************************/
+ struct tx_blob_entry
+ {
+ blobdata blob;
+ crypto::hash prunable_hash;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(blob)
+ KV_SERIALIZE_VAL_POD_AS_BLOB(prunable_hash)
+ END_KV_SERIALIZE_MAP()
+
+ tx_blob_entry(const blobdata &bd = {}, const crypto::hash &h = crypto::null_hash): blob(bd), prunable_hash(h) {}
+ };
struct block_complete_entry
{
+ bool pruned;
blobdata block;
- std::vector<blobdata> txs;
+ uint64_t block_weight;
+ std::vector<tx_blob_entry> txs;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(pruned, false)
KV_SERIALIZE(block)
- KV_SERIALIZE(txs)
+ KV_SERIALIZE_OPT(block_weight, (uint64_t)0)
+ if (this_ref.pruned)
+ {
+ KV_SERIALIZE(txs)
+ }
+ else
+ {
+ std::vector<blobdata> txs;
+ if (is_store)
+ {
+ txs.reserve(this_ref.txs.size());
+ for (const auto &e: this_ref.txs) txs.push_back(e.blob);
+ }
+ epee::serialization::selector<is_store>::serialize(txs, stg, hparent_section, "txs");
+ if (!is_store)
+ {
+ block_complete_entry &self = const_cast<block_complete_entry&>(this_ref);
+ self.txs.clear();
+ self.txs.reserve(txs.size());
+ for (auto &e: txs) self.txs.push_back({std::move(e), crypto::null_hash});
+ }
+ }
END_KV_SERIALIZE_MAP()
+
+ block_complete_entry(): pruned(false), block_weight(0) {}
};
@@ -176,8 +215,11 @@ namespace cryptonote
struct request_t
{
std::vector<crypto::hash> blocks;
+ bool prune;
+
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks)
+ KV_SERIALIZE_OPT(prune, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -229,9 +271,11 @@ namespace cryptonote
struct request_t
{
std::list<crypto::hash> block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */
+ bool prune;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
+ KV_SERIALIZE_OPT(prune, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -248,6 +292,7 @@ namespace cryptonote
uint64_t cumulative_difficulty;
uint64_t cumulative_difficulty_top64;
std::vector<crypto::hash> m_block_ids;
+ std::vector<uint64_t> m_block_weights;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(start_height)
@@ -255,6 +300,7 @@ namespace cryptonote
KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE(cumulative_difficulty_top64)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_weights)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h
index dcc5ec6ed..ddbd45a61 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -112,6 +112,7 @@ namespace cryptonote
void stop();
void on_connection_close(cryptonote_connection_context &context);
void set_max_out_peers(unsigned int max) { m_max_out_peers = max; }
+ bool no_sync() const { return m_no_sync; }
void set_no_sync(bool value) { m_no_sync = value; }
std::string get_peers_overview() const;
std::pair<uint32_t, uint32_t> get_next_needed_pruning_stripe() const;
@@ -129,7 +130,7 @@ namespace cryptonote
//----------------- i_bc_protocol_layout ---------------------------------------
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context);
- virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context);
+ virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone);
//----------------------------------------------------------------------------------
//bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context);
bool should_drop_connection(cryptonote_connection_context& context, uint32_t next_stripe);
@@ -137,7 +138,9 @@ namespace cryptonote
size_t get_synchronizing_connections_count();
bool on_connection_synchronized();
bool should_download_next_span(cryptonote_connection_context& context, bool standby);
+ bool should_ask_for_pruned_data(cryptonote_connection_context& context, uint64_t first_block_height, uint64_t nblocks, bool check_block_weights) const;
void drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans);
+ void drop_connection_with_score(cryptonote_connection_context &context, unsigned int score, bool flush_all_spans);
bool kick_idle_peers();
bool check_standby_peers();
bool update_sync_search();
@@ -164,6 +167,7 @@ namespace cryptonote
uint64_t m_sync_spans_downloaded, m_sync_old_spans_downloaded, m_sync_bad_spans_downloaded;
uint64_t m_sync_download_chain_size, m_sync_download_objects_size;
size_t m_block_download_max_size;
+ bool m_sync_pruned_blocks;
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 65115ee72..ae4abeef5 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -55,7 +55,7 @@
if (ELPP->vRegistry()->allowed(level, cat)) { \
init; \
if (test) \
- el::base::Writer(level, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(cat) << x; \
+ el::base::Writer(level, el::Color::Default, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(cat) << x; \
} \
} while(0)
@@ -106,6 +106,7 @@ namespace cryptonote
m_sync_download_objects_size = 0;
m_block_download_max_size = command_line::get_arg(vm, cryptonote::arg_block_download_max_size);
+ m_sync_pruned_blocks = command_line::get_arg(vm, cryptonote::arg_sync_pruned_blocks);
return true;
}
@@ -134,10 +135,11 @@ namespace cryptonote
if(context.m_state == cryptonote_connection_context::state_synchronizing)
{
- NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
+ NOTIFY_REQUEST_CHAIN::request r = {};
context.m_needed_objects.clear();
m_core.get_short_chain_history(r.block_ids);
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
+ r.prune = m_sync_pruned_blocks;
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
MLOG_PEER_STATE("requesting chain");
@@ -244,6 +246,7 @@ namespace cryptonote
cnx.port = std::to_string(cntxt.m_remote_address.as<epee::net_utils::ipv4_network_address>().port());
}
cnx.rpc_port = cntxt.m_rpc_port;
+ cnx.rpc_credits_per_hash = cntxt.m_rpc_credits_per_hash;
std::stringstream peer_id_str;
peer_id_str << std::hex << std::setw(16) << peer_id;
@@ -342,11 +345,6 @@ namespace cryptonote
if(m_core.have_block(hshd.top_id))
{
- if (target > m_core.get_current_blockchain_height())
- {
- MINFO(context << "peer is not ahead of us and we're syncing, disconnecting");
- return false;
- }
context.m_state = cryptonote_connection_context::state_normal;
if(is_inital && target == m_core.get_current_blockchain_height())
on_connection_synchronized();
@@ -370,7 +368,7 @@ namespace cryptonote
uint64_t max_block_height = std::max(hshd.current_height,m_core.get_current_blockchain_height());
uint64_t last_block_v1 = m_core.get_nettype() == TESTNET ? 624633 : m_core.get_nettype() == MAINNET ? 1009826 : (uint64_t)-1;
uint64_t diff_v2 = max_block_height > last_block_v1 ? std::min(abs_diff, max_block_height - last_block_v1) : 0;
- MCLOG(is_inital ? el::Level::Info : el::Level::Debug, "global", context << "Sync data returned a new top block candidate: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height
+ MCLOG(is_inital ? el::Level::Info : el::Level::Debug, "global", el::Color::Yellow, context << "Sync data returned a new top block candidate: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height
<< " [Your node is " << 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");
@@ -426,7 +424,7 @@ namespace cryptonote
template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::get_payload_sync_data(blobdata& data)
{
- CORE_SYNC_DATA hsd = boost::value_initialized<CORE_SYNC_DATA>();
+ CORE_SYNC_DATA hsd = {};
get_payload_sync_data(hsd);
epee::serialization::store_t_to_binary(hsd, data);
return true;
@@ -457,7 +455,7 @@ namespace cryptonote
for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++)
{
cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- m_core.handle_incoming_tx(*tx_blob_it, tvc, true, true, false);
+ m_core.handle_incoming_tx(*tx_blob_it, tvc, relay_method::block, true);
if(tvc.m_verifivation_failed)
{
LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection");
@@ -468,7 +466,7 @@ namespace cryptonote
}
}
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
m_core.handle_incoming_block(arg.b.block, pblocks.empty() ? NULL : &pblocks[0], bvc); // got block from handle_notify_new_block
if (!m_core.cleanup_handle_incoming_blocks(true))
{
@@ -480,7 +478,7 @@ namespace cryptonote
if(bvc.m_verifivation_failed)
{
LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection");
- drop_connection(context, true, false);
+ drop_connection_with_score(context, bvc.m_bad_pow ? P2P_IP_FAILS_BEFORE_BLOCK : 1, false);
return 1;
}
if(bvc.m_added_to_main_chain)
@@ -491,8 +489,9 @@ namespace cryptonote
{
context.m_needed_objects.clear();
context.m_state = cryptonote_connection_context::state_synchronizing;
- NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
+ NOTIFY_REQUEST_CHAIN::request r = {};
m_core.get_short_chain_history(r.block_ids);
+ r.prune = m_sync_pruned_blocks;
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
@@ -540,9 +539,9 @@ namespace cryptonote
return 1;
}
}
-
- std::vector<blobdata> have_tx;
-
+
+ std::vector<tx_blob_entry> have_tx;
+
// Instead of requesting missing transactions by hash like BTC,
// we do it by index (thanks to a suggestion from moneromooo) because
// we're way cooler .. and also because they're smaller than hashes.
@@ -556,7 +555,7 @@ namespace cryptonote
for(auto& tx_blob: arg.b.txs)
{
- if(parse_and_validate_tx_from_blob(tx_blob, tx))
+ if(parse_and_validate_tx_from_blob(tx_blob.blob, tx))
{
try
{
@@ -620,7 +619,7 @@ namespace cryptonote
{
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)
+ if(!m_core.handle_incoming_tx(tx_blob, tvc, relay_method::block, true) || tvc.m_verifivation_failed)
{
LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection");
drop_connection(context, false, false);
@@ -641,7 +640,7 @@ namespace cryptonote
LOG_ERROR_CCONTEXT
(
"sent wrong tx: failed to parse and validate transaction: "
- << epee::string_tools::buff_to_hex_nodelimer(tx_blob)
+ << epee::string_tools::buff_to_hex_nodelimer(tx_blob.blob)
<< ", dropping connection"
);
@@ -668,15 +667,15 @@ namespace cryptonote
drop_connection(context, false, false);
m_core.resume_mine();
return 1;
- }
-
+ }
+
size_t tx_idx = 0;
for(auto& tx_hash: new_block.tx_hashes)
{
cryptonote::blobdata txblob;
- if(m_core.get_pool_transaction(tx_hash, txblob))
+ if(m_core.get_pool_transaction(tx_hash, txblob, relay_category::broadcasted))
{
- have_tx.push_back(txblob);
+ have_tx.push_back({txblob, crypto::null_hash});
}
else
{
@@ -688,7 +687,7 @@ namespace cryptonote
{
if (txes.size() == 1)
{
- have_tx.push_back(tx_to_blob(txes.front()));
+ have_tx.push_back({tx_to_blob(txes.front()), crypto::null_hash});
}
else
{
@@ -703,7 +702,7 @@ namespace cryptonote
need_tx_indices.push_back(tx_idx);
}
}
-
+
++tx_idx;
}
@@ -740,7 +739,7 @@ namespace cryptonote
return 1;
}
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
m_core.handle_incoming_block(arg.b.block, pblocks.empty() ? NULL : &pblocks[0], bvc); // got block from handle_notify_new_block
if (!m_core.cleanup_handle_incoming_blocks(true))
{
@@ -753,7 +752,7 @@ namespace cryptonote
if( bvc.m_verifivation_failed )
{
LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection");
- drop_connection(context, true, false);
+ drop_connection_with_score(context, bvc.m_bad_pow ? P2P_IP_FAILS_BEFORE_BLOCK : 1, false);
return 1;
}
if( bvc.m_added_to_main_chain )
@@ -768,9 +767,10 @@ namespace cryptonote
{
context.m_needed_objects.clear();
context.m_state = cryptonote_connection_context::state_synchronizing;
- NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
+ NOTIFY_REQUEST_CHAIN::request r = {};
m_core.get_short_chain_history(r.block_ids);
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
+ r.prune = m_sync_pruned_blocks;
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() );
post_notify<NOTIFY_REQUEST_CHAIN>(r, context);
MLOG_PEER_STATE("requesting chain");
@@ -872,7 +872,7 @@ namespace cryptonote
for(auto& tx: txs)
{
- fluffy_response.b.txs.push_back(t_serializable_object_to_blob(tx));
+ fluffy_response.b.txs.push_back({t_serializable_object_to_blob(tx), crypto::null_hash});
}
MLOG_P2P_MESSAGE
@@ -909,8 +909,8 @@ namespace cryptonote
newtxs.reserve(arg.txs.size());
for (size_t i = 0; i < arg.txs.size(); ++i)
{
- cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- m_core.handle_incoming_tx(arg.txs[i], tvc, false, true, false);
+ cryptonote::tx_verification_context tvc{};
+ m_core.handle_incoming_tx({arg.txs[i], crypto::null_hash}, tvc, relay_method::fluff, true);
if(tvc.m_verifivation_failed)
{
LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection");
@@ -925,7 +925,7 @@ namespace cryptonote
if(arg.txs.size())
{
//TODO: add announce usage here
- relay_transactions(arg, context);
+ relay_transactions(arg, context.m_connection_id, context.m_remote_address.get_zone());
}
return 1;
@@ -991,7 +991,7 @@ namespace cryptonote
for (const auto &element : arg.blocks) {
blocks_size += element.block.size();
for (const auto &tx : element.txs)
- blocks_size += tx.size();
+ blocks_size += tx.blob.size();
}
size += blocks_size;
@@ -1090,6 +1090,53 @@ namespace cryptonote
return 1;
}
+ const bool pruned_ok = should_ask_for_pruned_data(context, start_height, arg.blocks.size(), true);
+ if (!pruned_ok)
+ {
+ // if we don't want pruned data, check we did not get any
+ for (block_complete_entry& block_entry: arg.blocks)
+ {
+ if (block_entry.pruned)
+ {
+ MERROR(context << "returned a pruned block, dropping connection");
+ drop_connection(context, false, false);
+ ++m_sync_bad_spans_downloaded;
+ return 1;
+ }
+ if (block_entry.block_weight)
+ {
+ MERROR(context << "returned a block weight for a non pruned block, dropping connection");
+ drop_connection(context, false, false);
+ ++m_sync_bad_spans_downloaded;
+ return 1;
+ }
+ for (const tx_blob_entry &tx_entry: block_entry.txs)
+ {
+ if (tx_entry.prunable_hash != crypto::null_hash)
+ {
+ MERROR(context << "returned at least one pruned object which we did not expect, dropping connection");
+ drop_connection(context, false, false);
+ ++m_sync_bad_spans_downloaded;
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ // we accept pruned data, check that if we got some, then no weights are zero
+ for (block_complete_entry& block_entry: arg.blocks)
+ {
+ if (block_entry.block_weight == 0 && block_entry.pruned)
+ {
+ MERROR(context << "returned at least one pruned block with 0 weight, dropping connection");
+ drop_connection(context, false, false);
+ ++m_sync_bad_spans_downloaded;
+ return 1;
+ }
+ }
+ }
+
{
MLOG_YELLOW(el::Level::Debug, context << " Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size()
<< ", blocks: " << start_height << " - " << (start_height + arg.blocks.size() - 1) <<
@@ -1269,22 +1316,36 @@ namespace cryptonote
TIME_MEASURE_START(transactions_process_time);
num_txs += block_entry.txs.size();
std::vector<tx_verification_context> tvc;
- m_core.handle_incoming_txs(block_entry.txs, tvc, true, true, false);
+ m_core.handle_incoming_txs(block_entry.txs, tvc, relay_method::block, true);
if (tvc.size() != block_entry.txs.size())
{
LOG_ERROR_CCONTEXT("Internal error: tvc.size() != block_entry.txs.size()");
+ if (!m_core.cleanup_handle_incoming_blocks())
+ {
+ LOG_PRINT_CCONTEXT_L0("Failure in cleanup_handle_incoming_blocks");
+ return 1;
+ }
return 1;
}
- std::vector<blobdata>::const_iterator it = block_entry.txs.begin();
+ std::vector<tx_blob_entry>::const_iterator it = block_entry.txs.begin();
for (size_t i = 0; i < tvc.size(); ++i, ++it)
{
if(tvc[i].m_verifivation_failed)
{
if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{
cryptonote::transaction tx;
- parse_and_validate_tx_from_blob(*it, tx); // must succeed if we got here
+ crypto::hash txid;
+ if (it->prunable_hash == crypto::null_hash)
+ {
+ parse_and_validate_tx_from_blob(it->blob, tx, txid); // must succeed if we got here
+ }
+ else
+ {
+ parse_and_validate_tx_base_from_blob(it->blob, tx); // must succeed if we got here
+ txid = get_pruned_transaction_hash(tx, it->prunable_hash);
+ }
LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = "
- << epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(tx)) << ", dropping connection");
+ << epee::string_tools::pod_to_hex(txid) << ", dropping connection");
drop_connection(context, false, true);
return 1;
}))
@@ -1306,7 +1367,7 @@ namespace cryptonote
// process block
TIME_MEASURE_START(block_process_time);
- block_verification_context bvc = boost::value_initialized<block_verification_context>();
+ block_verification_context bvc = {};
m_core.handle_incoming_block(block_entry.block, pblocks.empty() ? NULL : &pblocks[blockidx], bvc, false); // <--- process block
@@ -1314,7 +1375,7 @@ namespace cryptonote
{
if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{
LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection");
- drop_connection(context, true, true);
+ drop_connection_with_score(context, bvc.m_bad_pow ? P2P_IP_FAILS_BEFORE_BLOCK : 1, true);
return 1;
}))
LOG_ERROR_CCONTEXT("span connection id not found");
@@ -1543,7 +1604,7 @@ skip:
{
MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_CHAIN (" << arg.block_ids.size() << " blocks");
NOTIFY_RESPONSE_CHAIN_ENTRY::request r;
- if(!m_core.find_blockchain_supplement(arg.block_ids, r))
+ if(!m_core.find_blockchain_supplement(arg.block_ids, !arg.prune, r))
{
LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN.");
drop_connection(context, false, false);
@@ -1662,6 +1723,12 @@ skip:
MDEBUG(context << "This peer has needed stripe " << peer_stripe << ", not dropping");
return false;
}
+ const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed());
+ if (m_sync_pruned_blocks && local_stripe && next_stripe != local_stripe)
+ {
+ MDEBUG(context << "We can sync pruned blocks off this peer, not dropping");
+ return false;
+ }
if (!context.m_needed_objects.empty())
{
@@ -1701,22 +1768,42 @@ skip:
{
// take out blocks we already have
size_t skip = 0;
- while (skip < context.m_needed_objects.size() && (m_core.have_block(context.m_needed_objects[skip]) || (check_block_queue && m_block_queue.have(context.m_needed_objects[skip]))))
+ while (skip < context.m_needed_objects.size() && (m_core.have_block(context.m_needed_objects[skip].first) || (check_block_queue && m_block_queue.have(context.m_needed_objects[skip].first))))
{
// if we're popping the last hash, record it so we can ask again from that hash,
// this prevents never being able to progress on peers we get old hash lists from
if (skip + 1 == context.m_needed_objects.size())
- context.m_last_known_hash = context.m_needed_objects[skip];
+ context.m_last_known_hash = context.m_needed_objects[skip].first;
++skip;
}
if (skip > 0)
{
MDEBUG(context << "skipping " << skip << "/" << context.m_needed_objects.size() << " blocks");
- context.m_needed_objects = std::vector<crypto::hash>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end());
+ context.m_needed_objects = std::vector<std::pair<crypto::hash, uint64_t>>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end());
}
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
+ bool t_cryptonote_protocol_handler<t_core>::should_ask_for_pruned_data(cryptonote_connection_context& context, uint64_t first_block_height, uint64_t nblocks, bool check_block_weights) const
+ {
+ if (!m_sync_pruned_blocks)
+ return false;
+ if (!m_core.is_within_compiled_block_hash_area(first_block_height + nblocks - 1))
+ return false;
+ const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed());
+ if (local_stripe == 0)
+ return false;
+ // assumes the span size is less or equal to the stripe size
+ bool full_data_needed = tools::get_pruning_stripe(first_block_height, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES) == local_stripe
+ || tools::get_pruning_stripe(first_block_height + nblocks - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES) == local_stripe;
+ if (full_data_needed)
+ return false;
+ if (check_block_weights && !m_core.has_block_weights(first_block_height, nblocks))
+ return false;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------
+ template<class t_core>
bool t_cryptonote_protocol_handler<t_core>::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks, bool force_next_span)
{
// flush stale spans
@@ -1739,6 +1826,7 @@ skip:
const auto next_needed_pruning_stripe = get_next_needed_pruning_stripe();
const uint32_t add_stripe = tools::get_pruning_stripe(bc_height, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES);
const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed);
+ const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed());
const size_t block_queue_size_threshold = m_block_download_max_size ? m_block_download_max_size : BLOCK_QUEUE_SIZE_THRESHOLD;
bool queue_proceed = nspans < BLOCK_QUEUE_NSPANS_THRESHOLD || size < block_queue_size_threshold;
// get rid of blocks we already requested, or already have
@@ -1749,7 +1837,7 @@ skip:
next_block_height = next_needed_height;
else
next_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1;
- bool stripe_proceed_main = (add_stripe == 0 || peer_stripe == 0 || add_stripe == peer_stripe) && (next_block_height < bc_height + BLOCK_QUEUE_FORCE_DOWNLOAD_NEAR_BLOCKS || next_needed_height < bc_height + BLOCK_QUEUE_FORCE_DOWNLOAD_NEAR_BLOCKS);
+ bool stripe_proceed_main = ((m_sync_pruned_blocks && local_stripe && add_stripe != local_stripe) || add_stripe == 0 || peer_stripe == 0 || add_stripe == peer_stripe) && (next_block_height < bc_height + BLOCK_QUEUE_FORCE_DOWNLOAD_NEAR_BLOCKS || next_needed_height < bc_height + BLOCK_QUEUE_FORCE_DOWNLOAD_NEAR_BLOCKS);
bool stripe_proceed_secondary = tools::has_unpruned_block(next_block_height, context.m_remote_blockchain_height, context.m_pruning_seed);
bool proceed = stripe_proceed_main || (queue_proceed && stripe_proceed_secondary);
if (!stripe_proceed_main && !stripe_proceed_secondary && should_drop_connection(context, tools::get_pruning_stripe(next_block_height, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES)))
@@ -1812,8 +1900,9 @@ skip:
{
const uint64_t now = tools::get_tick_count();
const uint64_t dt = now - m_last_add_end_time;
- if (tools::ticks_to_ns(dt) >= DROP_ON_SYNC_WEDGE_THRESHOLD)
+ if (m_last_add_end_time && tools::ticks_to_ns(dt) >= DROP_ON_SYNC_WEDGE_THRESHOLD)
{
+ MDEBUG(context << "ns " << tools::ticks_to_ns(dt) << " from " << m_last_add_end_time << " and " << now);
MDEBUG(context << "Block addition seems to have wedged, dropping connection");
return false;
}
@@ -1880,7 +1969,8 @@ skip:
skip_unneeded_hashes(context, false);
const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1;
- span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, context.m_pruning_seed, context.m_remote_blockchain_height, context.m_needed_objects);
+ bool sync_pruned_blocks = m_sync_pruned_blocks && m_core.get_blockchain_pruning_seed();
+ span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, sync_pruned_blocks, m_core.get_blockchain_pruning_seed(), context.m_pruning_seed, context.m_remote_blockchain_height, context.m_needed_objects);
MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second);
if (span.second > 0)
{
@@ -1910,7 +2000,8 @@ skip:
++count;
context.m_requested_objects.insert(hash);
// that's atrocious O(n) wise, but this is rare
- auto i = std::find(context.m_needed_objects.begin(), context.m_needed_objects.end(), hash);
+ auto i = std::find_if(context.m_needed_objects.begin(), context.m_needed_objects.end(),
+ [&hash](const std::pair<crypto::hash, uint64_t> &o) { return o.first == hash; });
if (i != context.m_needed_objects.end())
context.m_needed_objects.erase(i);
}
@@ -1929,7 +2020,7 @@ skip:
return false;
}
if (skip > 0)
- context.m_needed_objects = std::vector<crypto::hash>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end());
+ context.m_needed_objects = std::vector<std::pair<crypto::hash, uint64_t>>(context.m_needed_objects.begin() + skip, context.m_needed_objects.end());
if (context.m_needed_objects.size() < span.second)
{
MERROR("ERROR: span " << span.first << "/" << span.second << ", m_needed_objects " << context.m_needed_objects.size());
@@ -1938,18 +2029,37 @@ skip:
for (size_t n = 0; n < span.second; ++n)
{
- req.blocks.push_back(context.m_needed_objects[n]);
+ req.blocks.push_back(context.m_needed_objects[n].first);
++count;
- context.m_requested_objects.insert(context.m_needed_objects[n]);
+ context.m_requested_objects.insert(context.m_needed_objects[n].first);
}
- context.m_needed_objects = std::vector<crypto::hash>(context.m_needed_objects.begin() + span.second, context.m_needed_objects.end());
+ context.m_needed_objects = std::vector<std::pair<crypto::hash, uint64_t>>(context.m_needed_objects.begin() + span.second, context.m_needed_objects.end());
}
+ req.prune = should_ask_for_pruned_data(context, span.first, span.second, true);
+
+ // if we need to ask for full data and that peer does not have the right stripe, we can't ask it
+ if (!req.prune && context.m_pruning_seed)
+ {
+ const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed);
+ const uint32_t first_stripe = tools::get_pruning_stripe(span.first, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES);
+ const uint32_t last_stripe = tools::get_pruning_stripe(span.first + span.second - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES);
+ if ((((first_stripe && peer_stripe != first_stripe) || (last_stripe && peer_stripe != last_stripe)) && !m_sync_pruned_blocks) || (m_sync_pruned_blocks && req.prune))
+ {
+ MDEBUG(context << "We need full data, but the peer does not have it, dropping peer");
+ return false;
+ }
+ }
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size()
<< "requested blocks count=" << count << " / " << count_limit << " from " << span.first << ", first hash " << req.blocks.front());
//epee::net_utils::network_throttle_manager::get_global_throttle_inreq().logger_handle_net("log/dr-monero/net/req-all.data", sec, get_avg_block_size());
+ MDEBUG("Asking for " << (req.prune ? "pruned" : "full") << " data, start/end "
+ << tools::get_pruning_stripe(span.first, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES)
+ << "/" << tools::get_pruning_stripe(span.first + span.second - 1, context.m_remote_blockchain_height, CRYPTONOTE_PRUNING_LOG_STRIPES)
+ << ", ours " << tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed()) << ", peer stripe " << tools::get_pruning_stripe(context.m_pruning_seed));
+
post_notify<NOTIFY_REQUEST_GET_OBJECTS>(req, context);
MLOG_PEER_STATE("requesting objects");
return true;
@@ -1959,7 +2069,8 @@ skip:
// drop it to make space for other peers, or ask for a span further down the line
const uint32_t next_stripe = get_next_needed_pruning_stripe().first;
const uint32_t peer_stripe = tools::get_pruning_stripe(context.m_pruning_seed);
- if (next_stripe && peer_stripe && next_stripe != peer_stripe)
+ const uint32_t local_stripe = tools::get_pruning_stripe(m_core.get_blockchain_pruning_seed());
+ if (!(m_sync_pruned_blocks && peer_stripe == local_stripe) && next_stripe && peer_stripe && next_stripe != peer_stripe)
{
// at this point, we have to either close the connection, or start getting blocks past the
// current point, or become dormant
@@ -2010,7 +2121,7 @@ skip:
if(context.m_last_response_height < context.m_remote_blockchain_height-1)
{//we have to fetch more objects ids, request blockchain entry
- NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>();
+ NOTIFY_REQUEST_CHAIN::request r = {};
m_core.get_short_chain_history(r.block_ids);
CHECK_AND_ASSERT_MES(!r.block_ids.empty(), false, "Short chain history is empty");
@@ -2022,6 +2133,7 @@ skip:
}
handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?)
+ r.prune = m_sync_pruned_blocks;
//std::string blob; // for calculate size of request
//epee::serialization::store_t_to_binary(r, blob);
@@ -2064,7 +2176,7 @@ skip:
bool t_cryptonote_protocol_handler<t_core>::on_connection_synchronized()
{
bool val_expected = false;
- if(m_synchronized.compare_exchange_strong(val_expected, true))
+ if(!m_core.is_within_compiled_block_hash_area(m_core.get_current_blockchain_height()) && m_synchronized.compare_exchange_strong(val_expected, true))
{
MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL
<< "You are now synchronized with the network. You may now start monero-wallet-cli." << ENDL
@@ -2128,6 +2240,12 @@ skip:
drop_connection(context, true, false);
return 1;
}
+ if (!arg.m_block_weights.empty() && arg.m_block_weights.size() != arg.m_block_ids.size())
+ {
+ LOG_ERROR_CCONTEXT("sent invalid block weight array, dropping connection");
+ drop_connection(context, true, false);
+ return 1;
+ }
MDEBUG(context << "first block hash " << arg.m_block_ids.front() << ", last " << arg.m_block_ids.back());
if (arg.total_height >= CRYPTONOTE_MAX_BLOCK_NUMBER || arg.m_block_ids.size() >= CRYPTONOTE_MAX_BLOCK_NUMBER)
@@ -2147,7 +2265,7 @@ skip:
return 1;
}
- uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids);
+ uint64_t n_use_blocks = m_core.prevalidate_block_hashes(arg.start_height, arg.m_block_ids, arg.m_block_weights);
if (n_use_blocks + HASH_OF_HASHES_STEP <= arg.m_block_ids.size())
{
LOG_ERROR_CCONTEXT("Most blocks are invalid, dropping connection");
@@ -2157,9 +2275,10 @@ skip:
context.m_needed_objects.clear();
uint64_t added = 0;
- for(auto& bl_id: arg.m_block_ids)
+ for (size_t i = 0; i < arg.m_block_ids.size(); ++i)
{
- context.m_needed_objects.push_back(bl_id);
+ const uint64_t block_weight = arg.m_block_weights.empty() ? 0 : arg.m_block_weights[i];
+ context.m_needed_objects.push_back(std::make_pair(arg.m_block_ids[i], block_weight));
if (++added == n_use_blocks)
break;
}
@@ -2183,7 +2302,7 @@ skip:
{
NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_arg = AUTO_VAL_INIT(fluffy_arg);
fluffy_arg.current_blockchain_height = arg.current_blockchain_height;
- std::vector<blobdata> fluffy_txs;
+ std::vector<tx_blob_entry> fluffy_txs;
fluffy_arg.b = arg.b;
fluffy_arg.b.txs = fluffy_txs;
@@ -2225,14 +2344,14 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
- bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)
+ bool t_cryptonote_protocol_handler<t_core>::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone)
{
- for(auto& tx_blob : arg.txs)
- m_core.on_transaction_relayed(tx_blob);
-
- // no check for success, so tell core they're relayed unconditionally
- m_p2p->send_txs(std::move(arg.txs), exclude_context.m_remote_address.get_zone(), exclude_context.m_connection_id, m_core.pad_transactions());
- return true;
+ /* Push all outgoing transactions to this function. The behavior needs to
+ identify how the transaction is going to be relayed, and then update the
+ local mempool before doing the relay. The code was already updating the
+ DB twice on received transactions - it is difficult to workaround this
+ due to the internal design. */
+ return m_p2p->send_txs(std::move(arg.txs), zone, source, m_core) != epee::net_utils::zone::invalid;
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
@@ -2310,14 +2429,14 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
- void t_cryptonote_protocol_handler<t_core>::drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans)
+ void t_cryptonote_protocol_handler<t_core>::drop_connection_with_score(cryptonote_connection_context &context, unsigned score, bool flush_all_spans)
{
LOG_DEBUG_CC(context, "dropping connection id " << context.m_connection_id << " (pruning seed " <<
epee::string_tools::to_string_hex(context.m_pruning_seed) <<
- "), add_fail " << add_fail << ", flush_all_spans " << flush_all_spans);
+ "), score " << score << ", flush_all_spans " << flush_all_spans);
- if (add_fail)
- m_p2p->add_host_fail(context.m_remote_address);
+ if (score > 0)
+ m_p2p->add_host_fail(context.m_remote_address, score);
m_block_queue.flush_spans(context.m_connection_id, flush_all_spans);
@@ -2325,6 +2444,12 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
+ void t_cryptonote_protocol_handler<t_core>::drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans)
+ {
+ return drop_connection_with_score(context, add_fail ? 1 : 0, flush_all_spans);
+ }
+ //------------------------------------------------------------------------------------------------------------------------
+ template<class t_core>
void t_cryptonote_protocol_handler<t_core>::on_connection_close(cryptonote_connection_context &context)
{
uint64_t target = 0;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
index a67178c52..978a9ebf3 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h
@@ -41,7 +41,7 @@ namespace cryptonote
struct i_cryptonote_protocol
{
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0;
- virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)=0;
+ virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone)=0;
//virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0;
};
@@ -54,7 +54,7 @@ namespace cryptonote
{
return false;
}
- virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)
+ virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, const boost::uuids::uuid& source, epee::net_utils::zone zone)
{
return false;
}
diff --git a/src/cryptonote_protocol/enums.h b/src/cryptonote_protocol/enums.h
new file mode 100644
index 000000000..2ec622d94
--- /dev/null
+++ b/src/cryptonote_protocol/enums.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2019, 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 <cstdint>
+
+namespace cryptonote
+{
+ //! Methods tracking how a tx was received and relayed
+ enum class relay_method : std::uint8_t
+ {
+ none = 0, //!< Received via RPC with `do_not_relay` set
+ local, //!< Received via RPC; trying to send over i2p/tor, etc.
+ block, //!< Received in block, takes precedence over others
+ fluff //!< Received/sent over public networks
+ };
+}
diff --git a/src/cryptonote_protocol/fwd.h b/src/cryptonote_protocol/fwd.h
new file mode 100644
index 000000000..616b48be3
--- /dev/null
+++ b/src/cryptonote_protocol/fwd.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+namespace cryptonote
+{
+ class core;
+ struct cryptonote_connection_context;
+ struct i_core_events;
+}
+
diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp
index 26cd93b5a..a0a4bbbb1 100644
--- a/src/cryptonote_protocol/levin_notify.cpp
+++ b/src/cryptonote_protocol/levin_notify.cpp
@@ -33,6 +33,7 @@
#include <chrono>
#include <deque>
#include <stdexcept>
+#include <utility>
#include "common/expect.h"
#include "common/varint.h"
@@ -57,6 +58,37 @@ namespace levin
constexpr const std::chrono::seconds noise_min_delay{CRYPTONOTE_NOISE_MIN_DELAY};
constexpr const std::chrono::seconds noise_delay_range{CRYPTONOTE_NOISE_DELAY_RANGE};
+ /* A custom duration is used for the poisson distribution because of the
+ variance. If 5 seconds is given to `std::poisson_distribution`, 95% of
+ the values fall between 1-9s in 1s increments (not granular enough). If
+ 5000 milliseconds is given, 95% of the values fall between 4859ms-5141ms
+ in 1ms increments (not enough time variance). Providing 20 quarter
+ seconds yields 95% of the values between 3s-7.25s in 1/4s increments. */
+ using fluff_stepsize = std::chrono::duration<std::chrono::milliseconds::rep, std::ratio<1, 4>>;
+ constexpr const std::chrono::seconds fluff_average_in{CRYPTONOTE_DANDELIONPP_FLUSH_AVERAGE};
+
+ /*! Bitcoin Core is using 1/2 average seconds for outgoing connections
+ compared to incoming. The thinking is that the user controls outgoing
+ connections (Dandelion++ makes similar assumptions in its stem
+ algorithm). The randomization yields 95% values between 1s-4s in
+ 1/4s increments. */
+ constexpr const fluff_stepsize fluff_average_out{fluff_stepsize{fluff_average_in} / 2};
+
+ class random_poisson
+ {
+ std::poisson_distribution<fluff_stepsize::rep> dist;
+ public:
+ explicit random_poisson(fluff_stepsize average)
+ : dist(average.count() < 0 ? 0 : average.count())
+ {}
+
+ fluff_stepsize operator()()
+ {
+ crypto::random_device rand{};
+ return fluff_stepsize{dist(rand)};
+ }
+ };
+
/*! Select a randomized duration from 0 to `range`. The precision will be to
the systems `steady_clock`. As an example, supplying 3 seconds to this
function will select a duration from [0, 3] seconds, and the increments
@@ -129,6 +161,12 @@ namespace levin
return fullBlob;
}
+ bool make_payload_send_txs(connections& p2p, std::vector<blobdata>&& txs, const boost::uuids::uuid& destination, const bool pad)
+ {
+ const cryptonote::blobdata blob = make_tx_payload(std::move(txs), pad);
+ return p2p.notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan<std::uint8_t>(blob), destination);
+ }
+
/* The current design uses `asio::strand`s. The documentation isn't as clear
as it should be - a `strand` has an internal `mutex` and `bool`. The
`mutex` synchronizes thread access and the `bool` is set when a thread is
@@ -187,14 +225,18 @@ namespace levin
{
struct zone
{
- explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in)
+ explicit zone(boost::asio::io_service& io_service, std::shared_ptr<connections> p2p, epee::byte_slice noise_in, bool is_public, bool pad_txs)
: p2p(std::move(p2p)),
noise(std::move(noise_in)),
next_epoch(io_service),
+ flush_txs(io_service),
strand(io_service),
map(),
channels(),
- connection_count(0)
+ flush_time(std::chrono::steady_clock::time_point::max()),
+ connection_count(0),
+ is_public(is_public),
+ pad_txs(pad_txs)
{
for (std::size_t count = 0; !noise.empty() && count < CRYPTONOTE_NOISE_CHANNELS; ++count)
channels.emplace_back(io_service);
@@ -203,10 +245,14 @@ namespace levin
const std::shared_ptr<connections> p2p;
const epee::byte_slice noise; //!< `!empty()` means zone is using noise channels
boost::asio::steady_timer next_epoch;
+ boost::asio::steady_timer flush_txs;
boost::asio::io_service::strand strand;
net::dandelionpp::connection_map map;//!< Tracks outgoing uuid's for noise channels or Dandelion++ stems
std::deque<noise_channel> channels; //!< Never touch after init; only update elements on `noise_channel.strand`
+ std::chrono::steady_clock::time_point flush_time; //!< Next expected Dandelion++ fluff flush
std::atomic<std::size_t> connection_count; //!< Only update in strand, can be read at any time
+ const bool is_public; //!< Zone is public ipv4/ipv6 connections
+ const bool pad_txs; //!< Pad txs to the next boundary for privacy
};
} // detail
@@ -243,46 +289,112 @@ namespace levin
}
};
- //! Sends a message to every active connection
- class flood_notify
+ //! Sends txs on connections with expired timers, and queues callback for next timer expiration (if any).
+ struct fluff_flush
{
std::shared_ptr<detail::zone> zone_;
- epee::byte_slice message_; // Requires manual copy
- boost::uuids::uuid source_;
+ std::chrono::steady_clock::time_point flush_time_;
- public:
- explicit flood_notify(std::shared_ptr<detail::zone> zone, epee::byte_slice message, const boost::uuids::uuid& source)
- : zone_(std::move(zone)), message_(message.clone()), source_(source)
- {}
+ static void queue(std::shared_ptr<detail::zone> zone, const std::chrono::steady_clock::time_point flush_time)
+ {
+ assert(zone != nullptr);
+ assert(zone->strand.running_in_this_thread());
- flood_notify(flood_notify&&) = default;
- flood_notify(const flood_notify& source)
- : zone_(source.zone_), message_(source.message_.clone()), source_(source.source_)
- {}
+ detail::zone& this_zone = *zone;
+ this_zone.flush_time = flush_time;
+ this_zone.flush_txs.expires_at(flush_time);
+ this_zone.flush_txs.async_wait(this_zone.strand.wrap(fluff_flush{std::move(zone), flush_time}));
+ }
- void operator()() const
+ void operator()(const boost::system::error_code error)
{
if (!zone_ || !zone_->p2p)
return;
assert(zone_->strand.running_in_this_thread());
- /* The foreach should be quick, but then it iterates and acquires the
- same lock for every connection. So do in a strand because two threads
- will ping-pong each other with cacheline invalidations. Revisit if
- algorithm changes or the locking strategy within the levin config
- class changes. */
-
- std::vector<boost::uuids::uuid> connections;
- connections.reserve(connection_id_reserve_size);
- zone_->p2p->foreach_connection([this, &connections] (detail::p2p_context& context) {
- if (this->source_ != context.m_connection_id)
- connections.emplace_back(context.m_connection_id);
+ const bool timer_error = bool(error);
+ if (timer_error)
+ {
+ if (error != boost::system::errc::operation_canceled)
+ throw boost::system::system_error{error, "fluff_flush timer failed"};
+
+ // new timer canceled this one set in future
+ if (zone_->flush_time < flush_time_)
+ return;
+ }
+
+ const auto now = std::chrono::steady_clock::now();
+ auto next_flush = std::chrono::steady_clock::time_point::max();
+ std::vector<std::pair<std::vector<blobdata>, boost::uuids::uuid>> connections{};
+ zone_->p2p->foreach_connection([timer_error, now, &next_flush, &connections] (detail::p2p_context& context)
+ {
+ if (!context.fluff_txs.empty())
+ {
+ if (context.flush_time <= now || timer_error) // flush on canceled timer
+ {
+ context.flush_time = std::chrono::steady_clock::time_point::max();
+ connections.emplace_back(std::move(context.fluff_txs), context.m_connection_id);
+ context.fluff_txs.clear();
+ }
+ else // not flushing yet
+ next_flush = std::min(next_flush, context.flush_time);
+ }
+ else // nothing to flush
+ context.flush_time = std::chrono::steady_clock::time_point::max();
return true;
});
- for (const boost::uuids::uuid& connection : connections)
- zone_->p2p->send(message_.clone(), connection);
+ for (auto& connection : connections)
+ make_payload_send_txs(*zone_->p2p, std::move(connection.first), connection.second, zone_->pad_txs);
+
+ if (next_flush != std::chrono::steady_clock::time_point::max())
+ fluff_flush::queue(std::move(zone_), next_flush);
+ else
+ zone_->flush_time = next_flush; // signal that no timer is set
+ }
+ };
+
+ /*! The "fluff" portion of the Dandelion++ algorithm. Every tx is queued
+ per-connection and flushed with a randomized poisson timer. This
+ implementation only has one system timer per-zone, and instead tracks
+ the lowest flush time. */
+ struct fluff_notify
+ {
+ std::shared_ptr<detail::zone> zone_;
+ std::vector<blobdata> txs_;
+ boost::uuids::uuid source_;
+
+ void operator()()
+ {
+ if (!zone_ || !zone_->p2p || txs_.empty())
+ return;
+
+ assert(zone_->strand.running_in_this_thread());
+
+ const auto now = std::chrono::steady_clock::now();
+ auto next_flush = std::chrono::steady_clock::time_point::max();
+
+ random_poisson in_duration(fluff_average_in);
+ random_poisson out_duration(fluff_average_out);
+
+ zone_->p2p->foreach_connection([this, now, &in_duration, &out_duration, &next_flush] (detail::p2p_context& context)
+ {
+ if (this->source_ != context.m_connection_id && (this->zone_->is_public || !context.m_is_income))
+ {
+ if (context.fluff_txs.empty())
+ context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());
+
+ next_flush = std::min(next_flush, context.flush_time);
+ context.fluff_txs.reserve(context.fluff_txs.size() + this->txs_.size());
+ for (const blobdata& tx : this->txs_)
+ context.fluff_txs.push_back(tx); // must copy instead of move (multiple conns)
+ }
+ return true;
+ });
+
+ if (next_flush < zone_->flush_time)
+ fluff_flush::queue(std::move(zone_), next_flush);
}
};
@@ -446,7 +558,7 @@ namespace levin
}
};
- //! Prepares connections for new channel epoch and sets timer for next epoch
+ //! Prepares connections for new channel/dandelionpp epoch and sets timer for next epoch
struct start_epoch
{
// Variables allow for Dandelion++ extension
@@ -476,8 +588,8 @@ namespace levin
};
} // anonymous
- notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise)
- : zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise)))
+ notify::notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, const bool is_public, const bool pad_txs)
+ : zone_(std::make_shared<detail::zone>(service, std::move(p2p), std::move(noise), is_public, pad_txs))
{
if (!zone_->p2p)
throw std::logic_error{"cryptonote::levin::notify cannot have nullptr p2p argument"};
@@ -528,9 +640,19 @@ namespace levin
channel.next_noise.cancel();
}
- bool notify::send_txs(std::vector<cryptonote::blobdata> txs, const boost::uuids::uuid& source, const bool pad_txs)
+ void notify::run_fluff()
{
if (!zone_)
+ return;
+ zone_->flush_txs.cancel();
+ }
+
+ bool notify::send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source)
+ {
+ if (txs.empty())
+ return true;
+
+ if (!zone_)
return false;
if (!zone_->noise.empty() && !zone_->channels.empty())
@@ -560,12 +682,7 @@ namespace levin
}
else
{
- const std::string payload = make_tx_payload(std::move(txs), pad_txs);
- epee::byte_slice message =
- epee::levin::make_notify(NOTIFY_NEW_TRANSACTIONS::ID, epee::strspan<std::uint8_t>(payload));
-
- // traditional monero send technique
- zone_->strand.dispatch(flood_notify{zone_, std::move(message), source});
+ zone_->strand.dispatch(fluff_notify{zone_, std::move(txs), source});
}
return true;
diff --git a/src/cryptonote_protocol/levin_notify.h b/src/cryptonote_protocol/levin_notify.h
index 82d22680a..ce652d933 100644
--- a/src/cryptonote_protocol/levin_notify.h
+++ b/src/cryptonote_protocol/levin_notify.h
@@ -35,6 +35,7 @@
#include "byte_slice.h"
#include "cryptonote_basic/blobdatatype.h"
+#include "cryptonote_protocol/fwd.h"
#include "net/enums.h"
#include "span.h"
@@ -53,11 +54,6 @@ namespace nodetool
namespace cryptonote
{
- struct cryptonote_connection_context;
-}
-
-namespace cryptonote
-{
namespace levin
{
namespace detail
@@ -86,7 +82,7 @@ namespace levin
{}
//! Construct an instance with available notification `zones`.
- explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise);
+ explicit notify(boost::asio::io_service& service, std::shared_ptr<connections> p2p, epee::byte_slice noise, bool is_public, bool pad_txs);
notify(const notify&) = delete;
notify(notify&&) = default;
@@ -108,11 +104,14 @@ namespace levin
//! Run the logic for the next stem timeout imemdiately. Only use in testing.
void run_stems();
+ //! Run the logic for flushing all Dandelion++ fluff queued txs. Only use in testing.
+ void run_fluff();
+
/*! Send txs using `cryptonote_protocol_defs.h` payload format wrapped in a
levin header. The message will be sent in a "discreet" manner if
enabled - if `!noise.empty()` then the `command`/`payload` will be
queued to send at the next available noise interval. Otherwise, a
- standard Monero flood notification will be used.
+ Dandelion++ fluff algorithm will be used.
\note Eventually Dandelion++ stem sending will be used here when
enabled.
@@ -121,12 +120,9 @@ namespace levin
\param source The source of the notification. `is_nil()` indicates this
node is the source. Dandelion++ will use this to map a source to a
particular stem.
- \param pad_txs A request to pad txs to help conceal origin via
- statistical analysis. Ignored if noise was enabled during
- construction.
\return True iff the notification is queued for sending. */
- bool send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source, bool pad_txs);
+ bool send_txs(std::vector<blobdata> txs, const boost::uuids::uuid& source);
};
} // levin
} // net
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index d47823735..99a460c78 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -28,7 +28,6 @@
#include "common/dns_utils.h"
#include "common/command_line.h"
-#include "version.h"
#include "daemon/command_parser_executor.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -794,10 +793,16 @@ bool t_command_parser_executor::pop_blocks(const std::vector<std::string>& args)
return false;
}
+bool t_command_parser_executor::rpc_payments(const std::vector<std::string>& args)
+{
+ if (args.size() != 0) return false;
+
+ return m_executor.rpc_payments();
+}
+
bool t_command_parser_executor::version(const std::vector<std::string>& args)
{
- std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl;
- return true;
+ return m_executor.version();
}
bool t_command_parser_executor::prune_blockchain(const std::vector<std::string>& args)
@@ -837,4 +842,16 @@ bool t_command_parser_executor::set_bootstrap_daemon(const std::vector<std::stri
args_count > 2 ? args[2] : std::string());
}
+bool t_command_parser_executor::flush_cache(const std::vector<std::string>& args)
+{
+ if (args.empty())
+ goto show_list;
+ if (args[0] == "bad-txs")
+ return m_executor.flush_cache(true);
+
+show_list:
+ std::cout << "Cache type needed: bad-txs" << std::endl;
+ return true;
+}
+
} // namespace daemonize
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 25587dea8..8b85dcf69 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -143,6 +143,8 @@ public:
bool pop_blocks(const std::vector<std::string>& args);
+ bool rpc_payments(const std::vector<std::string>& args);
+
bool version(const std::vector<std::string>& args);
bool prune_blockchain(const std::vector<std::string>& args);
@@ -152,6 +154,8 @@ public:
bool print_net_stats(const std::vector<std::string>& args);
bool set_bootstrap_daemon(const std::vector<std::string>& args);
+
+ bool flush_cache(const std::vector<std::string>& args);
};
} // namespace daemonize
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index 757e072a4..8ec690631 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -65,7 +65,7 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"print_pl"
, std::bind(&t_command_parser_executor::print_peer_list, &m_parser, p::_1)
- , "print_pl [white] [gray] [<limit>]"
+ , "print_pl [white] [gray] [pruned] [publicrpc] [<limit>]"
, "Print the current peer list."
);
m_command_lookup.set_handler(
@@ -296,6 +296,11 @@ t_command_server::t_command_server(
, "Remove blocks from end of blockchain"
);
m_command_lookup.set_handler(
+ "rpc_payments"
+ , std::bind(&t_command_parser_executor::rpc_payments, &m_parser, p::_1)
+ , "Print information about RPC payments."
+ );
+ m_command_lookup.set_handler(
"version"
, std::bind(&t_command_parser_executor::version, &m_parser, p::_1)
, "Print version information."
@@ -317,6 +322,12 @@ t_command_server::t_command_server(
, "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced.\n"
"Use 'auto' to enable automatic public nodes discovering and bootstrap daemon switching"
);
+ m_command_lookup.set_handler(
+ "flush_cache"
+ , std::bind(&t_command_parser_executor::flush_cache, &m_parser, p::_1)
+ , "flush_cache bad-txs"
+ , "Flush the specified cache(s)."
+ );
}
bool t_command_server::process_command_str(const std::string& cmd)
diff --git a/src/daemon/core.h b/src/daemon/core.h
index 91dbb7a4b..9a3579e20 100644
--- a/src/daemon/core.h
+++ b/src/daemon/core.h
@@ -59,16 +59,6 @@ public:
: m_core{nullptr}
, m_vm_HACK{vm}
{
- }
-
- // TODO - get rid of circular dependencies in internals
- void set_protocol(t_protocol_raw & protocol)
- {
- m_core.set_cryptonote_protocol(&protocol);
- }
-
- bool run()
- {
//initialize core here
MGINFO("Initializing core...");
#if defined(PER_BLOCK_CHECKPOINT)
@@ -78,9 +68,19 @@ public:
#endif
if (!m_core.init(m_vm_HACK, nullptr, get_checkpoints))
{
- return false;
+ throw std::runtime_error("Failed to initialize core");
}
MGINFO("Core initialized OK");
+ }
+
+ // TODO - get rid of circular dependencies in internals
+ void set_protocol(t_protocol_raw & protocol)
+ {
+ m_core.set_cryptonote_protocol(&protocol);
+ }
+
+ bool run()
+ {
return true;
}
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index cb288071e..8fa983fe5 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -44,7 +44,6 @@
#include "rpc/core_rpc_server.h"
#include "rpc/rpc_args.h"
#include "daemon/command_line_args.h"
-#include "blockchain_db/db_types.h"
#include "version.h"
#ifdef STACK_TRACE
@@ -224,16 +223,6 @@ int main(int argc, char const * argv[])
return 1;
}
- std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
-
- // verify that blockchaindb type is valid
- if(!cryptonote::blockchain_valid_db_type(db_type))
- {
- std::cout << "Invalid database type (" << db_type << "), available types are: " <<
- cryptonote::blockchain_db_types(", ") << std::endl;
- return 0;
- }
-
// data_dir
// default: e.g. ~/.bitmonero/ or ~/.bitmonero/testnet
// if data-dir argument given:
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index d4b9434da..ef9fed177 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -37,6 +37,8 @@
#include "cryptonote_core/cryptonote_core.h"
#include "cryptonote_basic/difficulty.h"
#include "cryptonote_basic/hardfork.h"
+#include "rpc/rpc_payment_signature.h"
+#include "rpc/rpc_version_str.h"
#include <boost/format.hpp>
#include <ctime>
#include <string>
@@ -60,6 +62,13 @@ namespace {
}
}
+ std::string print_float(float f, int prec)
+ {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%*.*f", prec, prec, f);
+ return buf;
+ }
+
void print_peer(std::string const & prefix, cryptonote::peer const & peer, bool pruned_only, bool publicrpc_only)
{
if (pruned_only && peer.pruning_seed == 0)
@@ -77,8 +86,9 @@ namespace {
epee::string_tools::xtype_to_string(peer.port, port_str);
std::string addr_str = peer.host + ":" + port_str;
std::string rpc_port = peer.rpc_port ? std::to_string(peer.rpc_port) : "-";
+ std::string rpc_credits_per_hash = peer.rpc_credits_per_hash ? print_float(peer.rpc_credits_per_hash / RPC_CREDITS_PER_HASH_SCALE, 2) : "-";
std::string pruning_seed = epee::string_tools::to_string_hex(peer.pruning_seed);
- tools::msg_writer() << boost::format("%-10s %-25s %-25s %-5s %-4s %s") % prefix % id_str % addr_str % rpc_port % pruning_seed % elapsed;
+ tools::msg_writer() << boost::format("%-10s %-25s %-25s %-5s %-5s %-4s %s") % prefix % id_str % addr_str % rpc_port % rpc_credits_per_hash % pruning_seed % elapsed;
}
void print_block_header(cryptonote::block_header_response const & header)
@@ -91,13 +101,15 @@ namespace {
<< "height: " << boost::lexical_cast<std::string>(header.height) << std::endl
<< "depth: " << boost::lexical_cast<std::string>(header.depth) << std::endl
<< "hash: " << header.hash << std::endl
- << "difficulty: " << header.wide_difficulty << std::endl
+ << "difficulty: " << cryptonote::difficulty_type(header.wide_difficulty) << std::endl
+ << "cumulative difficulty: " << cryptonote::difficulty_type(header.wide_cumulative_difficulty) << std::endl
<< "POW hash: " << header.pow_hash << std::endl
<< "block size: " << header.block_size << std::endl
<< "block weight: " << header.block_weight << std::endl
<< "long term weight: " << header.long_term_weight << std::endl
<< "num txes: " << header.num_txes << std::endl
- << "reward: " << cryptonote::print_money(header.reward);
+ << "reward: " << cryptonote::print_money(header.reward) << std::endl
+ << "miner tx hash: " << header.miner_tx_hash;
}
std::string get_human_time_ago(time_t t, time_t now)
@@ -355,8 +367,8 @@ bool t_rpc_command_executor::show_difficulty() {
tools::success_msg_writer() << "BH: " << res.height
<< ", TH: " << res.top_block_hash
- << ", DIFF: " << res.wide_difficulty
- << ", CUM_DIFF: " << res.wide_cumulative_difficulty
+ << ", DIFF: " << cryptonote::difficulty_type(res.wide_difficulty)
+ << ", CUM_DIFF: " << cryptonote::difficulty_type(res.wide_cumulative_difficulty)
<< ", HR: " << cryptonote::difficulty_type(res.wide_difficulty) / res.target << " H/s";
return true;
@@ -369,7 +381,7 @@ static void get_metric_prefix(cryptonote::difficulty_type hr, double& hr_d, char
prefix = 0;
return;
}
- static const char metric_prefixes[4] = { 'k', 'M', 'G', 'T' };
+ static const char metric_prefixes[] = { 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' };
for (size_t i = 0; i < sizeof(metric_prefixes); ++i)
{
if (hr < 1000000)
@@ -578,9 +590,9 @@ bool t_rpc_command_executor::mining_status() {
tools::msg_writer() << "Mining at " << get_mining_speed(mres.speed) << " with " << mres.threads_count << " threads";
}
+ tools::msg_writer() << "PoW algorithm: " << mres.pow_algorithm;
if (mres.active || mres.is_background_mining_enabled)
{
- tools::msg_writer() << "PoW algorithm: " << mres.pow_algorithm;
tools::msg_writer() << "Mining address: " << mres.address;
}
@@ -769,7 +781,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
<< ", size: " << header.block_size << ", weight: " << header.block_weight << " (long term " << header.long_term_weight << "), transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
- << "difficulty: " << header.wide_difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
+ << "difficulty: " << cryptonote::difficulty_type(header.wide_difficulty) << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
first = false;
}
@@ -960,10 +972,11 @@ bool t_rpc_command_executor::print_transaction(crypto::hash transaction_hash,
if (1 == res.txs.size())
{
// only available for new style answers
+ bool pruned = res.txs.front().prunable_as_hex.empty() && res.txs.front().prunable_hash != epee::string_tools::pod_to_hex(crypto::null_hash);
if (res.txs.front().in_pool)
tools::success_msg_writer() << "Found in pool";
else
- tools::success_msg_writer() << "Found in blockchain at height " << res.txs.front().block_height << (res.txs.front().prunable_as_hex.empty() ? " (pruned)" : "");
+ tools::success_msg_writer() << "Found in blockchain at height " << res.txs.front().block_height << (pruned ? " (pruned)" : "");
}
const std::string &as_hex = (1 == res.txs.size()) ? res.txs.front().as_hex : res.txs_as_hex.front();
@@ -1901,7 +1914,7 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip, size_t above
const auto &chain = chains[idx];
const 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.wide_difficulty << ": " << chain.block_hash;
+ << " deep), diff " << cryptonote::difficulty_type(chain.wide_difficulty) << ": " << chain.block_hash;
}
}
else
@@ -1914,7 +1927,7 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip, size_t above
tools::success_msg_writer() << "Found alternate chain with tip " << tip;
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.wide_difficulty << ":";
+ << " deep), diff " << cryptonote::difficulty_type(chain.wide_difficulty) << ":";
for (const std::string &block_id: chain.block_hashes)
tools::msg_writer() << " " << block_id;
tools::msg_writer() << "Chain parent on main chain: " << chain.main_chain_parent_block;
@@ -2018,7 +2031,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
}
}
- tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.wide_difficulty << ", cum. diff " << ires.wide_cumulative_difficulty
+ tools::msg_writer() << "Height: " << ires.height << ", diff " << cryptonote::difficulty_type(ires.wide_difficulty) << ", cum. diff " << cryptonote::difficulty_type(ires.wide_cumulative_difficulty)
<< ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/" << (hfres.enabled ? "byte" : "kB");
if (nblocks > 0)
@@ -2361,4 +2374,108 @@ bool t_rpc_command_executor::set_bootstrap_daemon(
return true;
}
+bool t_rpc_command_executor::flush_cache(bool bad_txs)
+{
+ cryptonote::COMMAND_RPC_FLUSH_CACHE::request req;
+ cryptonote::COMMAND_RPC_FLUSH_CACHE::response res;
+ std::string fail_message = "Unsuccessful";
+ epee::json_rpc::error error_resp;
+
+ req.bad_txs = bad_txs;
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(req, res, "flush_cache", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_flush_cache(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+
+ return true;
+}
+
+bool t_rpc_command_executor::rpc_payments()
+{
+ cryptonote::COMMAND_RPC_ACCESS_DATA::request req;
+ cryptonote::COMMAND_RPC_ACCESS_DATA::response res;
+ std::string fail_message = "Unsuccessful";
+ epee::json_rpc::error error_resp;
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(req, res, "rpc_access_data", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_rpc_access_data(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+
+ const uint64_t now = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+ uint64_t balance = 0;
+ tools::msg_writer() << boost::format("%64s %16u %16u %8u %8u %8u %8u %s")
+ % "Client ID" % "Balance" % "Total mined" % "Good" % "Stale" % "Bad" % "Dupes" % "Last update";
+ for (const auto &entry: res.entries)
+ {
+ tools::msg_writer() << boost::format("%64s %16u %16u %8u %8u %8u %8u %s")
+ % entry.client % entry.balance % entry.credits_total
+ % entry.nonces_good % entry.nonces_stale % entry.nonces_bad % entry.nonces_dupe
+ % (entry.last_update_time == 0 ? "never" : get_human_time_ago(entry.last_update_time, now).c_str());
+ balance += entry.balance;
+ }
+ tools::msg_writer() << res.entries.size() << " clients with a total of " << balance << " credits";
+ tools::msg_writer() << "Aggregated client hash rate: " << get_mining_speed(res.hashrate);
+
+ return true;
+}
+
+bool t_rpc_command_executor::version()
+{
+ cryptonote::COMMAND_RPC_GET_INFO::request req;
+ cryptonote::COMMAND_RPC_GET_INFO::response res;
+
+ const char *fail_message = "Problem fetching info";
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->rpc_request(req, res, "/getinfo", fail_message))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_info(req, res) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+
+ if (res.version.empty() || !cryptonote::rpc::is_version_string_valid(res.version))
+ {
+ tools::fail_msg_writer() << "The daemon software version is not available.";
+ }
+ else
+ {
+ tools::success_msg_writer() << res.version;
+ }
+
+ return true;
+}
+
}// namespace daemonize
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index af7081ef3..af55f0e22 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -163,10 +163,16 @@ public:
bool print_net_stats();
+ bool version();
+
bool set_bootstrap_daemon(
const std::string &address,
const std::string &username,
const std::string &password);
+
+ bool rpc_payments();
+
+ bool flush_cache(bool bad_txs);
};
} // namespace daemonize
diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp
index 5af4e1a4a..16758215d 100644
--- a/src/daemonizer/posix_fork.cpp
+++ b/src/daemonizer/posix_fork.cpp
@@ -127,13 +127,18 @@ void fork(const std::string & pidfile)
{
quit("Unable to open output file: " + output);
}
+#else
+ if (open("/dev/null", O_WRONLY) < 0)
+ {
+ quit("Unable to open /dev/null");
+ }
+#endif
// Also send standard error to the same log file.
if (dup(1) < 0)
{
quit("Unable to dup output descriptor");
}
-#endif
}
} // namespace posix
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index 15fe560d1..49f54e5a5 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -65,6 +65,41 @@ namespace hw {
#endif
/* ===================================================================== */
+ /* === hmacmap ==== */
+ /* ===================================================================== */
+
+
+ SecHMAC::SecHMAC(const uint8_t s[32], const uint8_t h[32]) {
+ memcpy(this->sec, s, 32);
+ memcpy(this->hmac, h, 32);
+ }
+
+ void HMACmap::find_mac(const uint8_t sec[32], uint8_t hmac[32]) {
+ size_t sz = hmacs.size();
+ log_hexbuffer("find_mac: lookup for ", (char*)sec,32);
+ for (size_t i=0; i<sz; i++) {
+ log_hexbuffer("find_mac: - try ",(char*)hmacs[i].sec,32);
+ if (memcmp(sec, hmacs[i].sec, 32) == 0) {
+ memcpy(hmac, hmacs[i].hmac, 32);
+ log_hexbuffer("find_mac: - found ",(char*)hmacs[i].hmac,32);
+ return;
+ }
+
+ }
+ throw std::runtime_error("Protocol error: try to send untrusted secret");
+ }
+
+ void HMACmap::add_mac(const uint8_t sec[32], const uint8_t hmac[32]) {
+ log_hexbuffer("add_mac: sec ", (char*)sec,32);
+ log_hexbuffer("add_mac: hmac ", (char*)hmac,32);
+ hmacs.push_back(SecHMAC(sec,hmac));
+ }
+
+ void HMACmap::clear() {
+ hmacs.clear();
+ }
+
+ /* ===================================================================== */
/* === Keymap ==== */
/* ===================================================================== */
@@ -162,9 +197,11 @@ namespace hw {
#define INS_RESET 0x02
#define INS_GET_KEY 0x20
+ #define INS_DISPLAY_ADDRESS 0x21
#define INS_PUT_KEY 0x22
#define INS_GET_CHACHA8_PREKEY 0x24
#define INS_VERIFY_KEY 0x26
+ #define INS_MANAGE_SEEDWORDS 0x28
#define INS_SECRET_KEY_TO_PUBLIC_KEY 0x30
#define INS_GEN_KEY_DERIVATION 0x32
@@ -205,6 +242,7 @@ namespace hw {
this->reset_buffer();
this->mode = NONE;
this->has_view_key = false;
+ this->tx_in_progress = false;
MDEBUG( "Device "<<this->id <<" Created");
}
@@ -317,6 +355,30 @@ namespace hw {
}
}
+ void device_ledger::send_secret(const unsigned char sec[32], int &offset) {
+ MDEBUG("send_secret: " << this->tx_in_progress);
+ ASSERT_X(offset + 32 <= BUFFER_SEND_SIZE, "send_secret: out of bounds write (secret)");
+ memmove(this->buffer_send+offset, sec, 32);
+ offset +=32;
+ if (this->tx_in_progress) {
+ ASSERT_X(offset + 32 <= BUFFER_SEND_SIZE, "send_secret: out of bounds write (mac)");
+ this->hmac_map.find_mac((uint8_t*)sec, this->buffer_send+offset);
+ offset += 32;
+ }
+ }
+
+ void device_ledger::receive_secret(unsigned char sec[32], int &offset) {
+ MDEBUG("receive_secret: " << this->tx_in_progress);
+ ASSERT_X(offset + 32 <= BUFFER_RECV_SIZE, "receive_secret: out of bounds read (secret)");
+ memmove(sec, this->buffer_recv+offset, 32);
+ offset += 32;
+ if (this->tx_in_progress) {
+ ASSERT_X(offset + 32 <= BUFFER_RECV_SIZE, "receive_secret: out of bounds read (mac)");
+ this->hmac_map.add_mac((uint8_t*)sec, this->buffer_recv+offset);
+ offset += 32;
+ }
+ }
+
bool device_ledger::reset() {
reset_buffer();
int offset = set_command_header_noopt(INS_RESET);
@@ -418,10 +480,10 @@ namespace hw {
#ifdef DEBUG_HWDEVICE
cryptonote::account_public_address pubkey;
this->get_public_address(pubkey);
+ #endif
crypto::secret_key vkey;
crypto::secret_key skey;
this->get_secret_keys(vkey,skey);
- #endif
return true;
}
@@ -509,6 +571,7 @@ namespace hw {
}
#ifdef DEBUG_HWDEVICE
+ send_simple(INS_GET_KEY, 0x04);
memmove(dbg_viewkey.data, this->buffer_recv+0, 32);
memmove(dbg_spendkey.data, this->buffer_recv+32, 32);
#endif
@@ -538,6 +601,27 @@ namespace hw {
return true;
}
+ void device_ledger::display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) {
+ AUTO_LOCK_CMD();
+
+ int offset = set_command_header_noopt(INS_DISPLAY_ADDRESS, payment_id?1:0);
+ //index
+ memmove(this->buffer_send+offset, &index, sizeof(cryptonote::subaddress_index));
+ offset +=8 ;
+
+ //payment ID
+ if (payment_id) {
+ memmove(this->buffer_send+offset, (*payment_id).data, 8);
+ } else {
+ memset(this->buffer_send+offset, 0, 8);
+ }
+ offset +=8;
+
+ this->buffer_send[4] = offset-5;
+ this->length_send = offset;
+ CHECK_AND_ASSERT_THROW_MES(this->exchange_wait_on_input() == 0, "Timeout/Error on display address.");
+ }
+
/* ======================================================================= */
/* SUB ADDRESS */
/* ======================================================================= */
@@ -573,8 +657,7 @@ namespace hw {
memmove(this->buffer_send+offset, pub.data, 32);
offset += 32;
//derivation
- memmove(this->buffer_send+offset, derivation.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)derivation.data, offset);
//index
this->buffer_send[offset+0] = output_index>>24;
this->buffer_send[offset+1] = output_index>>16;
@@ -706,8 +789,7 @@ namespace hw {
int offset = set_command_header_noopt(INS_GET_SUBADDRESS_SECRET_KEY);
//sec
- memmove(this->buffer_send+offset, sec.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)sec.data, offset);
//index
static_assert(sizeof(cryptonote::subaddress_index) == 8, "cryptonote::subaddress_index shall be 8 bytes length");
memmove(this->buffer_send+offset, &index, sizeof(cryptonote::subaddress_index));
@@ -717,7 +799,8 @@ namespace hw {
this->length_send = offset;
this->exchange();
- memmove(sub_sec.data, &this->buffer_recv[0], 32);
+ offset = 0;
+ this->receive_secret((unsigned char*)sub_sec.data, offset);
#ifdef DEBUG_HWDEVICE
crypto::secret_key sub_sec_clear = hw::ledger::decrypt(sub_sec);
@@ -737,8 +820,7 @@ namespace hw {
offset = set_command_header_noopt(INS_VERIFY_KEY);
//sec
- memmove(this->buffer_send+offset, secret_key.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)secret_key.data, offset);
//pub
memmove(this->buffer_send+offset, public_key.data, 32);
offset += 32;
@@ -774,9 +856,7 @@ namespace hw {
memmove(this->buffer_send+offset, P.bytes, 32);
offset += 32;
//sec
- memmove(this->buffer_send+offset, a.bytes, 32);
- offset += 32;
-
+ this->send_secret(a.bytes, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -805,8 +885,7 @@ namespace hw {
int offset = set_command_header_noopt(INS_SECRET_SCAL_MUL_BASE);
//sec
- memmove(this->buffer_send+offset, a.bytes, 32);
- offset += 32;
+ this->send_secret(a.bytes, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -824,6 +903,7 @@ namespace hw {
bool device_ledger::sc_secret_add( crypto::secret_key &r, const crypto::secret_key &a, const crypto::secret_key &b) {
AUTO_LOCK_CMD();
+ int offset;
#ifdef DEBUG_HWDEVICE
const crypto::secret_key a_x = hw::ledger::decrypt(a);
@@ -836,20 +916,19 @@ namespace hw {
log_hexbuffer("sc_secret_add: [[OUT]] aG", (char*)r_x.data, 32);
#endif
- int offset = set_command_header_noopt(INS_SECRET_KEY_ADD);
+ offset = set_command_header_noopt(INS_SECRET_KEY_ADD);
//sec key
- memmove(this->buffer_send+offset, a.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)a.data, offset);
//sec key
- memmove(this->buffer_send+offset, b.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)b.data, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
this->exchange();
- //pub key
- memmove(r.data, &this->buffer_recv[0], 32);
+ //sec key
+ offset = 0;
+ this->receive_secret((unsigned char*)r.data, offset);
#ifdef DEBUG_HWDEVICE
crypto::secret_key r_clear = hw::ledger::decrypt(r);
@@ -861,6 +940,8 @@ namespace hw {
crypto::secret_key device_ledger::generate_keys(crypto::public_key &pub, crypto::secret_key &sec, const crypto::secret_key& recovery_key, bool recover) {
AUTO_LOCK_CMD();
+ int offset;
+
if (recover) {
throw std::runtime_error("device generate key does not support recover");
}
@@ -877,9 +958,11 @@ namespace hw {
send_simple(INS_GENERATE_KEYPAIR);
+ offset = 0;
//pub key
memmove(pub.data, &this->buffer_recv[0], 32);
- memmove(sec.data, &this->buffer_recv[32], 32);
+ offset += 32;
+ this->receive_secret((unsigned char*)sec.data, offset);
#ifdef DEBUG_HWDEVICE
crypto::secret_key sec_clear = hw::ledger::decrypt(sec);
@@ -922,15 +1005,16 @@ namespace hw {
memmove(this->buffer_send+offset, pub.data, 32);
offset += 32;
//sec
- memmove(this->buffer_send+offset, sec.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)sec.data, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
this->exchange();
+ offset = 0;
//derivattion data
- memmove(derivation.data, &this->buffer_recv[0], 32);
+ this->receive_secret((unsigned char*)derivation.data, offset);
+
r = true;
}
#ifdef DEBUG_HWDEVICE
@@ -978,9 +1062,9 @@ namespace hw {
#endif
int offset = set_command_header_noopt(INS_DERIVATION_TO_SCALAR);
- //derivattion
- memmove(this->buffer_send+offset, derivation.data, 32);
- offset += 32;
+ //derivation
+ this->send_secret((unsigned char*)derivation.data, offset);
+
//index
this->buffer_send[offset+0] = output_index>>24;
this->buffer_send[offset+1] = output_index>>16;
@@ -992,8 +1076,9 @@ namespace hw {
this->length_send = offset;
this->exchange();
- //derivattion data
- memmove(res.data, &this->buffer_recv[0], 32);
+ //derivation data
+ offset = 0;
+ this->receive_secret((unsigned char*)res.data, offset);
#ifdef DEBUG_HWDEVICE
crypto::ec_scalar res_clear = hw::ledger::decrypt(res);
@@ -1020,8 +1105,7 @@ namespace hw {
int offset = set_command_header_noopt(INS_DERIVE_SECRET_KEY);
//derivation
- memmove(this->buffer_send+offset, derivation.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)derivation.data, offset);
//index
this->buffer_send[offset+0] = output_index>>24;
this->buffer_send[offset+1] = output_index>>16;
@@ -1029,15 +1113,15 @@ namespace hw {
this->buffer_send[offset+3] = output_index>>0;
offset += 4;
//sec
- memmove(this->buffer_send+offset, sec.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)sec.data, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
this->exchange();
- //pub key
- memmove(derived_sec.data, &this->buffer_recv[0], 32);
+ offset = 0;
+ //sec key
+ this->receive_secret((unsigned char*)derived_sec.data, offset);
#ifdef DEBUG_HWDEVICE
crypto::secret_key derived_sec_clear = hw::ledger::decrypt(derived_sec);
@@ -1064,8 +1148,7 @@ namespace hw {
int offset = set_command_header_noopt(INS_DERIVE_PUBLIC_KEY);
//derivation
- memmove(this->buffer_send+offset, derivation.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)derivation.data, offset);
//index
this->buffer_send[offset+0] = output_index>>24;
this->buffer_send[offset+1] = output_index>>16;
@@ -1106,8 +1189,7 @@ namespace hw {
int offset = set_command_header_noopt(INS_SECRET_KEY_TO_PUBLIC_KEY);
//sec key
- memmove(this->buffer_send+offset, sec.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)sec.data, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -1141,8 +1223,7 @@ namespace hw {
memmove(this->buffer_send+offset, pub.data, 32);
offset += 32;
//sec
- memmove(this->buffer_send+offset, sec.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)sec.data, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -1211,8 +1292,7 @@ namespace hw {
memmove(&this->buffer_send[offset], D.data, 32);
offset += 32;
// r
- memmove(&this->buffer_send[offset], r.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)r.data, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -1225,6 +1305,7 @@ namespace hw {
log_hexbuffer("GENERATE_TX_PROOF: **r** ", sig.r.data, sizeof( sig.r.data));
this->controle_device->generate_tx_proof(prefix_hash_x, R_x, A_x, B_x, D_x, r_x, sig_x);
+ MDEBUG("FAIL is normal if random is not fixed in proof");
hw::ledger::check32("generate_tx_proof", "c", sig_x.c.data, sig.c.data);
hw::ledger::check32("generate_tx_proof", "r", sig_x.r.data, sig.r.data);
@@ -1233,8 +1314,10 @@ namespace hw {
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
AUTO_LOCK_CMD();
-
+ this->lock();
key_map.clear();
+ hmac_map.clear();
+ this->tx_in_progress = true;
int offset = set_command_header_noopt(INS_OPEN_TX, 0x01);
//account
@@ -1248,7 +1331,13 @@ namespace hw {
this->length_send = offset;
this->exchange();
- memmove(tx_key.data, &this->buffer_recv[32], 32);
+ //skip R, receive: r, r_hmac, fake_a, a_hmac, fake_b, hmac_b
+ unsigned char tmp[32];
+ offset = 32;
+ this->receive_secret((unsigned char*)tx_key.data, offset);
+ this->receive_secret(tmp, offset);
+ this->receive_secret(tmp, offset);
+
#ifdef DEBUG_HWDEVICE
const crypto::secret_key r_x = hw::ledger::decrypt(tx_key);
log_hexbuffer("open_tx: [[OUT]] R ", (char*)&this->buffer_recv[0], 32);
@@ -1276,8 +1365,7 @@ namespace hw {
memmove(&this->buffer_send[offset], public_key.data, 32);
offset += 32;
//sec
- memmove(&this->buffer_send[offset], secret_key.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)secret_key.data, offset);
//id
memmove(&this->buffer_send[offset], payment_id.data, 8);
offset += 8;
@@ -1365,8 +1453,7 @@ namespace hw {
this->buffer_send[offset+3] = tx_version>>0;
offset += 4;
//tx_key
- memmove(&this->buffer_send[offset], tx_key.data, 32);
- offset += 32;
+ this->send_secret((unsigned char*)tx_key.data, offset);
//txkey_pub
memmove(&this->buffer_send[offset], txkey_pub.data, 32);
offset += 32;
@@ -1394,11 +1481,11 @@ namespace hw {
offset++;
//additional_tx_key
if (need_additional_txkeys) {
- memmove(&this->buffer_send[offset], additional_txkey.sec.data, 32);
+ this->send_secret((unsigned char*)additional_txkey.sec.data, offset);
} else {
memset(&this->buffer_send[offset], 0, 32);
+ offset += 32;
}
- offset += 32;
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -1411,9 +1498,8 @@ namespace hw {
{
ASSERT_X(recv_len>=32, "Not enought data from device");
crypto::secret_key scalar1;
- memmove(scalar1.data, &this->buffer_recv[offset],32);
+ this->receive_secret((unsigned char*)scalar1.data, offset);
amount_keys.push_back(rct::sk2rct(scalar1));
- offset += 32;
recv_len -= 32;
}
ASSERT_X(recv_len>=32, "Not enought data from device");
@@ -1464,8 +1550,7 @@ namespace hw {
rct::key mask;
int offset = set_command_header_noopt(INS_GEN_COMMITMENT_MASK);
// AKout
- memmove(this->buffer_send+offset, AKout.bytes, 32);
- offset += 32;
+ this->send_secret(AKout.bytes, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -1494,8 +1579,7 @@ namespace hw {
this->buffer_send[offset] = short_amount?0x02:0x00;
offset += 1;
// AKout
- memmove(this->buffer_send+offset, AKout.bytes, 32);
- offset += 32;
+ this->send_secret(AKout.bytes, offset);
//mask k
memmove(this->buffer_send+offset, unmasked.mask.bytes, 32);
offset += 32;
@@ -1535,8 +1619,7 @@ namespace hw {
this->buffer_send[offset] = short_amount?0x02:0x00;
offset += 1;
// AKout
- memmove(this->buffer_send+offset, AKout.bytes, 32);
- offset += 32;
+ this->send_secret(AKout.bytes, offset);
//mask k
memmove(this->buffer_send+offset, masked.mask.bytes, 32);
offset += 32;
@@ -1649,26 +1732,21 @@ namespace hw {
this->buffer_send[offset] = (i==outputs_size-1)? 0x00:0x80 ;
this->buffer_send[offset] |= (type==rct::RCTTypeBulletproof2)?0x02:0x00;
offset += 1;
- if (found) {
- //is_subaddress
- this->buffer_send[offset] = outKeys.is_subaddress;
- offset++;
- //is_change_address
- this->buffer_send[offset] = outKeys.is_change_address;
- offset++;
- //Aout
- memmove(this->buffer_send+offset, outKeys.Aout.bytes, 32);
- offset+=32;
- //Bout
- memmove(this->buffer_send+offset, outKeys.Bout.bytes, 32);
- offset+=32;
- //AKout
- memmove(this->buffer_send+offset, outKeys.AKout.bytes, 32);
- offset+=32;
- } else {
- // dummy: is_subaddress Aout Bout AKout
- offset += 2+32*3;
- }
+ //is_subaddress
+ this->buffer_send[offset] = outKeys.is_subaddress;
+ offset++;
+ //is_change_address
+ this->buffer_send[offset] = outKeys.is_change_address;
+ offset++;
+ //Aout
+ memmove(this->buffer_send+offset, outKeys.Aout.bytes, 32);
+ offset+=32;
+ //Bout
+ memmove(this->buffer_send+offset, outKeys.Bout.bytes, 32);
+ offset+=32;
+ //AKout
+ this->send_secret(outKeys.AKout.bytes, offset);
+
//C
memmove(this->buffer_send+offset, data+C_offset,32);
offset += 32;
@@ -1760,17 +1838,19 @@ namespace hw {
memmove(this->buffer_send+offset, H.bytes, 32);
offset += 32;
//mask xin
- memmove(this->buffer_send+offset, xx.bytes, 32);
- offset += 32;
+ this->send_secret(xx.bytes, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
this->exchange();
- memmove(a.bytes, &this->buffer_recv[32*0], 32);
- memmove(aG.bytes, &this->buffer_recv[32*1], 32);
- memmove(aHP.bytes, &this->buffer_recv[32*2], 32);
- memmove(II.bytes, &this->buffer_recv[32*3], 32);
+ offset = 0;
+ this->receive_secret(a.bytes, offset);
+ memmove(aG.bytes, &this->buffer_recv[offset], 32);
+ offset +=32;
+ memmove(aHP.bytes, &this->buffer_recv[offset], 32);
+ offset +=32;
+ memmove(II.bytes, &this->buffer_recv[offset], 32);
#ifdef DEBUG_HWDEVICE
a_x = hw::ledger::decrypt(a);
@@ -1788,6 +1868,7 @@ namespace hw {
bool device_ledger::mlsag_prepare(rct::key &a, rct::key &aG) {
AUTO_LOCK_CMD();
+ int offset;
#ifdef DEBUG_HWDEVICE
rct::key a_x;
@@ -1796,8 +1877,9 @@ namespace hw {
send_simple(INS_MLSAG, 0x01);
- memmove(a.bytes, &this->buffer_recv[32*0], 32);
- memmove(aG.bytes, &this->buffer_recv[32*1], 32);
+ offset = 0;
+ this->receive_secret(a.bytes, offset);
+ memmove(aG.bytes, &this->buffer_recv[offset], 32);
#ifdef DEBUG_HWDEVICE
a_x = hw::ledger::decrypt(a);
@@ -1870,11 +1952,9 @@ namespace hw {
}
offset += 1;
//xx
- memmove(this->buffer_send+offset, xx[j].bytes, 32);
- offset += 32;
+ this->send_secret(xx[j].bytes, offset);
//alpa
- memmove(this->buffer_send+offset, alpha[j].bytes, 32);
- offset += 32;
+ this->send_secret(alpha[j].bytes, offset);
this->buffer_send[4] = offset-5;
this->length_send = offset;
@@ -1900,6 +1980,10 @@ namespace hw {
bool device_ledger::close_tx() {
AUTO_LOCK_CMD();
send_simple(INS_CLOSE_TX);
+ key_map.clear();
+ hmac_map.clear();
+ this->tx_in_progress = false;
+ this->unlock();
return true;
}
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index 986087128..05a26143a 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -90,6 +90,25 @@ namespace hw {
void log();
};
+ class SecHMAC {
+ public:
+ uint32_t sec[32];
+ uint32_t hmac[32];
+
+ SecHMAC(const uint8_t s[32], const uint8_t m[32]);
+
+ };
+
+ class HMACmap {
+ public:
+ std::vector<SecHMAC> hmacs;
+
+ void find_mac(const uint8_t sec[32], uint8_t hmac[32]) ;
+ void add_mac(const uint8_t sec[32], const uint8_t hmac[32]) ;
+ void clear() ;
+ };
+
+
#define BUFFER_SEND_SIZE 262
#define BUFFER_RECV_SIZE 262
@@ -115,15 +134,21 @@ namespace hw {
int set_command_header(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00);
int set_command_header_noopt(unsigned char ins, unsigned char p1 = 0x00, unsigned char p2 = 0x00);
void send_simple(unsigned char ins, unsigned char p1 = 0x00);
-
+ void send_secret(const unsigned char sec[32], int &offset);
+ void receive_secret(unsigned char sec[32], int &offset);
// hw running mode
device_mode mode;
+ bool tx_in_progress;
+
// map public destination key to ephemeral destination key
Keymap key_map;
bool add_output_key_mapping(const crypto::public_key &Aout, const crypto::public_key &Bout, const bool is_subaddress, const bool is_change,
const bool need_additional, const size_t real_output_index,
const rct::key &amount_key, const crypto::public_key &out_eph_public_key);
+ //hmac for some encrypted value
+ HMACmap hmac_map;
+
// To speed up blockchain parsing the view key maybe handle here.
crypto::secret_key viewkey;
bool has_view_key;
@@ -174,7 +199,7 @@ namespace hw {
bool get_public_address(cryptonote::account_public_address &pubkey) override;
bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
bool generate_chacha_key(const cryptonote::account_keys &keys, crypto::chacha_key &key, uint64_t kdf_rounds) override;
-
+ void display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) override;
/* ======================================================================= */
/* SUB ADDRESS */
diff --git a/src/gen_ssl_cert/CMakeLists.txt b/src/gen_ssl_cert/CMakeLists.txt
new file mode 100644
index 000000000..471df021b
--- /dev/null
+++ b/src/gen_ssl_cert/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Copyright (c) 2017-2019, 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(gen_ssl_cert_sources
+ gen_ssl_cert.cpp)
+
+monero_add_executable(gen_ssl_cert
+ ${gen_ssl_cert_sources})
+target_link_libraries(gen_ssl_cert
+ PRIVATE
+ common
+ epee
+ version
+ ${EPEE_READLINE}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${EXTRA_LIBRARIES})
+add_dependencies(gen_ssl_cert
+ version)
+set_property(TARGET gen_ssl_cert
+ PROPERTY
+ OUTPUT_NAME "monero-gen-ssl-cert")
+install(TARGETS gen_ssl_cert DESTINATION bin)
diff --git a/src/gen_ssl_cert/gen_ssl_cert.cpp b/src/gen_ssl_cert/gen_ssl_cert.cpp
new file mode 100644
index 000000000..7a9b01700
--- /dev/null
+++ b/src/gen_ssl_cert/gen_ssl_cert.cpp
@@ -0,0 +1,254 @@
+// Copyright (c) 2019, 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 <boost/program_options.hpp>
+#include <boost/format.hpp>
+#include <boost/algorithm/string.hpp>
+#include <openssl/ssl.h>
+#include <openssl/pem.h>
+#include "include_base_utils.h"
+#include "file_io_utils.h"
+#include "net/net_ssl.h"
+#include "crypto/crypto.h"
+#include "common/util.h"
+#include "common/i18n.h"
+#include "common/command_line.h"
+#include "common/scoped_message_writer.h"
+#include "common/password.h"
+#include "version.h"
+
+namespace po = boost::program_options;
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "gen_ssl_cert"
+
+namespace gencert
+{
+ const char* tr(const char* str)
+ {
+ return i18n_translate(str, "tools::gen_ssl_cert");
+ }
+
+}
+
+namespace
+{
+ const command_line::arg_descriptor<std::string> arg_certificate_filename = {"certificate-filename", gencert::tr("Filename to save the certificate"), ""};
+ const command_line::arg_descriptor<std::string> arg_private_key_filename = {"private-key-filename", gencert::tr("Filename to save the private key"), ""};
+ const command_line::arg_descriptor<std::string> arg_passphrase = {"passphrase", gencert::tr("Passphrase with which to encrypt the private key"), ""};
+ const command_line::arg_descriptor<std::string> arg_passphrase_file = {"passphrase-file", gencert::tr("File containing the passphrase with which to encrypt the private key"), ""};
+ const command_line::arg_descriptor<bool> arg_prompt_for_passphrase = {"prompt-for-passphrase", gencert::tr("Prompt for a passphrase with which to encrypt the private key"), false};
+}
+
+// adapted from openssl's apps/x509.c
+static std::string get_fingerprint(X509 *cert, const EVP_MD *fdig)
+{
+ unsigned int j;
+ unsigned int n;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ std::string fingerprint;
+
+ if (!X509_digest(cert, fdig, md, &n))
+ {
+ tools::fail_msg_writer() << tr("Failed to create fingerprint: ") << ERR_reason_error_string(ERR_get_error());
+ return fingerprint;
+ }
+ fingerprint.resize(n * 3 - 1);
+ char *out = &fingerprint[0];
+ for (j = 0; j < n; ++j)
+ {
+ snprintf(out, 3 + (j + 1 < n), "%02X%s", md[j], (j + 1 == n) ? "" : ":");
+ out += 3;
+ }
+ return fingerprint;
+}
+
+int main(int argc, char* argv[])
+{
+ TRY_ENTRY();
+
+ tools::on_startup();
+
+ po::options_description desc_cmd_only("Command line options");
+ po::options_description desc_cmd_sett("Command line options and settings options");
+
+ command_line::add_arg(desc_cmd_sett, arg_certificate_filename);
+ command_line::add_arg(desc_cmd_sett, arg_private_key_filename);
+ command_line::add_arg(desc_cmd_sett, arg_passphrase);
+ command_line::add_arg(desc_cmd_sett, arg_passphrase_file);
+ command_line::add_arg(desc_cmd_sett, arg_prompt_for_passphrase);
+
+ command_line::add_arg(desc_cmd_only, command_line::arg_help);
+ command_line::add_arg(desc_cmd_only, command_line::arg_version);
+
+ po::options_description desc_options("Allowed options");
+ desc_options.add(desc_cmd_only).add(desc_cmd_sett);
+
+ po::variables_map vm;
+ bool r = command_line::handle_error_helper(desc_options, [&]()
+ {
+ po::store(po::parse_command_line(argc, argv, desc_options), vm);
+ po::notify(vm);
+ return true;
+ });
+ if (!r)
+ return 1;
+
+ if (command_line::get_arg(vm, command_line::arg_help))
+ {
+ std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL;
+ std::cout << desc_options << std::endl;
+ return 0;
+ }
+ if (command_line::get_arg(vm, command_line::arg_version))
+ {
+ std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL;
+ return 0;
+ }
+
+ const std::string certificate_filename = command_line::get_arg(vm, arg_certificate_filename);
+ if (certificate_filename.empty())
+ {
+ tools::fail_msg_writer() << gencert::tr("Argument is needed: ") << "--" << arg_certificate_filename.name;
+ return 1;
+ }
+ const std::string private_key_filename = command_line::get_arg(vm, arg_private_key_filename);
+ if (private_key_filename.empty())
+ {
+ tools::fail_msg_writer() << gencert::tr("Argument is needed: ") << "--" << arg_private_key_filename.name;
+ return 1;
+ }
+
+ epee::wipeable_string private_key_passphrase;
+ if (command_line::get_arg(vm, arg_prompt_for_passphrase))
+ {
+ auto pwd_container = tools::password_container::prompt(true, "Enter passphrase for the new SSL private key");
+ if (!pwd_container)
+ {
+ tools::fail_msg_writer() << gencert::tr("Failed to read passphrase");
+ return 1;
+ }
+ private_key_passphrase = pwd_container->password();
+ }
+ else if (!command_line::is_arg_defaulted(vm, arg_passphrase_file))
+ {
+ std::string passphrase_file = command_line::get_arg(vm, arg_passphrase_file);
+ if (!passphrase_file.empty())
+ {
+ std::string passphrase;
+ if (!epee::file_io_utils::load_file_to_string(passphrase_file, passphrase))
+ {
+ MERROR("Failed to load passphrase");
+ return 1;
+ }
+
+ // Remove line breaks the user might have inserted
+ boost::trim_right_if(passphrase, boost::is_any_of("\r\n"));
+ private_key_passphrase = passphrase;
+ memwipe(&passphrase[0], passphrase.size());
+ }
+ }
+ else
+ {
+ private_key_passphrase = command_line::get_arg(vm, arg_passphrase);
+ }
+ if (private_key_passphrase.empty())
+ tools::msg_writer(epee::console_color_yellow) << (boost::format(tr("Empty passphrase, the private key will be saved to disk unencrypted, use --%s to set a passphrase or --%s to prompt for one")) % arg_passphrase.name % arg_prompt_for_passphrase.name).str();
+
+ EVP_PKEY *pkey;
+ X509 *cert;
+ r = epee::net_utils::create_rsa_ssl_certificate(pkey, cert);
+ if (!r)
+ {
+ tools::fail_msg_writer() << gencert::tr("Failed to create certificate");
+ return 1;
+ }
+
+ // write cert
+ BIO *bio_cert = BIO_new(BIO_s_mem());
+ r = PEM_write_bio_X509(bio_cert, cert);
+ if (!r)
+ {
+ BIO_free(bio_cert);
+ tools::fail_msg_writer() << gencert::tr("Failed to write certificate: ") << ERR_reason_error_string(ERR_get_error());
+ return 1;
+ }
+ BUF_MEM *buf = NULL;
+ BIO_get_mem_ptr(bio_cert, &buf);
+ if (!buf || !buf->data || !buf->length)
+ {
+ BIO_free(bio_cert);
+ tools::fail_msg_writer() << gencert::tr("Failed to write certificate: ") << ERR_reason_error_string(ERR_get_error());
+ return 1;
+ }
+ const std::string certificate(std::string(buf->data, buf->length));
+ BIO_free(bio_cert);
+
+ // write private key
+ BIO *bio_pkey = BIO_new(BIO_s_mem());
+ r = PEM_write_bio_PKCS8PrivateKey(bio_pkey, pkey, private_key_passphrase.empty() ? NULL : EVP_aes_128_cfb(), private_key_passphrase.data(), private_key_passphrase.size(), NULL, NULL);
+ if (!r)
+ {
+ BIO_free(bio_pkey);
+ tools::fail_msg_writer() << gencert::tr("Failed to write private key: ") << ERR_reason_error_string(ERR_get_error());
+ return 1;
+ }
+ buf = NULL;
+ BIO_get_mem_ptr(bio_pkey, &buf);
+ if (!buf || !buf->data || !buf->length)
+ {
+ BIO_free(bio_pkey);
+ tools::fail_msg_writer() << gencert::tr("Failed to write private key: ") << ERR_reason_error_string(ERR_get_error());
+ return 1;
+ }
+ const std::string private_key(std::string(buf->data, buf->length));
+ BIO_free(bio_pkey);
+
+ // write files
+ tools::set_strict_default_file_permissions(true);
+ r = epee::file_io_utils::save_string_to_file(certificate_filename, certificate);
+ if (!r)
+ {
+ tools::fail_msg_writer() << gencert::tr("Failed to save certificate file");
+ return 1;
+ }
+ r = epee::file_io_utils::save_string_to_file(private_key_filename, private_key);
+ if (!r)
+ {
+ tools::fail_msg_writer() << gencert::tr("Failed to save private key file");
+ return 1;
+ }
+
+ tools::success_msg_writer() << tr("New certificate created:");
+ tools::success_msg_writer() << tr("Certificate: ") << certificate_filename;
+ tools::success_msg_writer() << tr("SHA-256 Fingerprint: ") << get_fingerprint(cert, EVP_sha256());
+ tools::success_msg_writer() << tr("Private key: ") << private_key_filename << " (" << (private_key_passphrase.empty() ? "unencrypted" : "encrypted") << ")";
+
+ return 0;
+ CATCH_ENTRY_L0("main", 1);
+}
diff --git a/src/hardforks/CMakeLists.txt b/src/hardforks/CMakeLists.txt
new file mode 100644
index 000000000..bd2d14ceb
--- /dev/null
+++ b/src/hardforks/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright (c) 2014-2019, 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(hardforks_sources
+ hardforks.cpp)
+
+set(hardforks_headers
+ hardforks.h)
+
+set(hardforks_private_headers)
+
+monero_private_headers(hardforks
+ ${hardforks_private_headers})
+monero_add_library(hardforks
+ ${hardforks_sources}
+ ${hardforks_headers}
+ ${hardforks_private_headers})
+target_link_libraries(hardforks
+ PUBLIC
+ version
+ PRIVATE
+ ${EXTRA_LIBRARIES})
diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp
new file mode 100644
index 000000000..7ad09dbef
--- /dev/null
+++ b/src/hardforks/hardforks.cpp
@@ -0,0 +1,114 @@
+// Copyright (c) 2014-2019, 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 "hardforks.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "blockchain.hardforks"
+
+const hardfork_t mainnet_hard_forks[] = {
+ // version 1 from the start of the blockchain
+ { 1, 1, 0, 1341378000 },
+
+ // version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork.
+ { 2, 1009827, 0, 1442763710 },
+
+ // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21.
+ { 3, 1141317, 0, 1458558528 },
+
+ // 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 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14.
+ { 5, 1288616, 0, 1489520158 },
+
+ // version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18.
+ { 6, 1400000, 0, 1503046577 },
+
+ // version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17.
+ { 7, 1546000, 0, 1521303150 },
+
+ // version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02.
+ { 8, 1685555, 0, 1535889547 },
+
+ // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02.
+ { 9, 1686275, 0, 1535889548 },
+
+ // version 10 starts from block 1788000, which is on or around the 9th of March, 2019. Fork time finalised on 2019-02-10.
+ { 10, 1788000, 0, 1549792439 },
+
+ // version 11 starts from block 1788720, which is on or around the 10th of March, 2019. Fork time finalised on 2019-02-15.
+ { 11, 1788720, 0, 1550225678 },
+
+ // version 12 starts from block 1978433, which is on or around the 30th of November, 2019. Fork time finalised on 2019-10-18.
+ { 12, 1978433, 0, 1571419280 },
+};
+const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
+const uint64_t mainnet_hard_fork_version_1_till = 1009826;
+
+const hardfork_t testnet_hard_forks[] = {
+ // version 1 from the start of the blockchain
+ { 1, 1, 0, 1341378000 },
+
+ // version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork.
+ { 2, 624634, 0, 1445355000 },
+
+ // versions 3-5 were passed in rapid succession from September 18th, 2016
+ { 3, 800500, 0, 1472415034 },
+ { 4, 801219, 0, 1472415035 },
+ { 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
+
+ { 6, 971400, 0, 1501709789 },
+ { 7, 1057027, 0, 1512211236 },
+ { 8, 1057058, 0, 1533211200 },
+ { 9, 1057778, 0, 1533297600 },
+ { 10, 1154318, 0, 1550153694 },
+ { 11, 1155038, 0, 1550225678 },
+ { 12, 1308737, 0, 1569582000 },
+};
+const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
+const uint64_t testnet_hard_fork_version_1_till = 624633;
+
+const hardfork_t stagenet_hard_forks[] = {
+ // version 1 from the start of the blockchain
+ { 1, 1, 0, 1341378000 },
+
+ // versions 2-7 in rapid succession from March 13th, 2018
+ { 2, 32000, 0, 1521000000 },
+ { 3, 33000, 0, 1521120000 },
+ { 4, 34000, 0, 1521240000 },
+ { 5, 35000, 0, 1521360000 },
+ { 6, 36000, 0, 1521480000 },
+ { 7, 37000, 0, 1521600000 },
+ { 8, 176456, 0, 1537821770 },
+ { 9, 177176, 0, 1537821771 },
+ { 10, 269000, 0, 1550153694 },
+ { 11, 269720, 0, 1550225678 },
+ { 12, 454721, 0, 1571419280 },
+};
+const size_t num_stagenet_hard_forks = sizeof(stagenet_hard_forks) / sizeof(stagenet_hard_forks[0]);
diff --git a/src/hardforks/hardforks.h b/src/hardforks/hardforks.h
new file mode 100644
index 000000000..e7bceca42
--- /dev/null
+++ b/src/hardforks/hardforks.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2014-2019, 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 <stdint.h>
+#include <time.h>
+
+struct hardfork_t
+{
+ uint8_t version;
+ uint64_t height;
+ uint8_t threshold;
+ time_t time;
+ hardfork_t(uint8_t version, uint64_t height, uint8_t threshold, time_t time): version(version), height(height), threshold(threshold), time(time) {}
+};
+
+extern const hardfork_t mainnet_hard_forks[];
+extern const uint64_t mainnet_hard_fork_version_1_till;
+extern const size_t num_mainnet_hard_forks;
+
+extern const hardfork_t testnet_hard_forks[];
+extern const uint64_t testnet_hard_fork_version_1_till;
+extern const size_t num_testnet_hard_forks;
+
+extern const hardfork_t stagenet_hard_forks[];
+extern const size_t num_stagenet_hard_forks;
diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt
index 24b707f77..1ce7118ad 100644
--- a/src/net/CMakeLists.txt
+++ b/src/net/CMakeLists.txt
@@ -26,9 +26,11 @@
# 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(net_sources dandelionpp.cpp error.cpp i2p_address.cpp parse.cpp socks.cpp socks_connect.cpp tor_address.cpp)
-set(net_headers dandelionpp.h error.h i2p_address.h parse.h socks.h socks_connect.h tor_address.h)
+set(net_sources dandelionpp.cpp error.cpp i2p_address.cpp parse.cpp socks.cpp
+ socks_connect.cpp tor_address.cpp zmq.cpp)
+set(net_headers dandelionpp.h error.h i2p_address.h parse.h socks.h socks_connect.h
+ tor_address.h zmq.h)
monero_add_library(net ${net_sources} ${net_headers})
-target_link_libraries(net common epee ${Boost_ASIO_LIBRARY})
+target_link_libraries(net common epee ${ZMQ_LIB} ${Boost_ASIO_LIBRARY})
diff --git a/src/net/zmq.cpp b/src/net/zmq.cpp
new file mode 100644
index 000000000..d02a22983
--- /dev/null
+++ b/src/net/zmq.cpp
@@ -0,0 +1,188 @@
+// Copyright (c) 2019, 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 "net/zmq.h"
+
+#include <cassert>
+#include <cerrno>
+#include <limits>
+#include <utility>
+
+namespace net
+{
+namespace zmq
+{
+ const std::error_category& error_category() noexcept
+ {
+ struct category final : std::error_category
+ {
+ virtual const char* name() const noexcept override final
+ {
+ return "error::error_category()";
+ }
+
+ virtual std::string message(int value) const override final
+ {
+ char const* const msg = zmq_strerror(value);
+ if (msg)
+ return msg;
+ return "zmq_strerror failure";
+ }
+
+ virtual std::error_condition default_error_condition(int value) const noexcept override final
+ {
+ // maps specific errors to generic `std::errc` cases.
+ switch (value)
+ {
+ case EFSM:
+ case ETERM:
+ break;
+ default:
+ /* zmq is using cerrno errors. C++ spec indicates that
+ `std::errc` values must be identical to the cerrno value.
+ So just map every zmq specific error to the generic errc
+ equivalent. zmq extensions must be in the switch or they
+ map to a non-existent errc enum value. */
+ return std::errc(value);
+ }
+ return std::error_condition{value, *this};
+ }
+
+ };
+ static const category instance{};
+ return instance;
+ }
+
+ void terminate::call(void* ptr) noexcept
+ {
+ assert(ptr != nullptr); // see header
+ while (zmq_term(ptr))
+ {
+ if (zmq_errno() != EINTR)
+ break;
+ }
+ }
+
+ namespace
+ {
+ //! RAII wrapper for `zmq_msg_t`.
+ class message
+ {
+ zmq_msg_t handle_;
+
+ public:
+ message() noexcept
+ : handle_()
+ {
+ zmq_msg_init(handle());
+ }
+
+ message(message&& rhs) = delete;
+ message(const message& rhs) = delete;
+ message& operator=(message&& rhs) = delete;
+ message& operator=(const message& rhs) = delete;
+
+ ~message() noexcept
+ {
+ zmq_msg_close(handle());
+ }
+
+ zmq_msg_t* handle() noexcept
+ {
+ return std::addressof(handle_);
+ }
+
+ const char* data() noexcept
+ {
+ return static_cast<const char*>(zmq_msg_data(handle()));
+ }
+
+ std::size_t size() noexcept
+ {
+ return zmq_msg_size(handle());
+ }
+ };
+
+ struct do_receive
+ {
+ /* ZMQ documentation states that message parts are atomic - either
+ all are received or none are. Looking through ZMQ code and
+ Github discussions indicates that after part 1 is returned,
+ `EAGAIN` cannot be returned to meet these guarantees. Unit tests
+ verify (for the `inproc://` case) that this is the behavior.
+ Therefore, read errors after the first part are treated as a
+ failure for the entire message (probably `ETERM`). */
+ int operator()(std::string& payload, void* const socket, const int flags) const
+ {
+ static constexpr const int max_out = std::numeric_limits<int>::max();
+ const std::string::size_type initial = payload.size();
+ message part{};
+ for (;;)
+ {
+ int last = 0;
+ if ((last = zmq_msg_recv(part.handle(), socket, flags)) < 0)
+ return last;
+
+ payload.append(part.data(), part.size());
+ if (!zmq_msg_more(part.handle()))
+ break;
+ }
+ const std::string::size_type added = payload.size() - initial;
+ return unsigned(max_out) < added ? max_out : int(added);
+ }
+ };
+
+ template<typename F, typename... T>
+ expect<void> retry_op(F op, T&&... args) noexcept(noexcept(op(args...)))
+ {
+ for (;;)
+ {
+ if (0 <= op(args...))
+ return success();
+
+ const int error = zmq_errno();
+ if (error != EINTR)
+ return make_error_code(error);
+ }
+ }
+ } // anonymous
+
+ expect<std::string> receive(void* const socket, const int flags)
+ {
+ std::string payload{};
+ MONERO_CHECK(retry_op(do_receive{}, payload, socket, flags));
+ return {std::move(payload)};
+ }
+
+ expect<void> send(const epee::span<const std::uint8_t> payload, void* const socket, const int flags) noexcept
+ {
+ return retry_op(zmq_send, socket, payload.data(), payload.size(), flags);
+ }
+} // zmq
+} // net
+
diff --git a/src/net/zmq.h b/src/net/zmq.h
new file mode 100644
index 000000000..c6a7fd743
--- /dev/null
+++ b/src/net/zmq.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2019, 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 <memory>
+#include <string>
+#include <system_error>
+#include <zmq.h>
+
+#include "common/expect.h"
+#include "span.h"
+
+//! If the expression is less than 0, return the current ZMQ error code.
+#define MONERO_ZMQ_CHECK(...) \
+ do \
+ { \
+ if (( __VA_ARGS__ ) < 0) \
+ return {::net::zmq::get_error_code()}; \
+ } while (0)
+
+//! Print a message followed by the current ZMQ error message.
+#define MONERO_LOG_ZMQ_ERROR(...) \
+ do \
+ { \
+ MERROR( __VA_ARGS__ << ": " << ::net::zmq::get_error_code().message()); \
+ } while (0)
+
+//! Throw an exception with a custom `msg`, current ZMQ error code, filename, and line number.
+#define MONERO_ZMQ_THROW(msg) \
+ MONERO_THROW( ::net::zmq::get_error_code(), msg )
+
+namespace net
+{
+namespace zmq
+{
+ //! \return Category for ZMQ errors.
+ const std::error_category& error_category() noexcept;
+
+ //! \return `code` (usally from zmq_errno()`) using `net::zmq::error_category()`.
+ inline std::error_code make_error_code(int code) noexcept
+ {
+ return std::error_code{code, error_category()};
+ }
+
+ //! \return Error from `zmq_errno()` using `net::zmq::error_category()`.
+ inline std::error_code get_error_code() noexcept
+ {
+ return make_error_code(zmq_errno());
+ }
+
+ //! Calls `zmq_term`
+ class terminate
+ {
+ static void call(void* ptr) noexcept;
+ public:
+ void operator()(void* ptr) const noexcept
+ {
+ if (ptr)
+ call(ptr);
+ }
+ };
+
+ //! Calls `zmq_close`
+ struct close
+ {
+ void operator()(void* ptr) const noexcept
+ {
+ if (ptr)
+ zmq_close(ptr);
+ }
+ };
+
+ //! Unique ZMQ context handle, calls `zmq_term` on destruction.
+ using context = std::unique_ptr<void, terminate>;
+
+ //! Unique ZMQ socket handle, calls `zmq_close` on destruction.
+ using socket = std::unique_ptr<void, close>;
+
+ /*! Read all parts of the next message on `socket`. Blocks until the entire
+ next message (all parts) are read, or until `zmq_term` is called on the
+ `zmq_context` associated with `socket`. If the context is terminated,
+ `make_error_code(ETERM)` is returned.
+
+ \note This will automatically retry on `EINTR`, so exiting on
+ interrupts requires context termination.
+ \note If non-blocking behavior is requested on `socket` or by `flags`,
+ then `net::zmq::make_error_code(EAGAIN)` will be returned if this
+ would block.
+
+ \param socket Handle created with `zmq_socket`.
+ \param flags See `zmq_msg_read` for possible flags.
+ \return Message payload read from `socket` or ZMQ error. */
+ expect<std::string> receive(void* socket, int flags = 0);
+
+ /*! Sends `payload` on `socket`. Blocks until the entire message is queued
+ for sending, or until `zmq_term` is called on the `zmq_context`
+ associated with `socket`. If the context is terminated,
+ `make_error_code(ETERM)` is returned.
+
+ \note This will automatically retry on `EINTR`, so exiting on
+ interrupts requires context termination.
+ \note If non-blocking behavior is requested on `socket` or by `flags`,
+ then `net::zmq::make_error_code(EAGAIN)` will be returned if this
+ would block.
+
+ \param payload sent as one message on `socket`.
+ \param socket Handle created with `zmq_socket`.
+ \param flags See `zmq_send` for possible flags.
+ \return `success()` if sent, otherwise ZMQ error. */
+ expect<void> send(epee::span<const std::uint8_t> payload, void* socket, int flags = 0) noexcept;
+} // zmq
+} // net
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index c7fc058ca..58c5706de 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -144,7 +144,7 @@ namespace nodetool
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node = {"add-exclusive-node", "Specify list of peers to connect to only."
" If this option is given the options add-priority-node and seed-node are ignored"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"};
- const command_line::arg_descriptor<std::vector<std::string> > arg_proxy = {"proxy", "<network-type>,<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""};
+ const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy = {"tx-proxy", "Send local txes through proxy: <network-type>,<socks-ip:port>[,max_connections][,disable_noise] i.e. \"tor,127.0.0.1:9050,100,disable_noise\""};
const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound = {"anonymous-inbound", "<hidden-service-address>,<[bind-ip:]port>[,max_connections] i.e. \"x.onion,127.0.0.1:18083,100\""};
const command_line::arg_descriptor<bool> arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true};
const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false};
@@ -152,7 +152,7 @@ namespace nodetool
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"};
const command_line::arg_descriptor<bool> arg_p2p_use_ipv6 = {"p2p-use-ipv6", "Enable IPv6 for p2p", false};
- const command_line::arg_descriptor<bool> arg_p2p_require_ipv4 = {"p2p-require-ipv4", "Require successful IPv4 bind for p2p", true};
+ const command_line::arg_descriptor<bool> arg_p2p_ignore_ipv4 = {"p2p-ignore-ipv4", "Ignore unsuccessful IPv4 bind for p2p", false};
const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1};
const command_line::arg_descriptor<int64_t> arg_in_peers = {"in-peers", "set max number of in peers", -1};
const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1};
@@ -161,13 +161,17 @@ namespace nodetool
const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN};
const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1};
+ const command_line::arg_descriptor<bool> arg_pad_transactions = {
+ "pad-transactions", "Pad relayed transactions to help defend against traffic volume analysis", false
+ };
+
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
{
namespace ip = boost::asio::ip;
std::vector<proxy> proxies{};
- const std::vector<std::string> args = command_line::get_arg(vm, arg_proxy);
+ const std::vector<std::string> args = command_line::get_arg(vm, arg_tx_proxy);
proxies.reserve(args.size());
for (const boost::string_ref arg : args)
@@ -175,11 +179,11 @@ namespace nodetool
proxies.emplace_back();
auto next = boost::algorithm::make_split_iterator(arg, boost::algorithm::first_finder(","));
- CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No network type for --" << arg_proxy.name);
+ CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No network type for --" << arg_tx_proxy.name);
const boost::string_ref zone{next->begin(), next->size()};
++next;
- CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No ipv4:port given for --" << arg_proxy.name);
+ CHECK_AND_ASSERT_MES(!next.eof() && !next->empty(), boost::none, "No ipv4:port given for --" << arg_tx_proxy.name);
const boost::string_ref proxy{next->begin(), next->size()};
++next;
@@ -187,7 +191,7 @@ namespace nodetool
{
if (2 <= count)
{
- MERROR("Too many ',' characters given to --" << arg_proxy.name);
+ MERROR("Too many ',' characters given to --" << arg_tx_proxy.name);
return boost::none;
}
@@ -198,7 +202,7 @@ namespace nodetool
proxies.back().max_connections = get_max_connections(*next);
if (proxies.back().max_connections == 0)
{
- MERROR("Invalid max connections given to --" << arg_proxy.name);
+ MERROR("Invalid max connections given to --" << arg_tx_proxy.name);
return boost::none;
}
}
@@ -213,7 +217,7 @@ namespace nodetool
proxies.back().zone = epee::net_utils::zone::i2p;
break;
default:
- MERROR("Invalid network for --" << arg_proxy.name);
+ MERROR("Invalid network for --" << arg_tx_proxy.name);
return boost::none;
}
@@ -221,7 +225,7 @@ namespace nodetool
std::uint16_t port = 0;
if (!epee::string_tools::parse_peer_from_string(ip, port, std::string{proxy}) || port == 0)
{
- MERROR("Invalid ipv4:port given for --" << arg_proxy.name);
+ MERROR("Invalid ipv4:port given for --" << arg_tx_proxy.name);
return boost::none;
}
proxies.back().address = ip::tcp::endpoint{ip::address_v4{boost::endian::native_to_big(ip)}, port};
@@ -258,7 +262,7 @@ namespace nodetool
inbounds.back().max_connections = get_max_connections(*next);
if (inbounds.back().max_connections == 0)
{
- MERROR("Invalid max connections given to --" << arg_proxy.name);
+ MERROR("Invalid max connections given to --" << arg_tx_proxy.name);
return boost::none;
}
}
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 255a1fc1f..0e9c1c942 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -38,11 +38,13 @@
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/uuid/uuid.hpp>
+#include <chrono>
#include <functional>
#include <utility>
#include <vector>
#include "cryptonote_config.h"
+#include "cryptonote_protocol/fwd.h"
#include "cryptonote_protocol/levin_notify.h"
#include "warnings.h"
#include "net/abstract_tcp_server2.h"
@@ -108,8 +110,16 @@ namespace nodetool
template<class base_type>
struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base
{
- p2p_connection_context_t(): peer_id(0), support_flags(0), m_in_timedsync(false) {}
+ p2p_connection_context_t()
+ : fluff_txs(),
+ flush_time(std::chrono::steady_clock::time_point::max()),
+ peer_id(0),
+ support_flags(0),
+ m_in_timedsync(false)
+ {}
+ std::vector<cryptonote::blobdata> fluff_txs;
+ std::chrono::steady_clock::time_point flush_time;
peerid_type peer_id;
uint32_t support_flags;
bool m_in_timedsync;
@@ -231,6 +241,7 @@ namespace nodetool
: m_payload_handler(payload_handler),
m_external_port(0),
m_rpc_port(0),
+ m_rpc_credits_per_hash(0),
m_allow_local_ip(false),
m_hide_my_port(false),
m_igd(no_igd),
@@ -271,7 +282,7 @@ namespace nodetool
virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet);
virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return !is_remote_host_allowed(address, seconds); }
- virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
+ virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_subnets; }
virtual void add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
@@ -335,14 +346,14 @@ namespace nodetool
virtual void callback(p2p_connection_context& context);
//----------------- i_p2p_endpoint -------------------------------------------------------------
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections);
- virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs);
+ virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core);
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context);
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context);
virtual bool drop_connection(const epee::net_utils::connection_context_base& context);
virtual void request_callback(const epee::net_utils::connection_context_base& context);
virtual void for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f);
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f);
- virtual bool add_host_fail(const epee::net_utils::network_address &address);
+ virtual bool add_host_fail(const epee::net_utils::network_address &address, unsigned int score = 1);
//----------------- i_connection_filter --------------------------------------------------------
virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL);
//-----------------------------------------------------------------------------------------------
@@ -431,6 +442,11 @@ namespace nodetool
m_rpc_port = rpc_port;
}
+ void set_rpc_credits_per_hash(uint32_t rpc_credits_per_hash)
+ {
+ m_rpc_credits_per_hash = rpc_credits_per_hash;
+ }
+
private:
std::string m_config_folder;
@@ -440,6 +456,7 @@ namespace nodetool
uint32_t m_listening_port_ipv6;
uint32_t m_external_port;
uint16_t m_rpc_port;
+ uint32_t m_rpc_credits_per_hash;
bool m_allow_local_ip;
bool m_hide_my_port;
igd_t m_igd;
@@ -484,11 +501,11 @@ namespace nodetool
std::map<epee::net_utils::zone, network_zone> m_network_zones;
- std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache;
+ std::map<std::string, time_t> m_conn_fails_cache;
epee::critical_section m_conn_fails_cache_lock;
epee::critical_section m_blocked_hosts_lock; // for both hosts and subnets
- std::map<epee::net_utils::network_address, time_t> m_blocked_hosts;
+ std::map<std::string, time_t> m_blocked_hosts;
std::map<epee::net_utils::ipv4_network_subnet, time_t> m_blocked_subnets;
epee::critical_section m_host_fails_score_lock;
@@ -510,14 +527,14 @@ namespace nodetool
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port;
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6;
extern const command_line::arg_descriptor<bool> arg_p2p_use_ipv6;
- extern const command_line::arg_descriptor<bool> arg_p2p_require_ipv4;
+ extern const command_line::arg_descriptor<bool> arg_p2p_ignore_ipv4;
extern const command_line::arg_descriptor<uint32_t> arg_p2p_external_port;
extern const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_priority_node;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_exclusive_node;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_seed_node;
- extern const command_line::arg_descriptor<std::vector<std::string> > arg_proxy;
+ extern const command_line::arg_descriptor<std::vector<std::string> > arg_tx_proxy;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_anonymous_inbound;
extern const command_line::arg_descriptor<bool> arg_p2p_hide_my_port;
extern const command_line::arg_descriptor<bool> arg_no_sync;
@@ -532,6 +549,7 @@ namespace nodetool
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up;
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_down;
extern const command_line::arg_descriptor<int64_t> arg_limit_rate;
+ extern const command_line::arg_descriptor<bool> arg_pad_transactions;
}
POP_WARNINGS
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 97a18b519..263cecfa2 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -97,14 +97,14 @@ namespace nodetool
command_line::add_arg(desc, arg_p2p_bind_port, false);
command_line::add_arg(desc, arg_p2p_bind_port_ipv6, false);
command_line::add_arg(desc, arg_p2p_use_ipv6);
- command_line::add_arg(desc, arg_p2p_require_ipv4);
+ command_line::add_arg(desc, arg_p2p_ignore_ipv4);
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);
command_line::add_arg(desc, arg_p2p_add_priority_node);
command_line::add_arg(desc, arg_p2p_add_exclusive_node);
command_line::add_arg(desc, arg_p2p_seed_node);
- command_line::add_arg(desc, arg_proxy);
+ command_line::add_arg(desc, arg_tx_proxy);
command_line::add_arg(desc, arg_anonymous_inbound);
command_line::add_arg(desc, arg_p2p_hide_my_port);
command_line::add_arg(desc, arg_no_sync);
@@ -116,6 +116,7 @@ namespace nodetool
command_line::add_arg(desc, arg_limit_rate_up);
command_line::add_arg(desc, arg_limit_rate_down);
command_line::add_arg(desc, arg_limit_rate);
+ command_line::add_arg(desc, arg_pad_transactions);
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@@ -166,7 +167,7 @@ namespace nodetool
const time_t now = time(nullptr);
// look in the hosts list
- auto it = m_blocked_hosts.find(address);
+ auto it = m_blocked_hosts.find(address.host_str());
if (it != m_blocked_hosts.end())
{
if (now >= it->second)
@@ -224,7 +225,7 @@ namespace nodetool
limit = std::numeric_limits<time_t>::max();
else
limit = now + seconds;
- m_blocked_hosts[addr] = limit;
+ m_blocked_hosts[addr.host_str()] = limit;
// drop any connection to that address. This should only have to look into
// the zone related to the connection, but really make sure everything is
@@ -254,7 +255,7 @@ namespace nodetool
bool node_server<t_payload_net_handler>::unblock_host(const epee::net_utils::network_address &address)
{
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
- auto i = m_blocked_hosts.find(address);
+ auto i = m_blocked_hosts.find(address.host_str());
if (i == m_blocked_hosts.end())
return false;
m_blocked_hosts.erase(i);
@@ -315,13 +316,13 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address)
+ bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address, unsigned int score)
{
if(!address.is_blockable())
return false;
CRITICAL_REGION_LOCAL(m_host_fails_score_lock);
- uint64_t fails = ++m_host_fails_score[address.host_str()];
+ uint64_t fails = m_host_fails_score[address.host_str()] += score;
MDEBUG("Host " << address.host_str() << " fail score=" << fails);
if(fails > P2P_IP_FAILS_BEFORE_BLOCK)
{
@@ -340,6 +341,7 @@ namespace nodetool
{
bool testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
bool stagenet = command_line::get_arg(vm, cryptonote::arg_stagenet_on);
+ const bool pad_txs = command_line::get_arg(vm, arg_pad_transactions);
m_nettype = testnet ? cryptonote::TESTNET : stagenet ? cryptonote::STAGENET : cryptonote::MAINNET;
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
@@ -382,9 +384,9 @@ namespace nodetool
}
m_offline = command_line::get_arg(vm, cryptonote::arg_offline);
m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
- m_require_ipv4 = command_line::get_arg(vm, arg_p2p_require_ipv4);
+ m_require_ipv4 = !command_line::get_arg(vm, arg_p2p_ignore_ipv4);
public_zone.m_notifier = cryptonote::levin::notify{
- public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr
+ public_zone.m_net_server.get_io_service(), public_zone.m_net_server.get_config_shared(), nullptr, true, pad_txs
};
if (command_line::has_arg(vm, arg_p2p_add_peer))
@@ -475,7 +477,7 @@ namespace nodetool
network_zone& zone = add_zone(proxy.zone);
if (zone.m_connect != nullptr)
{
- MERROR("Listed --" << arg_proxy.name << " twice with " << epee::net_utils::zone_to_string(proxy.zone));
+ MERROR("Listed --" << arg_tx_proxy.name << " twice with " << epee::net_utils::zone_to_string(proxy.zone));
return false;
}
zone.m_connect = &socks_connect;
@@ -495,7 +497,7 @@ namespace nodetool
}
zone.m_notifier = cryptonote::levin::notify{
- zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise)
+ zone.m_net_server.get_io_service(), zone.m_net_server.get_config_shared(), std::move(this_noise), false, pad_txs
};
}
@@ -503,7 +505,7 @@ namespace nodetool
{
if (zone.second.m_connect == nullptr)
{
- MERROR("Set outgoing peer for " << epee::net_utils::zone_to_string(zone.first) << " but did not set --" << arg_proxy.name);
+ MERROR("Set outgoing peer for " << epee::net_utils::zone_to_string(zone.first) << " but did not set --" << arg_tx_proxy.name);
return false;
}
}
@@ -525,7 +527,7 @@ namespace nodetool
if (zone.m_connect == nullptr && tx_relay_zones <= 1)
{
- MERROR("Listed --" << arg_anonymous_inbound.name << " without listing any --" << arg_proxy.name << ". The latter is necessary for sending origin txes over anonymity networks");
+ MERROR("Listed --" << arg_anonymous_inbound.name << " without listing any --" << arg_tx_proxy.name << ". The latter is necessary for sending local txes over anonymity networks");
return false;
}
@@ -604,11 +606,13 @@ namespace nodetool
full_addrs.insert("163.172.182.165:28080");
full_addrs.insert("195.154.123.123:28080");
full_addrs.insert("212.83.172.165:28080");
+ full_addrs.insert("192.110.160.146:28080");
}
else if (nettype == cryptonote::STAGENET)
{
full_addrs.insert("162.210.173.150:38080");
full_addrs.insert("162.210.173.151:38080");
+ full_addrs.insert("192.110.160.146:38080");
}
else if (nettype == cryptonote::FAKECHAIN)
{
@@ -623,6 +627,7 @@ namespace nodetool
full_addrs.insert("198.74.231.92:18080");
full_addrs.insert("195.154.123.123:18080");
full_addrs.insert("212.83.172.165:18080");
+ full_addrs.insert("192.110.160.146:18080");
}
return full_addrs;
}
@@ -670,11 +675,18 @@ namespace nodetool
std::vector<std::vector<std::string>> dns_results;
dns_results.resize(m_seed_nodes_list.size());
+ // some libc implementation provide only a very small stack
+ // for threads, e.g. musl only gives +- 80kb, which is not
+ // enough to do a resolve with unbound. we request a stack
+ // of 1 mb, which should be plenty
+ boost::thread::attributes thread_attributes;
+ thread_attributes.set_stack_size(1024*1024);
+
std::list<boost::thread> dns_threads;
uint64_t result_index = 0;
for (const std::string& addr_str : m_seed_nodes_list)
{
- boost::thread th = boost::thread([=, &dns_results, &addr_str]
+ boost::thread th = boost::thread(thread_attributes, [=, &dns_results, &addr_str]
{
MDEBUG("dns_threads[" << result_index << "] created for: " << addr_str);
// TODO: care about dnssec avail/valid
@@ -1047,7 +1059,8 @@ namespace nodetool
pi = context.peer_id = rsp.node_data.peer_id;
context.m_rpc_port = rsp.node_data.rpc_port;
- m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port);
+ context.m_rpc_credits_per_hash = rsp.node_data.rpc_credits_per_hash;
+ m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash);
// move
for (auto const& zone : m_network_zones)
@@ -1078,7 +1091,7 @@ namespace nodetool
LOG_WARNING_CC(context_, "COMMAND_HANDSHAKE Failed");
m_network_zones.at(context_.m_remote_address.get_zone()).m_net_server.get_config_object().close(context_.m_connection_id);
}
- else
+ else if (!just_take_peerlist)
{
try_get_support_flags(context_, [](p2p_connection_context& flags_context, const uint32_t& support_flags)
{
@@ -1113,7 +1126,7 @@ namespace nodetool
add_host_fail(context.m_remote_address);
}
if(!context.m_is_income)
- m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port);
+ m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port, context.m_rpc_credits_per_hash);
if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false))
{
m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id );
@@ -1282,6 +1295,7 @@ namespace nodetool
pe_local.last_seen = static_cast<int64_t>(last_seen);
pe_local.pruning_seed = con->m_pruning_seed;
pe_local.rpc_port = con->m_rpc_port;
+ pe_local.rpc_credits_per_hash = con->m_rpc_credits_per_hash;
zone.m_peerlist.append_with_peer_white(pe_local);
//update last seen and push it to peerlist manager
@@ -1342,7 +1356,7 @@ namespace nodetool
bool node_server<t_payload_net_handler>::is_addr_recently_failed(const epee::net_utils::network_address& addr)
{
CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock);
- auto it = m_conn_fails_cache.find(addr);
+ auto it = m_conn_fails_cache.find(addr.host_str());
if(it == m_conn_fails_cache.end())
return false;
@@ -1857,7 +1871,11 @@ namespace nodetool
const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
if (ipv4.ip() == 0)
ignore = true;
+ else if (ipv4.port() == be.rpc_port)
+ ignore = true;
}
+ if (be.pruning_seed && (be.pruning_seed < tools::make_pruning_seed(1, CRYPTONOTE_PRUNING_LOG_STRIPES) || be.pruning_seed > tools::make_pruning_seed(1ul << CRYPTONOTE_PRUNING_LOG_STRIPES, CRYPTONOTE_PRUNING_LOG_STRIPES)))
+ ignore = true;
if (ignore)
{
MDEBUG("Ignoring " << be.adr.str());
@@ -1866,6 +1884,7 @@ namespace nodetool
--i;
continue;
}
+ local_peerlist[i].last_seen = 0;
#ifdef CRYPTONOTE_PRUNING_DEBUG_SPOOF_SEED
be.pruning_seed = tools::make_pruning_seed(1 + (be.adr.as<epee::net_utils::ipv4_network_address>().ip()) % (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES), CRYPTONOTE_PRUNING_LOG_STRIPES);
@@ -1908,6 +1927,7 @@ namespace nodetool
else
node_data.my_port = 0;
node_data.rpc_port = zone.m_can_pingback ? m_rpc_port : 0;
+ node_data.rpc_credits_per_hash = zone.m_can_pingback ? m_rpc_credits_per_hash : 0;
node_data.network_id = m_network_id;
return true;
}
@@ -2036,13 +2056,18 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs)
+ epee::net_utils::zone node_server<t_payload_net_handler>::send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)
{
namespace enet = epee::net_utils;
- const auto send = [&txs, &source, pad_txs] (std::pair<const enet::zone, network_zone>& network)
+ const auto send = [&txs, &source, &core] (std::pair<const enet::zone, network_zone>& network)
{
- if (network.second.m_notifier.send_txs(std::move(txs), source, (pad_txs || network.first != enet::zone::public_)))
+ const bool is_public = (network.first == enet::zone::public_);
+ const cryptonote::relay_method tx_relay = is_public ?
+ cryptonote::relay_method::fluff : cryptonote::relay_method::local;
+
+ core.on_transactions_relayed(epee::to_span(txs), tx_relay);
+ if (network.second.m_notifier.send_txs(std::move(txs), source))
return network.first;
return enet::zone::invalid;
};
@@ -2352,6 +2377,7 @@ namespace nodetool
context.peer_id = arg.node_data.peer_id;
context.m_in_timedsync = false;
context.m_rpc_port = arg.node_data.rpc_port;
+ context.m_rpc_credits_per_hash = arg.node_data.rpc_credits_per_hash;
if(arg.node_data.my_port && zone.m_can_pingback)
{
@@ -2379,6 +2405,7 @@ namespace nodetool
pe.id = peer_id_l;
pe.pruning_seed = context.m_pruning_seed;
pe.rpc_port = context.m_rpc_port;
+ pe.rpc_credits_per_hash = context.m_rpc_credits_per_hash;
this->m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.append_with_peer_white(pe);
LOG_DEBUG_CC(context, "PING SUCCESS " << context.m_remote_address.host_str() << ":" << port_l);
});
@@ -2696,7 +2723,7 @@ namespace nodetool
}
else
{
- zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed, pe.rpc_port);
+ zone.second.m_peerlist.set_peer_just_seen(pe.id, pe.adr, pe.pruning_seed, pe.rpc_port, pe.rpc_credits_per_hash);
LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << pe.adr.host_str() << " Peer ID: " << peerid_type(pe.id));
}
}
diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h
index 239814c2c..ed88aa28c 100644
--- a/src/p2p/net_node_common.h
+++ b/src/p2p/net_node_common.h
@@ -34,6 +34,8 @@
#include <utility>
#include <vector>
#include "cryptonote_basic/blobdatatype.h"
+#include "cryptonote_protocol/enums.h"
+#include "cryptonote_protocol/fwd.h"
#include "net/enums.h"
#include "net/net_utils_base.h"
#include "p2p_protocol_defs.h"
@@ -48,7 +50,7 @@ namespace nodetool
struct i_p2p_endpoint
{
virtual bool relay_notify_to_list(int command, const epee::span<const uint8_t> data_buff, std::vector<std::pair<epee::net_utils::zone, boost::uuids::uuid>> connections)=0;
- virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs)=0;
+ virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)=0;
virtual bool invoke_command_to_peer(int command, const epee::span<const uint8_t> req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0;
virtual bool invoke_notify_to_peer(int command, const epee::span<const uint8_t> req_buff, const epee::net_utils::connection_context_base& context)=0;
virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0;
@@ -58,9 +60,9 @@ namespace nodetool
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0;
virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0;
virtual bool unblock_host(const epee::net_utils::network_address &address)=0;
- virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()=0;
+ virtual std::map<std::string, time_t> get_blocked_hosts()=0;
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()=0;
- virtual bool add_host_fail(const epee::net_utils::network_address &address)=0;
+ virtual bool add_host_fail(const epee::net_utils::network_address &address, unsigned int score = 1)=0;
virtual void add_used_stripe_peer(const t_connection_context &context)=0;
virtual void remove_used_stripe_peer(const t_connection_context &context)=0;
virtual void clear_used_stripe_peers()=0;
@@ -73,7 +75,7 @@ namespace nodetool
{
return false;
}
- virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, const bool pad_txs)
+ virtual epee::net_utils::zone send_txs(std::vector<cryptonote::blobdata> txs, const epee::net_utils::zone origin, const boost::uuids::uuid& source, cryptonote::i_core_events& core)
{
return epee::net_utils::zone::invalid;
}
@@ -114,15 +116,15 @@ namespace nodetool
{
return true;
}
- virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()
+ virtual std::map<std::string, time_t> get_blocked_hosts()
{
- return std::map<epee::net_utils::network_address, time_t>();
+ return std::map<std::string, time_t>();
}
virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()
{
return std::map<epee::net_utils::ipv4_network_subnet, time_t>();
}
- virtual bool add_host_fail(const epee::net_utils::network_address &address)
+ virtual bool add_host_fail(const epee::net_utils::network_address &address, unsigned int score)
{
return true;
}
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index c65b9dd82..58b704f73 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -111,7 +111,7 @@ namespace nodetool
bool append_with_peer_white(const peerlist_entry& pr);
bool append_with_peer_gray(const peerlist_entry& pr);
bool append_with_peer_anchor(const anchor_peerlist_entry& ple);
- bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port);
+ bool set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash);
bool set_peer_unreachable(const peerlist_entry& pr);
bool is_host_allowed(const epee::net_utils::network_address &address);
bool get_random_gray_peer(peerlist_entry& pe);
@@ -315,7 +315,7 @@ namespace nodetool
}
//--------------------------------------------------------------------------------------------------
inline
- bool peerlist_manager::set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port)
+ bool peerlist_manager::set_peer_just_seen(peerid_type peer, const epee::net_utils::network_address& addr, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash)
{
TRY_ENTRY();
CRITICAL_REGION_LOCAL(m_peerlist_lock);
@@ -326,6 +326,7 @@ namespace nodetool
ple.last_seen = time(NULL);
ple.pruning_seed = pruning_seed;
ple.rpc_port = rpc_port;
+ ple.rpc_credits_per_hash = rpc_credits_per_hash;
return append_with_peer_white(ple);
CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false);
}
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index c2773981c..bd5063510 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -42,7 +42,7 @@
#include "common/pruning.h"
#endif
-BOOST_CLASS_VERSION(nodetool::peerlist_entry, 2)
+BOOST_CLASS_VERSION(nodetool::peerlist_entry, 3)
namespace boost
{
@@ -241,6 +241,13 @@ namespace boost
return;
}
a & pl.rpc_port;
+ if (ver < 3)
+ {
+ if (!typename Archive::is_saving())
+ pl.rpc_credits_per_hash = 0;
+ return;
+ }
+ a & pl.rpc_credits_per_hash;
}
template <class Archive, class ver_type>
diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h
index 85774fcd5..44b278589 100644
--- a/src/p2p/p2p_protocol_defs.h
+++ b/src/p2p/p2p_protocol_defs.h
@@ -77,6 +77,7 @@ namespace nodetool
int64_t last_seen;
uint32_t pruning_seed;
uint16_t rpc_port;
+ uint32_t rpc_credits_per_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(adr)
@@ -85,6 +86,7 @@ namespace nodetool
KV_SERIALIZE_OPT(last_seen, (int64_t)0)
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
KV_SERIALIZE_OPT(rpc_port, (uint16_t)0)
+ KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0)
END_KV_SERIALIZE_MAP()
};
typedef peerlist_entry_base<epee::net_utils::network_address> peerlist_entry;
@@ -132,6 +134,7 @@ namespace nodetool
{
ss << pe.id << "\t" << pe.adr.str()
<< " \trpc port " << (pe.rpc_port > 0 ? std::to_string(pe.rpc_port) : "-")
+ << " \trpc credits per hash " << (pe.rpc_credits_per_hash > 0 ? std::to_string(pe.rpc_credits_per_hash) : "-")
<< " \tpruning seed " << pe.pruning_seed
<< " \tlast_seen: " << (pe.last_seen == 0 ? std::string("never") : epee::misc_utils::get_time_interval_string(now_time - pe.last_seen))
<< std::endl;
@@ -166,6 +169,7 @@ namespace nodetool
uint64_t local_time;
uint32_t my_port;
uint16_t rpc_port;
+ uint32_t rpc_credits_per_hash;
peerid_type peer_id;
BEGIN_KV_SERIALIZE_MAP()
@@ -174,6 +178,7 @@ namespace nodetool
KV_SERIALIZE(local_time)
KV_SERIALIZE(my_port)
KV_SERIALIZE_OPT(rpc_port, (uint16_t)(0))
+ KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0)
END_KV_SERIALIZE_MAP()
};
@@ -220,7 +225,7 @@ namespace nodetool
{
const epee::net_utils::network_address &na = p.adr;
const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
- local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen, p.pruning_seed, p.rpc_port}));
+ local_peerlist.push_back(peerlist_entry_base<network_address_old>({{ipv4.ip(), ipv4.port()}, p.id, p.last_seen, p.pruning_seed, p.rpc_port, p.rpc_credits_per_hash}));
}
else
MDEBUG("Not including in legacy peer list: " << p.adr.str());
@@ -235,7 +240,7 @@ namespace nodetool
std::vector<peerlist_entry_base<network_address_old>> local_peerlist;
epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(local_peerlist, stg, hparent_section, "local_peerlist");
for (const auto &p: local_peerlist)
- ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen, p.pruning_seed, p.rpc_port}));
+ ((response&)this_ref).local_peerlist_new.push_back(peerlist_entry({epee::net_utils::ipv4_network_address(p.adr.ip, p.adr.port), p.id, p.last_seen, p.pruning_seed, p.rpc_port, p.rpc_credits_per_hash}));
}
}
END_KV_SERIALIZE_MAP()
diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc
index ff6fee95c..80ecc5593 100644
--- a/src/ringct/bulletproofs.cc
+++ b/src/ringct/bulletproofs.cc
@@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Adapted from Java code by Sarang Noether
+// Paper references are to https://eprint.iacr.org/2017/1066 (revision 1 July 2018)
#include <stdlib.h>
#include <boost/thread/mutex.hpp>
@@ -521,6 +522,7 @@ Bulletproof bulletproof_PROVE(const rct::keyV &sv, const rct::keyV &gamma)
}
PERF_TIMER_STOP_BP(PROVE_v);
+ // PAPER LINES 41-42
PERF_TIMER_START_BP(PROVE_aLaR);
for (size_t j = 0; j < M; ++j)
{
@@ -566,14 +568,14 @@ try_again:
rct::key hash_cache = rct::hash_to_scalar(V);
PERF_TIMER_START_BP(PROVE_step1);
- // PAPER LINES 38-39
+ // PAPER LINES 43-44
rct::key alpha = rct::skGen();
rct::key ve = vector_exponent(aL8, aR8);
rct::key A;
sc_mul(tmp.bytes, alpha.bytes, INV_EIGHT.bytes);
rct::addKeys(A, ve, rct::scalarmultBase(tmp));
- // PAPER LINES 40-42
+ // PAPER LINES 45-47
rct::keyV sL = rct::skvGen(MN), sR = rct::skvGen(MN);
rct::key rho = rct::skGen();
ve = vector_exponent(sL, sR);
@@ -581,7 +583,7 @@ try_again:
rct::addKeys(S, ve, rct::scalarmultBase(rho));
S = rct::scalarmultKey(S, INV_EIGHT);
- // PAPER LINES 43-45
+ // PAPER LINES 48-50
rct::key y = hash_cache_mash(hash_cache, A, S);
if (y == rct::zero())
{
@@ -598,24 +600,20 @@ try_again:
}
// Polynomial construction by coefficients
+ // PAPER LINES 70-71
rct::keyV l0 = vector_subtract(aL, z);
const rct::keyV &l1 = sL;
- // This computes the ugly sum/concatenation from PAPER LINE 65
rct::keyV zero_twos(MN);
const rct::keyV zpow = vector_powers(z, M+2);
- for (size_t i = 0; i < MN; ++i)
+ for (size_t j = 0; j < M; ++j)
{
- zero_twos[i] = rct::zero();
- for (size_t j = 1; j <= M; ++j)
- {
- if (i >= (j-1)*N && i < j*N)
+ for (size_t i = 0; i < N; ++i)
{
- CHECK_AND_ASSERT_THROW_MES(1+j < zpow.size(), "invalid zpow index");
- CHECK_AND_ASSERT_THROW_MES(i-(j-1)*N < twoN.size(), "invalid twoN index");
- sc_muladd(zero_twos[i].bytes, zpow[1+j].bytes, twoN[i-(j-1)*N].bytes, zero_twos[i].bytes);
+ CHECK_AND_ASSERT_THROW_MES(j+2 < zpow.size(), "invalid zpow index");
+ CHECK_AND_ASSERT_THROW_MES(i < twoN.size(), "invalid twoN index");
+ sc_mul(zero_twos[j*N+i].bytes,zpow[j+2].bytes,twoN[i].bytes);
}
- }
}
rct::keyV r0 = vector_add(aR, z);
@@ -624,7 +622,7 @@ try_again:
r0 = vector_add(r0, zero_twos);
rct::keyV r1 = hadamard(yMN, sR);
- // Polynomial construction before PAPER LINE 46
+ // Polynomial construction before PAPER LINE 51
rct::key t1_1 = inner_product(l0, r1);
rct::key t1_2 = inner_product(l1, r0);
rct::key t1;
@@ -634,7 +632,7 @@ try_again:
PERF_TIMER_STOP_BP(PROVE_step1);
PERF_TIMER_START_BP(PROVE_step2);
- // PAPER LINES 47-48
+ // PAPER LINES 52-53
rct::key tau1 = rct::skGen(), tau2 = rct::skGen();
rct::key T1, T2;
@@ -648,7 +646,7 @@ try_again:
ge_double_scalarmult_base_vartime_p3(&p3, tmp.bytes, &ge_p3_H, tmp2.bytes);
ge_p3_tobytes(T2.bytes, &p3);
- // PAPER LINES 49-51
+ // PAPER LINES 54-56
rct::key x = hash_cache_mash(hash_cache, z, T1, T2);
if (x == rct::zero())
{
@@ -657,7 +655,7 @@ try_again:
goto try_again;
}
- // PAPER LINES 52-53
+ // PAPER LINES 61-63
rct::key taux;
sc_mul(taux.bytes, tau1.bytes, x.bytes);
rct::key xsq;
@@ -671,7 +669,7 @@ try_again:
rct::key mu;
sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes);
- // PAPER LINES 54-57
+ // PAPER LINES 58-60
rct::keyV l = l0;
l = vector_add(l, vector_scalar(l1, x));
rct::keyV r = r0;
@@ -690,7 +688,7 @@ try_again:
CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed");
#endif
- // PAPER LINES 32-33
+ // PAPER LINE 6
rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t);
if (x_ip == rct::zero())
{
@@ -725,20 +723,19 @@ try_again:
PERF_TIMER_STOP_BP(PROVE_step3);
PERF_TIMER_START_BP(PROVE_step4);
- // PAPER LINE 13
const rct::keyV *scale = &yinvpow;
while (nprime > 1)
{
- // PAPER LINE 15
+ // PAPER LINE 20
nprime /= 2;
- // PAPER LINES 16-17
+ // PAPER LINES 21-22
PERF_TIMER_START_BP(PROVE_inner_product);
rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size()));
rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime));
PERF_TIMER_STOP_BP(PROVE_inner_product);
- // PAPER LINES 18-19
+ // PAPER LINES 23-24
PERF_TIMER_START_BP(PROVE_LR);
sc_mul(tmp.bytes, cL.bytes, x_ip.bytes);
L[round] = cross_vector_exponent8(nprime, Gprime, nprime, Hprime, 0, aprime, 0, bprime, nprime, scale, &ge_p3_H, &tmp);
@@ -746,7 +743,7 @@ try_again:
R[round] = cross_vector_exponent8(nprime, Gprime, 0, Hprime, nprime, aprime, nprime, bprime, 0, scale, &ge_p3_H, &tmp);
PERF_TIMER_STOP_BP(PROVE_LR);
- // PAPER LINES 21-22
+ // PAPER LINES 25-27
w[round] = hash_cache_mash(hash_cache, L[round], R[round]);
if (w[round] == rct::zero())
{
@@ -755,7 +752,7 @@ try_again:
goto try_again;
}
- // PAPER LINES 24-25
+ // PAPER LINES 29-30
const rct::key winv = invert(w[round]);
if (nprime > 1)
{
@@ -765,7 +762,7 @@ try_again:
PERF_TIMER_STOP_BP(PROVE_hadamard2);
}
- // PAPER LINES 28-29
+ // PAPER LINES 33-34
PERF_TIMER_START_BP(PROVE_prime);
aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv));
bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round]));
@@ -776,7 +773,6 @@ try_again:
}
PERF_TIMER_STOP_BP(PROVE_step4);
- // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20)
return Bulletproof(std::move(V), A, S, T1, T2, taux, mu, std::move(L), std::move(R), aprime[0], bprime[0], t);
}
@@ -810,7 +806,10 @@ struct proof_data_t
size_t logM, inv_offset;
};
-/* Given a range proof, determine if it is valid */
+/* Given a range proof, determine if it is valid
+ * This uses the method in PAPER LINES 95-105,
+ * weighted across multiple proofs in a batch
+ */
bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
{
init_exponents();
@@ -871,7 +870,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds");
PERF_TIMER_START_BP(VERIFY_line_21_22);
- // PAPER LINES 21-22
// The inner product challenges are computed per round
pd.w.resize(rounds);
for (size_t i = 0; i < rounds; ++i)
@@ -929,7 +927,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
rct::key proof8_A = rct::scalarmult8(proof.A);
PERF_TIMER_START_BP(VERIFY_line_61);
- // PAPER LINE 61
sc_mulsub(m_y0.bytes, proof.taux.bytes, weight_y.bytes, m_y0.bytes);
const rct::keyV zpow = vector_powers(pd.z, M+3);
@@ -962,7 +959,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
PERF_TIMER_STOP_BP(VERIFY_line_61rl_new);
PERF_TIMER_START_BP(VERIFY_line_62);
- // PAPER LINE 62
multiexp_data.emplace_back(weight_z, proof8_A);
sc_mul(tmp.bytes, pd.x.bytes, weight_z.bytes);
multiexp_data.emplace_back(tmp, proof8_S);
@@ -973,7 +969,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds");
PERF_TIMER_START_BP(VERIFY_line_24_25);
- // Basically PAPER LINES 24-25
// Compute the curvepoints from G[i] and H[i]
rct::key yinvpow = rct::identity();
rct::key ypow = rct::identity();
@@ -1010,7 +1005,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
sc_mul(g_scalar.bytes, g_scalar.bytes, w_cache[i].bytes);
sc_mul(h_scalar.bytes, h_scalar.bytes, w_cache[(~i) & (MN-1)].bytes);
- // Adjust the scalars using the exponents from PAPER LINE 62
sc_add(g_scalar.bytes, g_scalar.bytes, pd.z.bytes);
CHECK_AND_ASSERT_MES(2+i/N < zpow.size(), false, "invalid zpow index");
CHECK_AND_ASSERT_MES(i%N < twoN.size(), false, "invalid twoN index");
@@ -1043,7 +1037,6 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
PERF_TIMER_STOP_BP(VERIFY_line_24_25);
- // PAPER LINE 26
PERF_TIMER_START_BP(VERIFY_line_26_new);
sc_muladd(z1.bytes, proof.mu.bytes, weight_z.bytes, z1.bytes);
for (size_t i = 0; i < rounds; ++i)
diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp
index 2c4e5fc3b..1763542db 100644
--- a/src/ringct/rctTypes.cpp
+++ b/src/ringct/rctTypes.cpp
@@ -31,6 +31,7 @@
#include "misc_log_ex.h"
#include "cryptonote_config.h"
#include "rctTypes.h"
+#include "int-util.h"
using namespace crypto;
using namespace std;
@@ -118,40 +119,22 @@ namespace rct {
//uint long long to 32 byte key
void d2h(key & amounth, const xmr_amount in) {
sc_0(amounth.bytes);
- xmr_amount val = in;
- int i = 0;
- while (val != 0) {
- amounth[i] = (unsigned char)(val & 0xFF);
- i++;
- val /= (xmr_amount)256;
- }
+ memcpy_swap64le(amounth.bytes, &in, 1);
}
//uint long long to 32 byte key
key d2h(const xmr_amount in) {
key amounth;
- sc_0(amounth.bytes);
- xmr_amount val = in;
- int i = 0;
- while (val != 0) {
- amounth[i] = (unsigned char)(val & 0xFF);
- i++;
- val /= (xmr_amount)256;
- }
+ d2h(amounth, in);
return amounth;
}
//uint long long to int[64]
void d2b(bits amountb, xmr_amount val) {
int i = 0;
- while (val != 0) {
- amountb[i] = val & 1;
- i++;
- val >>= 1;
- }
while (i < 64) {
- amountb[i] = 0;
- i++;
+ amountb[i++] = val & 1;
+ val >>= 1;
}
}
@@ -172,16 +155,11 @@ namespace rct {
int val = 0, i = 0, j = 0;
for (j = 0; j < 8; j++) {
val = (unsigned char)test.bytes[j];
- i = 8 * j;
- while (val != 0) {
- amountb2[i] = val & 1;
- i++;
+ i = 0;
+ while (i < 8) {
+ amountb2[j*8+i++] = val & 1;
val >>= 1;
}
- while (i < 8 * (j + 1)) {
- amountb2[i] = 0;
- i++;
- }
}
}
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index f8729b872..bf4b7b4aa 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -321,6 +321,7 @@ namespace rct {
std::vector<mgSig> MGs; // simple rct has N, full has 1
keyV pseudoOuts; //C - for simple rct
+ // when changing this function, update cryptonote::get_pruned_transaction_weight
template<bool W, template <bool> class Archive>
bool serialize_rctsig_prunable(Archive<W> &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin)
{
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index d98670e05..65d88b57e 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -26,13 +26,18 @@
# 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 ${ZMQ_INCLUDE_PATH})
+
set(rpc_base_sources
- rpc_args.cpp)
+ rpc_args.cpp
+ rpc_payment_signature.cpp
+ rpc_handler.cpp)
set(rpc_sources
bootstrap_daemon.cpp
core_rpc_server.cpp
- rpc_handler.cpp
+ rpc_payment.cpp
+ rpc_version_str.cpp
instanciations)
set(daemon_messages_sources
@@ -45,9 +50,12 @@ set(daemon_rpc_server_sources
set(rpc_base_headers
- rpc_args.h)
+ rpc_args.h
+ rpc_payment_signature.h
+ rpc_handler.h)
set(rpc_headers
+ rpc_version_str.h
rpc_handler.h)
set(daemon_rpc_server_headers)
@@ -56,6 +64,7 @@ set(daemon_rpc_server_headers)
set(rpc_daemon_private_headers
bootstrap_daemon.h
core_rpc_server.h
+ rpc_payment.h
core_rpc_server_commands_defs.h
core_rpc_server_error_codes.h)
diff --git a/src/rpc/bootstrap_daemon.cpp b/src/rpc/bootstrap_daemon.cpp
index 6f8426ee7..c97b2c95a 100644
--- a/src/rpc/bootstrap_daemon.cpp
+++ b/src/rpc/bootstrap_daemon.cpp
@@ -12,7 +12,7 @@
namespace cryptonote
{
- bootstrap_daemon::bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node) noexcept
+ bootstrap_daemon::bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node)
: m_get_next_public_node(get_next_public_node)
{
}
diff --git a/src/rpc/bootstrap_daemon.h b/src/rpc/bootstrap_daemon.h
index 130a6458d..6276b1b21 100644
--- a/src/rpc/bootstrap_daemon.h
+++ b/src/rpc/bootstrap_daemon.h
@@ -15,7 +15,7 @@ namespace cryptonote
class bootstrap_daemon
{
public:
- bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node) noexcept;
+ bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node);
bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials);
std::string address() const noexcept;
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 7706d2cf7..dc93e7023 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -29,6 +29,7 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <boost/preprocessor/stringize.hpp>
+#include <boost/uuid/nil_generator.hpp>
#include "include_base_utils.h"
#include "string_tools.h"
using namespace epee;
@@ -39,6 +40,7 @@ using namespace epee;
#include "common/download.h"
#include "common/util.h"
#include "common/perf_timer.h"
+#include "int-util.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
@@ -49,6 +51,8 @@ using namespace epee;
#include "crypto/hash.h"
#include "rpc/rpc_args.h"
#include "rpc/rpc_handler.h"
+#include "rpc/rpc_payment_costs.h"
+#include "rpc/rpc_payment_signature.h"
#include "core_rpc_server_error_codes.h"
#include "p2p/net_node.h"
#include "version.h"
@@ -61,8 +65,50 @@ using namespace epee;
#define OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION (3 * 86400) // 3 days max, the wallet requests 1.8 days
+#define DEFAULT_PAYMENT_DIFFICULTY 1000
+#define DEFAULT_PAYMENT_CREDITS_PER_HASH 100
+
+#define RPC_TRACKER(rpc) \
+ PERF_TIMER(rpc); \
+ RPCTracker tracker(#rpc, PERF_TIMER_NAME(rpc))
+
namespace
{
+ class RPCTracker
+ {
+ public:
+ struct entry_t
+ {
+ uint64_t count;
+ uint64_t time;
+ uint64_t credits;
+ };
+
+ RPCTracker(const char *rpc, tools::LoggingPerformanceTimer &timer): rpc(rpc), timer(timer) {
+ }
+ ~RPCTracker() {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ auto &e = tracker[rpc];
+ ++e.count;
+ e.time += timer.value();
+ }
+ void pay(uint64_t amount) {
+ boost::unique_lock<boost::mutex> lock(mutex);
+ auto &e = tracker[rpc];
+ e.credits += amount;
+ }
+ const std::string &rpc_name() const { return rpc; }
+ static void clear() { boost::unique_lock<boost::mutex> lock(mutex); tracker.clear(); }
+ static std::unordered_map<std::string, entry_t> data() { boost::unique_lock<boost::mutex> lock(mutex); return tracker; }
+ private:
+ std::string rpc;
+ tools::LoggingPerformanceTimer &timer;
+ static boost::mutex mutex;
+ static std::unordered_map<std::string, entry_t> tracker;
+ };
+ boost::mutex RPCTracker::mutex;
+ std::unordered_map<std::string, RPCTracker::entry_t> RPCTracker::tracker;
+
void add_reason(std::string &reasons, const char *reason)
{
if (!reasons.empty())
@@ -95,6 +141,9 @@ namespace cryptonote
command_line::add_arg(desc, arg_bootstrap_daemon_address);
command_line::add_arg(desc, arg_bootstrap_daemon_login);
cryptonote::rpc_args::init_options(desc, true);
+ command_line::add_arg(desc, arg_rpc_payment_address);
+ command_line::add_arg(desc, arg_rpc_payment_difficulty);
+ command_line::add_arg(desc, arg_rpc_payment_credits);
}
//------------------------------------------------------------------------------------------------------------------------------
core_rpc_server::core_rpc_server(
@@ -173,6 +222,11 @@ namespace cryptonote
return true;
}
+ core_rpc_server::~core_rpc_server()
+ {
+ if (m_rpc_payment)
+ m_rpc_payment->store();
+ }
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::init(
const boost::program_options::variables_map& vm
@@ -188,6 +242,45 @@ namespace cryptonote
if (!rpc_config)
return false;
+ std::string address = command_line::get_arg(vm, arg_rpc_payment_address);
+ if (!address.empty())
+ {
+ if (!m_restricted && nettype() != FAKECHAIN)
+ {
+ MERROR("RPC payment enabled, but server is not restricted, anyone can adjust their balance to bypass payment");
+ return false;
+ }
+ cryptonote::address_parse_info info;
+ if (!get_account_address_from_str(info, nettype(), address))
+ {
+ MERROR("Invalid payment address: " << address);
+ return false;
+ }
+ if (info.is_subaddress)
+ {
+ MERROR("Payment address may not be a subaddress: " << address);
+ return false;
+ }
+ uint64_t diff = command_line::get_arg(vm, arg_rpc_payment_difficulty);
+ uint64_t credits = command_line::get_arg(vm, arg_rpc_payment_credits);
+ if (diff == 0 || credits == 0)
+ {
+ MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given");
+ return false;
+ }
+ m_rpc_payment.reset(new rpc_payment(info.address, diff, credits));
+ m_rpc_payment->load(command_line::get_arg(vm, cryptonote::arg_data_dir));
+ m_p2p.set_rpc_credits_per_hash(RPC_CREDITS_PER_HASH_SCALE * (credits / (float)diff));
+ }
+
+ if (!m_rpc_payment)
+ {
+ uint32_t bind_ip;
+ bool ok = epee::string_tools::get_ip_int32_from_string(bind_ip, rpc_config->bind_ip);
+ if (ok & !epee::net_utils::is_ip_loopback(bind_ip))
+ MWARNING("The RPC server is accessible from the outside, but no RPC payment was setup. RPC access will be free for all.");
+ }
+
if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address),
command_line::get_arg(vm, arg_bootstrap_daemon_login)))
{
@@ -200,6 +293,9 @@ namespace cryptonote
if (rpc_config->login)
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
+ if (m_rpc_payment)
+ m_net_server.add_idle_handler([this](){ return m_rpc_payment->on_idle(); }, 60 * 1000);
+
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
rng, std::move(port), std::move(rpc_config->bind_ip),
@@ -208,6 +304,45 @@ namespace cryptonote
);
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::check_payment(const std::string &client_message, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash)
+ {
+ if (m_rpc_payment == NULL)
+ {
+ credits = 0;
+ return true;
+ }
+ uint64_t height;
+ crypto::hash hash;
+ m_core.get_blockchain_top(height, hash);
+ top_hash = epee::string_tools::pod_to_hex(hash);
+ crypto::public_key client;
+ uint64_t ts;
+#ifndef NDEBUG
+ if (nettype() == TESTNET && client_message == "debug")
+ {
+ credits = 0;
+ return true;
+ }
+#endif
+ if (!cryptonote::verify_rpc_payment_signature(client_message, client, ts))
+ {
+ credits = 0;
+ message = "Client signature does not verify for " + rpc;
+ return false;
+ }
+ crypto::public_key local_client;
+ if (!m_rpc_payment->pay(client, ts, payment, rpc, same_ts, credits))
+ {
+ message = CORE_RPC_STATUS_PAYMENT_REQUIRED;
+ return false;
+ }
+ return true;
+ }
+#define CHECK_PAYMENT_BASE(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P > 0 && !check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0)
+#define CHECK_PAYMENT(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, false)
+#define CHECK_PAYMENT_SAME_TS(req, res, payment) CHECK_PAYMENT_BASE(req, res, payment, true)
+#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx) break; uint64_t P = (uint64_t)payment; if (P == 0) P = 1; if(!check_payment(req.client, P, tracker.rpc_name(), same_ts, res.status, res.credits, res.top_hash)){return true;} tracker.pay(P); } while(0)
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::check_core_ready()
{
if(!m_p2p.get_payload_object().is_synchronized())
@@ -217,13 +352,13 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::add_host_fail(const connection_context *ctx)
+ bool core_rpc_server::add_host_fail(const connection_context *ctx, unsigned int score)
{
if(!ctx || !ctx->m_remote_address.is_blockable())
return false;
CRITICAL_REGION_LOCAL(m_host_fails_score_lock);
- uint64_t fails = ++m_host_fails_score[ctx->m_remote_address.host_str()];
+ uint64_t fails = m_host_fails_score[ctx->m_remote_address.host_str()] += score;
MDEBUG("Host " << ctx->m_remote_address.host_str() << " fail score=" << fails);
if(fails > RPC_IP_FAILS_BEFORE_BLOCK)
{
@@ -239,7 +374,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_height);
+ RPC_TRACKER(get_height);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HEIGHT>(invoke_http_mode::JON, "/getheight", req, res, r))
return r;
@@ -254,7 +389,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_info);
+ RPC_TRACKER(get_info);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r))
{
@@ -272,6 +407,8 @@ namespace cryptonote
return r;
}
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_GET_INFO, false);
+
const bool restricted = m_restricted && ctx;
crypto::hash top_hash;
@@ -322,7 +459,7 @@ namespace cryptonote
if (restricted)
res.database_size = round_up(res.database_size, 5ull* 1024 * 1024 * 1024);
res.update_available = restricted ? false : m_core.is_update_available();
- res.version = restricted ? "" : MONERO_VERSION;
+ res.version = restricted ? "" : MONERO_VERSION_FULL;
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -330,7 +467,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_net_stats);
+ RPC_TRACKER(get_net_stats);
// No bootstrap daemon check: Only ever get stats about local server
res.start_time = (uint64_t)m_core.get_start_time();
{
@@ -357,26 +494,58 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_blocks);
+ RPC_TRACKER(get_blocks);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_FAST>(invoke_http_mode::BIN, "/getblocks.bin", req, res, r))
return r;
- std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
+ CHECK_PAYMENT(req, res, 1);
+
+ // quick check for noop
+ if (!req.block_ids.empty())
+ {
+ uint64_t last_block_height;
+ crypto::hash last_block_hash;
+ m_core.get_blockchain_top(last_block_height, last_block_hash);
+ if (last_block_hash == req.block_ids.front())
+ {
+ res.start_height = 0;
+ res.current_height = m_core.get_current_blockchain_height();
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ }
+
+ size_t max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT;
+ if (m_rpc_payment)
+ {
+ max_blocks = res.credits / COST_PER_BLOCK;
+ if (max_blocks > COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)
+ max_blocks = COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT;
+ if (max_blocks == 0)
+ {
+ res.status = CORE_RPC_STATUS_PAYMENT_REQUIRED;
+ return false;
+ }
+ }
- if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
+ std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata> > > > bs;
+ if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, max_blocks))
{
res.status = "Failed";
add_host_fail(ctx);
return false;
}
+ CHECK_PAYMENT_SAME_TS(req, res, bs.size() * COST_PER_BLOCK);
+
size_t pruned_size = 0, unpruned_size = 0, ntxes = 0;
res.blocks.reserve(bs.size());
res.output_indices.reserve(bs.size());
for(auto& bd: bs)
{
res.blocks.resize(res.blocks.size()+1);
+ res.blocks.back().pruned = req.prune;
res.blocks.back().block = bd.first.first;
pruned_size += bd.first.first.size();
unpruned_size += bd.first.first.size();
@@ -389,10 +558,10 @@ namespace cryptonote
for (std::vector<std::pair<crypto::hash, cryptonote::blobdata>>::iterator i = bd.second.begin(); i != bd.second.end(); ++i)
{
unpruned_size += i->second.size();
- res.blocks.back().txs.push_back(std::move(i->second));
+ res.blocks.back().txs.push_back({std::move(i->second), crypto::null_hash});
i->second.clear();
i->second.shrink_to_fit();
- pruned_size += res.blocks.back().txs.back().size();
+ pruned_size += res.blocks.back().txs.back().blob.size();
}
const size_t n_txes_to_lookup = bd.second.size() + (req.no_miner_tx ? 0 : 1);
@@ -421,7 +590,7 @@ namespace cryptonote
}
bool core_rpc_server::on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_alt_blocks_hashes);
+ RPC_TRACKER(get_alt_blocks_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_ALT_BLOCKS_HASHES>(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r))
return r;
@@ -448,7 +617,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_blocks_by_height);
+ RPC_TRACKER(get_blocks_by_height);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCKS_BY_HEIGHT>(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r))
return r;
@@ -456,6 +625,7 @@ namespace cryptonote
res.status = "Failed";
res.blocks.clear();
res.blocks.reserve(req.heights.size());
+ CHECK_PAYMENT_MIN1(req, res, req.heights.size() * COST_PER_BLOCK, false);
for (uint64_t height : req.heights)
{
block blk;
@@ -474,7 +644,7 @@ namespace cryptonote
res.blocks.resize(res.blocks.size() + 1);
res.blocks.back().block = block_to_blob(blk);
for (auto& tx : txs)
- res.blocks.back().txs.push_back(tx_to_blob(tx));
+ res.blocks.back().txs.push_back({tx_to_blob(tx), crypto::null_hash});
}
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -482,30 +652,36 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_hashes);
+ RPC_TRACKER(get_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HASHES_FAST>(invoke_http_mode::BIN, "/gethashes.bin", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
res.start_height = req.start_height;
- if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, res.start_height, res.current_height, false))
+ if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, NULL, res.start_height, res.current_height, false))
{
res.status = "Failed";
add_host_fail(ctx);
return false;
}
+ CHECK_PAYMENT_SAME_TS(req, res, res.m_block_ids.size() * COST_PER_BLOCK_HASH);
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_outs_bin);
+ RPC_TRACKER(get_outs_bin);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS_BIN>(invoke_http_mode::BIN, "/get_outs.bin", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, req.outputs.size() * COST_PER_OUT, false);
+
res.status = "Failed";
const bool restricted = m_restricted && ctx;
@@ -529,11 +705,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_outs);
+ RPC_TRACKER(get_outs);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUTS>(invoke_http_mode::JON, "/get_outs", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, req.outputs.size() * COST_PER_OUT, false);
+
res.status = "Failed";
const bool restricted = m_restricted && ctx;
@@ -573,11 +751,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_indexes);
+ RPC_TRACKER(get_indexes);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES>(invoke_http_mode::BIN, "/get_o_indexes.bin", req, res, ok))
return ok;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_OUTPUT_INDEXES, false);
+
bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes);
if(!r)
{
@@ -591,11 +771,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transactions);
+ RPC_TRACKER(get_transactions);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTIONS>(invoke_http_mode::JON, "/gettransactions", req, res, ok))
return ok;
+ CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false);
+
std::vector<crypto::hash> vh;
for(const auto& tx_hex_str: req.txs_hashes)
{
@@ -817,13 +999,16 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_is_key_image_spent);
+ RPC_TRACKER(is_key_image_spent);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_IS_KEY_IMAGE_SPENT>(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok))
return ok;
+ CHECK_PAYMENT_MIN1(req, res, req.key_images.size() * COST_PER_KEY_IMAGE, false);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
+
std::vector<crypto::key_image> key_images;
for(const auto& ki_hex_str: req.key_images)
{
@@ -886,12 +1071,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_send_raw_tx);
+ RPC_TRACKER(send_raw_tx);
bool ok;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_SEND_RAW_TX>(invoke_http_mode::JON, "/sendrawtransaction", req, res, ok))
return ok;
CHECK_CORE_READY();
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_RELAY, false);
std::string tx_blob;
if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob))
@@ -910,9 +1096,8 @@ namespace cryptonote
}
res.sanity_check_failed = false;
- cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
- tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed)
+ tx_verification_context tvc{};
+ if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, (req.do_not_relay ? relay_method::none : relay_method::local), false) || tvc.m_verifivation_failed)
{
res.status = "Failed";
std::string reason = "";
@@ -957,7 +1142,7 @@ namespace cryptonote
NOTIFY_NEW_TRANSACTIONS::request r;
r.txs.push_back(tx_blob);
- m_core.get_protocol()->relay_transactions(r, fake_context);
+ m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid);
//TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -965,7 +1150,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_start_mining);
+ RPC_TRACKER(start_mining);
CHECK_CORE_READY();
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, nettype(), req.miner_address))
@@ -1016,7 +1201,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_stop_mining);
+ RPC_TRACKER(stop_mining);
cryptonote::miner &miner= m_core.get_miner();
if(!miner.is_mining())
{
@@ -1036,7 +1221,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_mining_status(const COMMAND_RPC_MINING_STATUS::request& req, COMMAND_RPC_MINING_STATUS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_mining_status);
+ RPC_TRACKER(mining_status);
const miner& lMiner = m_core.get_miner();
res.active = lMiner.is_mining();
@@ -1050,7 +1235,8 @@ namespace cryptonote
res.block_reward = lMiner.get_block_reward();
}
const account_public_address& lMiningAdr = lMiner.get_mining_address();
- res.address = get_account_address_as_str(nettype(), false, lMiningAdr);
+ if (lMiner.is_mining() || lMiner.get_is_background_mining_enabled())
+ res.address = get_account_address_as_str(nettype(), false, lMiningAdr);
const uint8_t major_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
const unsigned variant = major_version >= 7 ? major_version - 6 : 0;
switch (variant)
@@ -1059,6 +1245,7 @@ namespace cryptonote
case 1: res.pow_algorithm = "CNv1 (Cryptonight variant 1)"; break;
case 2: case 3: res.pow_algorithm = "CNv2 (Cryptonight variant 2)"; break;
case 4: case 5: res.pow_algorithm = "CNv4 (Cryptonight variant 4)"; break;
+ case 6: res.pow_algorithm = "RandomX"; break;
default: res.pow_algorithm = "I'm not sure actually"; break;
}
if (res.is_background_mining_enabled)
@@ -1075,7 +1262,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_save_bc);
+ RPC_TRACKER(save_bc);
if( !m_core.get_blockchain_storage().store_blockchain() )
{
res.status = "Error while storing blockchain";
@@ -1087,7 +1274,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_peer_list);
+ RPC_TRACKER(get_peer_list);
std::vector<nodetool::peerlist_entry> white_list;
std::vector<nodetool::peerlist_entry> gray_list;
@@ -1104,24 +1291,24 @@ namespace cryptonote
{
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
- entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
- entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else
- res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
}
for (auto & entry : gray_list)
{
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
- entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
- entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
else
- res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port, entry.rpc_credits_per_hash);
}
res.status = CORE_RPC_STATUS_OK;
@@ -1130,7 +1317,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_public_nodes);
+ RPC_TRACKER(get_public_nodes);
COMMAND_RPC_GET_PEER_LIST::response peer_list_res;
const bool success = on_get_peer_list(COMMAND_RPC_GET_PEER_LIST::request(), peer_list_res, ctx);
@@ -1169,7 +1356,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_set_log_hash_rate);
+ RPC_TRACKER(set_log_hash_rate);
if(m_core.get_miner().is_mining())
{
m_core.get_miner().do_print_hashrate(req.visible);
@@ -1184,7 +1371,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_set_log_level);
+ RPC_TRACKER(set_log_level);
if (req.level < 0 || req.level > 4)
{
res.status = "Error: log level not valid";
@@ -1197,7 +1384,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, const connection_context *ctx)
{
- PERF_TIMER(on_set_log_categories);
+ RPC_TRACKER(set_log_categories);
mlog_set_log(req.categories.c_str());
res.categories = mlog_get_categories();
res.status = CORE_RPC_STATUS_OK;
@@ -1206,62 +1393,92 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool);
+ RPC_TRACKER(get_transaction_pool);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL>(invoke_http_mode::JON, "/get_transaction_pool", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
- m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !restricted);
- for (tx_info& txi : res.transactions)
- txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
+
+ size_t n_txes = m_core.get_pool_transactions_count();
+ if (n_txes > 0)
+ {
+ CHECK_PAYMENT_SAME_TS(req, res, n_txes * COST_PER_TX);
+ m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !restricted);
+ for (tx_info& txi : res.transactions)
+ txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
+ }
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool_hashes);
+ RPC_TRACKER(get_transaction_pool_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN>(invoke_http_mode::JON, "/get_transaction_pool_hashes.bin", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
- m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !restricted);
+
+ size_t n_txes = m_core.get_pool_transactions_count();
+ if (n_txes > 0)
+ {
+ CHECK_PAYMENT_SAME_TS(req, res, n_txes * COST_PER_POOL_HASH);
+ m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !restricted);
+ }
+
res.status = CORE_RPC_STATUS_OK;
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, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool_hashes);
+ RPC_TRACKER(get_transaction_pool_hashes);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_HASHES>(invoke_http_mode::JON, "/get_transaction_pool_hashes", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, 1);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
- std::vector<crypto::hash> tx_hashes;
- m_core.get_pool_transaction_hashes(tx_hashes, !request_has_rpc_origin || !restricted);
- res.tx_hashes.reserve(tx_hashes.size());
- for (const crypto::hash &tx_hash: tx_hashes)
- res.tx_hashes.push_back(epee::string_tools::pod_to_hex(tx_hash));
+
+ size_t n_txes = m_core.get_pool_transactions_count();
+ if (n_txes > 0)
+ {
+ CHECK_PAYMENT_SAME_TS(req, res, n_txes * COST_PER_POOL_HASH);
+ std::vector<crypto::hash> tx_hashes;
+ m_core.get_pool_transaction_hashes(tx_hashes, !request_has_rpc_origin || !restricted);
+ res.tx_hashes.reserve(tx_hashes.size());
+ for (const crypto::hash &tx_hash: tx_hashes)
+ res.tx_hashes.push_back(epee::string_tools::pod_to_hex(tx_hash));
+ }
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_transaction_pool_stats);
+ RPC_TRACKER(get_transaction_pool_stats);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_STATS>(invoke_http_mode::JON, "/get_transaction_pool_stats", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_POOL_STATS, false);
+
const bool restricted = m_restricted && ctx;
const bool request_has_rpc_origin = ctx != NULL;
m_core.get_pool_transaction_stats(res.pool_stats, !request_has_rpc_origin || !restricted);
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -1290,7 +1507,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_stop_daemon);
+ RPC_TRACKER(stop_daemon);
// FIXME: replace back to original m_p2p.send_stop_signal() after
// investigating why that isn't working quite right.
m_p2p.send_stop_signal();
@@ -1300,7 +1517,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_getblockcount);
+ RPC_TRACKER(getblockcount);
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (m_should_use_bootstrap_daemon)
@@ -1316,7 +1533,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_getblockhash);
+ RPC_TRACKER(getblockhash);
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (m_should_use_bootstrap_daemon)
@@ -1358,9 +1575,65 @@ namespace cryptonote
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
+ {
+ b = boost::value_initialized<cryptonote::block>();
+ if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to create block template");
+ return false;
+ }
+ blobdata block_blob = t_serializable_object_to_blob(b);
+ crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
+ if(tx_pub_key == crypto::null_pkey)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to get tx pub key in coinbase extra");
+ return false;
+ }
+
+ if (b.major_version >= RX_BLOCK_VERSION)
+ {
+ uint64_t next_height;
+ crypto::rx_seedheights(height, &seed_height, &next_height);
+ seed_hash = m_core.get_block_id_by_height(seed_height);
+ if (next_height != seed_height)
+ next_seed_hash = m_core.get_block_id_by_height(next_height);
+ else
+ next_seed_hash = seed_hash;
+ }
+
+ if (extra_nonce.empty())
+ {
+ reserved_offset = 0;
+ return true;
+ }
+
+ reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key));
+ if(!reserved_offset)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to find tx pub key in blockblob");
+ return false;
+ }
+ reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
+ if(reserved_offset + extra_nonce.size() > block_blob.size())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: failed to create block template";
+ LOG_ERROR("Failed to calculate offset for ");
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_getblocktemplate);
+ RPC_TRACKER(getblocktemplate);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GETBLOCKTEMPLATE>(invoke_http_mode::JON_RPC, "getblocktemplate", req, res, r))
return r;
@@ -1410,6 +1683,7 @@ namespace cryptonote
block b;
cryptonote::blobdata blob_reserve;
+ size_t reserved_offset;
if(!req.extra_nonce.empty())
{
if(!string_tools::parse_hexstr_to_binbuff(req.extra_nonce, blob_reserve))
@@ -1432,42 +1706,20 @@ namespace cryptonote
return false;
}
}
- if(!m_core.get_block_template(b, req.prev_block.empty() ? NULL : &prev_block, info.address, wdiff, 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";
- LOG_ERROR("Failed to create block template");
+ uint64_t seed_height;
+ crypto::hash seed_hash, next_seed_hash;
+ if (!get_block_template(info.address, req.prev_block.empty() ? NULL : &prev_block, blob_reserve, reserved_offset, wdiff, res.height, res.expected_reward, b, res.seed_height, seed_hash, next_seed_hash, error_resp))
return false;
+ if (b.major_version >= RX_BLOCK_VERSION)
+ {
+ res.seed_hash = string_tools::pod_to_hex(seed_hash);
+ if (seed_hash != next_seed_hash)
+ res.next_seed_hash = string_tools::pod_to_hex(next_seed_hash);
}
+
+ res.reserved_offset = reserved_offset;
store_difficulty(wdiff, res.difficulty, res.wide_difficulty, res.difficulty_top64);
blobdata block_blob = t_serializable_object_to_blob(b);
- crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
- if(tx_pub_key == crypto::null_pkey)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to get tx pub key in coinbase extra");
- return false;
- }
- res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key));
- if(!res.reserved_offset)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to find tx pub key in blockblob");
- return false;
- }
- if (req.reserve_size)
- res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
- else
- res.reserved_offset = 0;
- if(res.reserved_offset + req.reserve_size > block_blob.size())
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: failed to create block template";
- LOG_ERROR("Failed to calculate offset for ");
- return false;
- }
blobdata hashing_blob = get_block_hashing_blob(b);
res.prev_hash = string_tools::pod_to_hex(b.prev_id);
res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob);
@@ -1478,7 +1730,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_submitblock);
+ RPC_TRACKER(submitblock);
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (m_should_use_bootstrap_daemon)
@@ -1534,7 +1786,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_generateblocks(const COMMAND_RPC_GENERATEBLOCKS::request& req, COMMAND_RPC_GENERATEBLOCKS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_generateblocks);
+ RPC_TRACKER(generateblocks);
CHECK_CORE_READY();
@@ -1555,7 +1807,7 @@ namespace cryptonote
template_req.reserve_size = 1;
template_req.wallet_address = req.wallet_address;
template_req.prev_block = req.prev_block;
- submit_req.push_back(boost::value_initialized<std::string>());
+ submit_req.push_back(std::string{});
res.height = m_core.get_blockchain_storage().get_current_blockchain_height();
for(size_t i = 0; i < req.amount_of_blocks; i++)
@@ -1581,7 +1833,9 @@ namespace cryptonote
return false;
}
b.nonce = req.starting_nonce;
- miner::find_nonce_for_given_block(b, template_res.difficulty, template_res.height);
+ miner::find_nonce_for_given_block([this](const cryptonote::block &b, uint64_t height, unsigned int threads, crypto::hash &hash) {
+ return cryptonote::get_block_longhash(&(m_core.get_blockchain_storage()), b, hash, height, threads);
+ }, b, template_res.difficulty, template_res.height);
submit_req.front() = string_tools::buff_to_hex_nodelimer(block_to_blob(b));
r = on_submitblock(submit_req, submit_res, error_resp, ctx);
@@ -1626,7 +1880,7 @@ namespace cryptonote
response.reward = get_block_reward(blk);
response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height);
response.num_txes = blk.tx_hashes.size();
- response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : "";
+ response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(&(m_core.get_blockchain_storage()), blk, height, 0)) : "";
response.long_term_weight = m_core.get_blockchain_storage().get_db().get_block_long_term_weight(height);
response.miner_tx_hash = string_tools::pod_to_hex(cryptonote::get_transaction_hash(blk.miner_tx));
return true;
@@ -1651,7 +1905,8 @@ namespace cryptonote
}
auto current_time = std::chrono::system_clock::now();
- if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s
+ if (!m_p2p.get_payload_object().no_sync() &&
+ current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s
{
{
boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock);
@@ -1675,9 +1930,10 @@ namespace cryptonote
uint64_t top_height = m_core.get_current_blockchain_height();
m_should_use_bootstrap_daemon = top_height + 10 < *bootstrap_daemon_height;
MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << *bootstrap_daemon_height << ")");
+
+ if (!m_should_use_bootstrap_daemon)
+ return false;
}
- if (!m_should_use_bootstrap_daemon)
- return false;
if (mode == invoke_http_mode::JON)
{
@@ -1709,12 +1965,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_last_block_header);
+ RPC_TRACKER(get_last_block_header);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LAST_BLOCK_HEADER>(invoke_http_mode::JON_RPC, "getlastblockheader", req, res, r))
return r;
CHECK_CORE_READY();
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
uint64_t last_block_height;
crypto::hash last_block_hash;
m_core.get_blockchain_top(last_block_height, last_block_hash);
@@ -1740,11 +1997,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block_header_by_hash);
+ RPC_TRACKER(get_block_header_by_hash);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH>(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
+
auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool {
crypto::hash block_hash;
bool hash_parsed = parse_hash256(hash, block_hash);
@@ -1800,7 +2059,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block_headers_range(const COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request& req, COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block_headers_range);
+ RPC_TRACKER(get_block_headers_range);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADERS_RANGE>(invoke_http_mode::JON_RPC, "getblockheadersrange", req, res, r))
return r;
@@ -1812,6 +2071,7 @@ namespace cryptonote
error_resp.message = "Invalid start/end heights.";
return false;
}
+ CHECK_PAYMENT_MIN1(req, res, (req.end_height - req.start_height + 1) * COST_PER_BLOCK_HEADER, false);
for (uint64_t h = req.start_height; h <= req.end_height; ++h)
{
crypto::hash block_hash = m_core.get_block_id_by_height(h);
@@ -1852,7 +2112,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block_header_by_height);
+ RPC_TRACKER(get_block_header_by_height);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT>(invoke_http_mode::JON_RPC, "getblockheaderbyheight", req, res, r))
return r;
@@ -1863,6 +2123,7 @@ namespace cryptonote
error_resp.message = std::string("Requested block height: ") + std::to_string(req.height) + " greater than current top block height: " + std::to_string(m_core.get_current_blockchain_height() - 1);
return false;
}
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK_HEADER, false);
crypto::hash block_hash = m_core.get_block_id_by_height(req.height);
block blk;
bool have_block = m_core.get_block_by_hash(block_hash, blk);
@@ -1886,11 +2147,13 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_block(const COMMAND_RPC_GET_BLOCK::request& req, COMMAND_RPC_GET_BLOCK::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_block);
+ RPC_TRACKER(get_block);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK>(invoke_http_mode::JON_RPC, "getblock", req, res, r))
return r;
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_BLOCK, false);
+
crypto::hash block_hash;
if (!req.hash.empty())
{
@@ -1936,7 +2199,6 @@ namespace cryptonote
error_resp.message = "Internal error: can't produce valid response.";
return false;
}
- res.miner_tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(blk.miner_tx));
for (size_t n = 0; n < blk.tx_hashes.size(); ++n)
{
res.tx_hashes.push_back(epee::string_tools::pod_to_hex(blk.tx_hashes[n]));
@@ -1949,7 +2211,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_connections);
+ RPC_TRACKER(get_connections);
res.connections = m_p2p.get_payload_object().get_connections();
@@ -1960,16 +2222,24 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- return on_get_info(req, res, ctx);
+ on_get_info(req, res, ctx);
+ if (res.status != CORE_RPC_STATUS_OK)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = res.status;
+ return false;
+ }
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_hard_fork_info);
+ RPC_TRACKER(hard_fork_info);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_HARD_FORK_INFO>(invoke_http_mode::JON_RPC, "hard_fork_info", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, COST_PER_HARD_FORK_INFO);
const Blockchain &blockchain = m_core.get_blockchain_storage();
uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version();
res.version = blockchain.get_current_hard_fork_version();
@@ -1981,15 +2251,15 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_bans);
+ RPC_TRACKER(get_bans);
auto now = time(nullptr);
- std::map<epee::net_utils::network_address, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
- for (std::map<epee::net_utils::network_address, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
+ std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
+ for (std::map<std::string, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
{
if (i->second > now) {
COMMAND_RPC_GETBANS::ban b;
- b.host = i->first.host_str();
+ b.host = i->first;
b.ip = 0;
uint32_t ip;
if (epee::string_tools::get_ip_int32_from_string(ip, b.host))
@@ -2045,7 +2315,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_set_bans);
+ RPC_TRACKER(set_bans);
for (auto i = req.bans.begin(); i != req.bans.end(); ++i)
{
@@ -2093,14 +2363,14 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_flush_txpool);
+ RPC_TRACKER(flush_txpool);
bool failed = false;
std::vector<crypto::hash> txids;
if (req.txids.empty())
{
std::vector<transaction> pool_txs;
- bool r = m_core.get_pool_transactions(pool_txs);
+ bool r = m_core.get_pool_transactions(pool_txs, true);
if (!r)
{
res.status = "Failed to get txpool contents";
@@ -2148,12 +2418,22 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_output_histogram);
+ RPC_TRACKER(get_output_histogram);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_HISTOGRAM>(invoke_http_mode::JON_RPC, "get_output_histogram", req, res, r))
return r;
const bool restricted = m_restricted && ctx;
+ size_t amounts = req.amounts.size();
+ if (restricted && amounts == 0)
+ {
+ res.status = "Restricted RPC will not serve histograms on the whole blockchain. Use your own node.";
+ return true;
+ }
+
+ uint64_t cost = req.amounts.empty() ? COST_PER_FULL_OUTPUT_HISTOGRAM : (COST_PER_OUTPUT_HISTOGRAM * amounts);
+ CHECK_PAYMENT_MIN1(req, res, cost, false);
+
if (restricted && req.recent_cutoff > 0 && req.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION)
{
res.status = "Recent cutoff is too old";
@@ -2185,7 +2465,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_version);
+ RPC_TRACKER(get_version);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_VERSION>(invoke_http_mode::JON_RPC, "get_version", req, res, r))
return r;
@@ -2198,7 +2478,14 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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, const connection_context *ctx)
{
- PERF_TIMER(on_get_coinbase_tx_sum);
+ RPC_TRACKER(get_coinbase_tx_sum);
+ const uint64_t bc_height = m_core.get_current_blockchain_height();
+ if (req.height >= bc_height || req.count > bc_height)
+ {
+ res.status = "height or count is too large";
+ return true;
+ }
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_COINBASE_TX_SUM_BLOCK * req.count, false);
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;
@@ -2208,11 +2495,12 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_base_fee_estimate);
+ RPC_TRACKER(get_base_fee_estimate);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BASE_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r))
return r;
+ CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE);
res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks);
res.quantization_mask = Blockchain::get_fee_quantization_mask();
res.status = CORE_RPC_STATUS_OK;
@@ -2221,7 +2509,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::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, const connection_context *ctx)
{
- PERF_TIMER(on_get_alternate_chains);
+ RPC_TRACKER(get_alternate_chains);
try
{
std::vector<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
@@ -2254,7 +2542,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_limit);
+ RPC_TRACKER(get_limit);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_LIMIT>(invoke_http_mode::JON, "/get_limit", req, res, r))
return r;
@@ -2267,7 +2555,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_set_limit);
+ RPC_TRACKER(set_limit);
// -1 = reset to default
// 0 = do not modify
@@ -2307,7 +2595,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_out_peers);
+ RPC_TRACKER(out_peers);
if (req.set)
m_p2p.change_max_out_public_peers(req.out_peers);
res.out_peers = m_p2p.get_max_out_public_peers();
@@ -2317,7 +2605,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_in_peers);
+ RPC_TRACKER(in_peers);
if (req.set)
m_p2p.change_max_in_public_peers(req.in_peers);
res.in_peers = m_p2p.get_max_in_public_peers();
@@ -2327,7 +2615,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_update);
+ RPC_TRACKER(update);
if (m_core.offline())
{
@@ -2429,7 +2717,7 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_pop_blocks);
+ RPC_TRACKER(pop_blocks);
m_core.get_blockchain_storage().pop_blocks(req.nblocks);
@@ -2441,7 +2729,8 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
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, const connection_context *ctx)
{
- PERF_TIMER(on_relay_tx);
+ RPC_TRACKER(relay_tx);
+ CHECK_PAYMENT_MIN1(req, res, req.txids.size() * COST_PER_TX_RELAY, false);
bool failed = false;
res.status = "";
@@ -2458,13 +2747,11 @@ namespace cryptonote
crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
cryptonote::blobdata txblob;
- bool r = m_core.get_pool_transaction(txid, txblob);
- if (r)
+ if (!m_core.get_pool_transaction(txid, txblob, relay_category::legacy))
{
- cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
NOTIFY_NEW_TRANSACTIONS::request r;
r.txs.push_back(txblob);
- m_core.get_protocol()->relay_transactions(r, fake_context);
+ m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid);
//TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
}
else
@@ -2487,7 +2774,8 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_sync_info);
+ RPC_TRACKER(sync_info);
+ CHECK_PAYMENT(req, res, COST_PER_SYNC_INFO);
crypto::hash top_hash;
m_core.get_blockchain_top(res.height, top_hash);
@@ -2516,10 +2804,12 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_txpool_backlog);
+ RPC_TRACKER(get_txpool_backlog);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG>(invoke_http_mode::JON_RPC, "get_txpool_backlog", req, res, r))
return r;
+ size_t n_txes = m_core.get_pool_transactions_count();
+ CHECK_PAYMENT_MIN1(req, res, COST_PER_TX_POOL_STATS * n_txes, false);
if (!m_core.get_txpool_backlog(res.backlog))
{
@@ -2534,11 +2824,16 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_output_distribution(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
- PERF_TIMER(on_get_output_distribution);
+ RPC_TRACKER(get_output_distribution);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_DISTRIBUTION>(invoke_http_mode::JON_RPC, "get_output_distribution", req, res, r))
return r;
+ size_t n_0 = 0, n_non0 = 0;
+ for (uint64_t amount: req.amounts)
+ if (amount) ++n_non0; else ++n_0;
+ CHECK_PAYMENT_MIN1(req, res, n_0 * COST_PER_OUTPUT_DISTRIBUTION_0 + n_non0 * COST_PER_OUTPUT_DISTRIBUTION, false);
+
try
{
// 0 is placeholder for the whole chain
@@ -2569,12 +2864,17 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx)
{
- PERF_TIMER(on_get_output_distribution_bin);
+ RPC_TRACKER(get_output_distribution_bin);
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_DISTRIBUTION>(invoke_http_mode::BIN, "/get_output_distribution.bin", req, res, r))
return r;
+ size_t n_0 = 0, n_non0 = 0;
+ for (uint64_t amount: req.amounts)
+ if (amount) ++n_non0; else ++n_0;
+ CHECK_PAYMENT_MIN1(req, res, n_0 * COST_PER_OUTPUT_DISTRIBUTION_0 + n_non0 * COST_PER_OUTPUT_DISTRIBUTION, false);
+
res.status = "Failed";
if (!req.binary)
@@ -2610,6 +2910,8 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_prune_blockchain(const COMMAND_RPC_PRUNE_BLOCKCHAIN::request& req, COMMAND_RPC_PRUNE_BLOCKCHAIN::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
+ RPC_TRACKER(prune_blockchain);
+
try
{
if (!(req.check ? m_core.check_blockchain_pruning() : m_core.prune_blockchain()))
@@ -2627,13 +2929,256 @@ namespace cryptonote
error_resp.message = "Failed to prune blockchain";
return false;
}
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_info(const COMMAND_RPC_ACCESS_INFO::request& req, COMMAND_RPC_ACCESS_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_info);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_INFO>(invoke_http_mode::JON, "rpc_access_info", req, res, r))
+ return r;
+
+ // if RPC payment is not enabled
+ if (m_rpc_payment == NULL)
+ {
+ res.diff = 0;
+ res.credits_per_hash_found = 0;
+ res.credits = 0;
+ res.height = 0;
+ res.seed_height = 0;
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+
+ crypto::public_key client;
+ uint64_t ts;
+ if (!cryptonote::verify_rpc_payment_signature(req.client, client, ts))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ crypto::hash top_hash;
+ m_core.get_blockchain_top(res.height, top_hash);
+ ++res.height;
+ cryptonote::blobdata hashing_blob;
+ crypto::hash seed_hash, next_seed_hash;
+ if (!m_rpc_payment->get_info(client, [&](const cryptonote::blobdata &extra_nonce, cryptonote::block &b, uint64_t &seed_height, crypto::hash &seed_hash)->bool{
+ cryptonote::difficulty_type difficulty;
+ uint64_t height, expected_reward;
+ size_t reserved_offset;
+ if (!get_block_template(m_rpc_payment->get_payment_address(), NULL, extra_nonce, reserved_offset, difficulty, height, expected_reward, b, seed_height, seed_hash, next_seed_hash, error_resp))
+ return false;
+ return true;
+ }, hashing_blob, res.seed_height, seed_hash, top_hash, res.diff, res.credits_per_hash_found, res.credits, res.cookie))
+ {
+ return false;
+ }
+ if (hashing_blob.empty())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
+ error_resp.message = "Invalid hashing blob";
+ return false;
+ }
+ res.hashing_blob = epee::string_tools::buff_to_hex_nodelimer(hashing_blob);
+ res.top_hash = epee::string_tools::pod_to_hex(top_hash);
+ if (hashing_blob[0] >= RX_BLOCK_VERSION)
+ {
+ res.seed_hash = string_tools::pod_to_hex(seed_hash);
+ if (seed_hash != next_seed_hash)
+ res.next_seed_hash = string_tools::pod_to_hex(next_seed_hash);
+ }
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_flush_cache(const COMMAND_RPC_FLUSH_CACHE::request& req, COMMAND_RPC_FLUSH_CACHE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(flush_cache);
+ if (req.bad_txs)
+ m_core.flush_bad_txs_cache();
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_submit_nonce(const COMMAND_RPC_ACCESS_SUBMIT_NONCE::request& req, COMMAND_RPC_ACCESS_SUBMIT_NONCE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_submit_nonce);
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_SUBMIT_NONCE>(invoke_http_mode::JON, "rpc_access_submit_nonce", req, res, r))
+ return r;
+
+ // if RPC payment is not enabled
+ if (m_rpc_payment == NULL)
+ {
+ res.status = "Payment not necessary";
+ return true;
+ }
+
+ crypto::public_key client;
+ uint64_t ts;
+ if (!cryptonote::verify_rpc_payment_signature(req.client, client, ts))
+ {
+ res.credits = 0;
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ crypto::hash hash;
+ cryptonote::block block;
+ crypto::hash top_hash;
+ uint64_t height;
+ bool stale;
+ m_core.get_blockchain_top(height, top_hash);
+ if (!m_rpc_payment->submit_nonce(client, req.nonce, top_hash, error_resp.code, error_resp.message, res.credits, hash, block, req.cookie, stale))
+ {
+ return false;
+ }
+
+ if (!stale)
+ {
+ // it might be a valid block!
+ const difficulty_type current_difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block();
+ if (check_hash(hash, current_difficulty))
+ {
+ MINFO("This payment meets the current network difficulty");
+ block_verification_context bvc;
+ if(m_core.handle_block_found(block, bvc))
+ MGINFO_GREEN("Block found by RPC user at height " << get_block_height(block) << ": " <<
+ print_money(cryptonote::get_outs_money_amount(block.miner_tx)));
+ else
+ MERROR("Seemingly valid block was not accepted");
+ }
+ }
+ m_core.get_blockchain_top(height, top_hash);
+ res.top_hash = epee::string_tools::pod_to_hex(top_hash);
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_pay(const COMMAND_RPC_ACCESS_PAY::request& req, COMMAND_RPC_ACCESS_PAY::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_pay);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_PAY>(invoke_http_mode::JON, "rpc_access_pay", req, res, r))
+ return r;
+
+ // if RPC payment is not enabled
+ if (m_rpc_payment == NULL)
+ {
+ res.status = "Payment not necessary";
+ return true;
+ }
+
+ crypto::public_key client;
+ uint64_t ts;
+ if (!cryptonote::verify_rpc_payment_signature(req.client, client, ts))
+ {
+ res.credits = 0;
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ RPCTracker ext_tracker(("external:" + req.paying_for).c_str(), PERF_TIMER_NAME(rpc_access_pay));
+ if (!check_payment(req.client, req.payment, req.paying_for, false, res.status, res.credits, res.top_hash))
+ return true;
+ ext_tracker.pay(req.payment);
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_tracking(const COMMAND_RPC_ACCESS_TRACKING::request& req, COMMAND_RPC_ACCESS_TRACKING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_tracking);
+
+ if (req.clear)
+ {
+ RPCTracker::clear();
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+
+ auto data = RPCTracker::data();
+ for (const auto &d: data)
+ {
+ res.data.resize(res.data.size() + 1);
+ res.data.back().rpc = d.first;
+ res.data.back().count = d.second.count;
+ res.data.back().time = d.second.time;
+ res.data.back().credits = d.second.credits;
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_data(const COMMAND_RPC_ACCESS_DATA::request& req, COMMAND_RPC_ACCESS_DATA::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_data);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_DATA>(invoke_http_mode::JON, "rpc_access_data", req, res, r))
+ return r;
+
+ if (!m_rpc_payment)
+ {
+ res.status = "Payments not enabled";
+ return false;
+ }
+
+ m_rpc_payment->foreach([&](const crypto::public_key &client, const rpc_payment::client_info &info){
+ res.entries.push_back({
+ epee::string_tools::pod_to_hex(client), info.credits, std::max(info.last_request_timestamp / 1000000, info.update_time),
+ info.credits_total, info.credits_used, info.nonces_good, info.nonces_stale, info.nonces_bad, info.nonces_dupe
+ });
+ return true;
+ });
+
+ res.hashrate = m_rpc_payment->get_hashes(600) / 600;
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_rpc_access_account(const COMMAND_RPC_ACCESS_ACCOUNT::request& req, COMMAND_RPC_ACCESS_ACCOUNT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ RPC_TRACKER(rpc_access_account);
+
+ bool r;
+ if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_ACCESS_ACCOUNT>(invoke_http_mode::JON, "rpc_access_account", req, res, r))
+ return r;
+
+ if (!m_rpc_payment)
+ {
+ res.status = "Payments not enabled";
+ return false;
+ }
+
+ crypto::public_key client;
+ if (!epee::string_tools::hex_to_pod(req.client.substr(0, 2 * sizeof(client)), client))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
+ error_resp.message = "Invalid client ID";
+ return false;
+ }
+
+ res.credits = m_rpc_payment->balance(client, req.delta_balance);
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
const command_line::arg_descriptor<std::string, false, true, 2> core_rpc_server::arg_rpc_bind_port = {
"rpc-bind-port"
, "Port for RPC server"
@@ -2672,4 +3217,22 @@ namespace cryptonote
, "Specify username:password for the bootstrap daemon login"
, ""
};
+
+ const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_payment_address = {
+ "rpc-payment-address"
+ , "Restrict RPC to clients sending micropayment to this address"
+ , ""
+ };
+
+ const command_line::arg_descriptor<uint64_t> core_rpc_server::arg_rpc_payment_difficulty = {
+ "rpc-payment-difficulty"
+ , "Restrict RPC to clients sending micropayment at this difficulty"
+ , DEFAULT_PAYMENT_DIFFICULTY
+ };
+
+ const command_line::arg_descriptor<uint64_t> core_rpc_server::arg_rpc_payment_credits = {
+ "rpc-payment-credits"
+ , "Restrict RPC to clients sending micropayment, yields that many credits per payment"
+ , DEFAULT_PAYMENT_CREDITS_PER_HASH
+ };
} // namespace cryptonote
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 379f6ed28..23c611470 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -42,6 +42,7 @@
#include "cryptonote_core/cryptonote_core.h"
#include "p2p/net_node.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
+#include "rpc_payment.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc"
@@ -71,6 +72,9 @@ namespace cryptonote
static const command_line::arg_descriptor<bool> arg_rpc_ssl_allow_any_cert;
static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_address;
static const command_line::arg_descriptor<std::string> arg_bootstrap_daemon_login;
+ static const command_line::arg_descriptor<std::string> arg_rpc_payment_address;
+ static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_difficulty;
+ static const command_line::arg_descriptor<uint64_t> arg_rpc_payment_credits;
typedef epee::net_utils::connection_context_base connection_context;
@@ -78,6 +82,7 @@ namespace cryptonote
core& cr
, nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& p2p
);
+ ~core_rpc_server();
static void init_options(boost::program_options::options_description& desc);
bool init(
@@ -111,7 +116,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC, !m_restricted)
MAP_URI_AUTO_JON2_IF("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST, !m_restricted)
- MAP_URI_AUTO_JON2_IF("/get_public_nodes", on_get_public_nodes, COMMAND_RPC_GET_PUBLIC_NODES, !m_restricted)
+ MAP_URI_AUTO_JON2("/get_public_nodes", on_get_public_nodes, COMMAND_RPC_GET_PUBLIC_NODES)
MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted)
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)
@@ -169,6 +174,13 @@ namespace cryptonote
MAP_JON_RPC_WE("get_txpool_backlog", on_get_txpool_backlog, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG)
MAP_JON_RPC_WE("get_output_distribution", on_get_output_distribution, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
MAP_JON_RPC_WE_IF("prune_blockchain", on_prune_blockchain, COMMAND_RPC_PRUNE_BLOCKCHAIN, !m_restricted)
+ MAP_JON_RPC_WE_IF("flush_cache", on_flush_cache, COMMAND_RPC_FLUSH_CACHE, !m_restricted)
+ MAP_JON_RPC_WE("rpc_access_info", on_rpc_access_info, COMMAND_RPC_ACCESS_INFO)
+ MAP_JON_RPC_WE("rpc_access_submit_nonce",on_rpc_access_submit_nonce, COMMAND_RPC_ACCESS_SUBMIT_NONCE)
+ MAP_JON_RPC_WE("rpc_access_pay", on_rpc_access_pay, COMMAND_RPC_ACCESS_PAY)
+ MAP_JON_RPC_WE_IF("rpc_access_tracking", on_rpc_access_tracking, COMMAND_RPC_ACCESS_TRACKING, !m_restricted)
+ MAP_JON_RPC_WE_IF("rpc_access_data", on_rpc_access_data, COMMAND_RPC_ACCESS_DATA, !m_restricted)
+ MAP_JON_RPC_WE_IF("rpc_access_account", on_rpc_access_account, COMMAND_RPC_ACCESS_ACCOUNT, !m_restricted)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -236,12 +248,19 @@ namespace cryptonote
bool on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_output_distribution(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_prune_blockchain(const COMMAND_RPC_PRUNE_BLOCKCHAIN::request& req, COMMAND_RPC_PRUNE_BLOCKCHAIN::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_flush_cache(const COMMAND_RPC_FLUSH_CACHE::request& req, COMMAND_RPC_FLUSH_CACHE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_rpc_access_info(const COMMAND_RPC_ACCESS_INFO::request& req, COMMAND_RPC_ACCESS_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_rpc_access_submit_nonce(const COMMAND_RPC_ACCESS_SUBMIT_NONCE::request& req, COMMAND_RPC_ACCESS_SUBMIT_NONCE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_rpc_access_pay(const COMMAND_RPC_ACCESS_PAY::request& req, COMMAND_RPC_ACCESS_PAY::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_rpc_access_tracking(const COMMAND_RPC_ACCESS_TRACKING::request& req, COMMAND_RPC_ACCESS_TRACKING::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_rpc_access_data(const COMMAND_RPC_ACCESS_DATA::request& req, COMMAND_RPC_ACCESS_DATA::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_rpc_access_account(const COMMAND_RPC_ACCESS_ACCOUNT::request& req, COMMAND_RPC_ACCESS_ACCOUNT::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
//-----------------------
private:
bool check_core_busy();
bool check_core_ready();
- bool add_host_fail(const connection_context *ctx);
+ bool add_host_fail(const connection_context *ctx, unsigned int score = 1);
//utils
uint64_t get_block_reward(const block& blk);
@@ -252,6 +271,8 @@ private:
enum invoke_http_mode { JON, BIN, JON_RPC };
template <typename COMMAND_TYPE>
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
+ bool get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp);
+ bool check_payment(const std::string &client, uint64_t payment, const std::string &rpc, bool same_ts, std::string &message, uint64_t &credits, std::string &top_hash);
core& m_core;
nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >& m_p2p;
@@ -260,10 +281,10 @@ private:
bool m_should_use_bootstrap_daemon;
std::chrono::system_clock::time_point m_bootstrap_height_check_time;
bool m_was_bootstrap_ever_used;
- network_type m_nettype;
bool m_restricted;
epee::critical_section m_host_fails_score_lock;
std::map<std::string, uint64_t> m_host_fails_score;
+ std::unique_ptr<rpc_payment> m_rpc_payment;
};
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index 325ac4343..855ea854c 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -78,6 +78,7 @@ namespace cryptonote
#define CORE_RPC_STATUS_OK "OK"
#define CORE_RPC_STATUS_BUSY "BUSY"
#define CORE_RPC_STATUS_NOT_MINING "NOT MINING"
+#define CORE_RPC_STATUS_PAYMENT_REQUIRED "PAYMENT REQUIRED"
// When making *any* change here, bump minor
// If the change is incompatible, then bump major and set minor to 0
@@ -86,31 +87,72 @@ namespace cryptonote
// whether they can talk to a given daemon without having to know in
// advance which version they will stop working with
// Don't go over 32767 for any of these
-#define CORE_RPC_VERSION_MAJOR 2
-#define CORE_RPC_VERSION_MINOR 10
+#define CORE_RPC_VERSION_MAJOR 3
+#define CORE_RPC_VERSION_MINOR 0
#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)
+ struct rpc_request_base
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct rpc_response_base
+ {
+ std::string status;
+ bool untrusted;
+
+ rpc_response_base(): untrusted(false) {}
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(untrusted)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct rpc_access_request_base: public rpc_request_base
+ {
+ std::string client;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ KV_SERIALIZE(client)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct rpc_access_response_base: public rpc_response_base
+ {
+ uint64_t credits;
+ std::string top_hash;
+
+ rpc_access_response_base(): credits(0) {}
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
+ KV_SERIALIZE(credits)
+ KV_SERIALIZE(top_hash)
+ END_KV_SERIALIZE_MAP()
+ };
+
struct COMMAND_RPC_GET_HEIGHT
{
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
uint64_t height;
- std::string status;
- bool untrusted;
std::string hash;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(height)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
KV_SERIALIZE(hash)
END_KV_SERIALIZE_MAP()
};
@@ -120,13 +162,14 @@ namespace cryptonote
struct COMMAND_RPC_GET_BLOCKS_FAST
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
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;
bool no_miner_tx;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE(start_height)
KV_SERIALIZE(prune)
@@ -153,22 +196,19 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<block_complete_entry> blocks;
uint64_t start_height;
uint64_t current_height;
- std::string status;
std::vector<block_output_indices> output_indices;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(blocks)
KV_SERIALIZE(start_height)
KV_SERIALIZE(current_height)
- KV_SERIALIZE(status)
KV_SERIALIZE(output_indices)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -176,25 +216,23 @@ namespace cryptonote
struct COMMAND_RPC_GET_BLOCKS_BY_HEIGHT
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<uint64_t> heights;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(heights)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<block_complete_entry> blocks;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(blocks)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -202,23 +240,21 @@ namespace cryptonote
struct COMMAND_RPC_GET_ALT_BLOCKS_HASHES
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<std::string> blks_hashes;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(blks_hashes)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -226,85 +262,33 @@ namespace cryptonote
struct COMMAND_RPC_GET_HASHES_FAST
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
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;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids)
KV_SERIALIZE(start_height)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<crypto::hash> m_block_ids;
uint64_t start_height;
uint64_t current_height;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids)
KV_SERIALIZE(start_height)
KV_SERIALIZE(current_height)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
-
- //-----------------------------------------------
- struct COMMAND_RPC_GET_RANDOM_OUTS
- {
- struct request_t
- {
- std::vector<std::string> amounts;
- uint32_t count;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(amounts)
- KV_SERIALIZE(count)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
-
- struct output {
- std::string public_key;
- uint64_t global_index;
- std::string rct; // 64+64+64 characters long (<rct commit> + <encrypted mask> + <rct amount>)
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(public_key)
- KV_SERIALIZE(global_index)
- KV_SERIALIZE(rct)
- END_KV_SERIALIZE_MAP()
- };
-
- struct amount_out {
- uint64_t amount;
- std::vector<output> outputs;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(amount)
- KV_SERIALIZE(outputs)
- END_KV_SERIALIZE_MAP()
-
- };
-
- struct response_t
- {
- std::vector<amount_out> amount_outs;
- std::string Error;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(amount_outs)
- KV_SERIALIZE(Error)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
//-----------------------------------------------
struct COMMAND_RPC_SUBMIT_RAW_TX
{
@@ -338,7 +322,7 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_GET_TRANSACTIONS
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<std::string> txs_hashes;
bool decode_as_json;
@@ -346,6 +330,7 @@ namespace cryptonote
bool split;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(txs_hashes)
KV_SERIALIZE(decode_as_json)
KV_SERIALIZE_OPT(prune, false)
@@ -391,7 +376,7 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct response_t
+ struct response_t: public rpc_access_response_base
{
// older compatibility stuff
std::vector<std::string> txs_as_hex; //transactions blobs as hex (old compat)
@@ -402,16 +387,13 @@ namespace cryptonote
// new style
std::vector<entry> txs;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(txs_as_hex)
KV_SERIALIZE(txs_as_json)
KV_SERIALIZE(txs)
KV_SERIALIZE(missed_tx)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -426,27 +408,25 @@ namespace cryptonote
SPENT_IN_POOL = 2,
};
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<std::string> key_images;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(key_images)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<int> spent_status;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(spent_status)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -455,25 +435,24 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
crypto::hash txid;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE_VAL_POD_AS_BLOB(txid)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<uint64_t> o_indexes;
- std::string status;
- bool untrusted;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(o_indexes)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -492,12 +471,13 @@ namespace cryptonote
struct COMMAND_RPC_GET_OUTPUTS_BIN
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<get_outputs_out> outputs;
bool get_txid;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(outputs)
KV_SERIALIZE_OPT(get_txid, true)
END_KV_SERIALIZE_MAP()
@@ -521,16 +501,13 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<outkey> outs;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(outs)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -538,12 +515,13 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_GET_OUTPUTS
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<get_outputs_out> outputs;
bool get_txid;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(outputs)
KV_SERIALIZE(get_txid)
END_KV_SERIALIZE_MAP()
@@ -567,16 +545,13 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct response_t
+ struct response_t: public rpc_access_response_base
{
std::vector<outkey> outs;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(outs)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -584,13 +559,14 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_SEND_RAW_TX
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::string tx_as_hex;
bool do_not_relay;
bool do_sanity_checks;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base);
KV_SERIALIZE(tx_as_hex)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(do_sanity_checks, true)
@@ -599,9 +575,8 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::string reason;
bool not_relayed;
bool low_mixin;
@@ -614,10 +589,9 @@ namespace cryptonote
bool not_rct;
bool too_few_outputs;
bool sanity_check_failed;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(reason)
KV_SERIALIZE(not_relayed)
KV_SERIALIZE(low_mixin)
@@ -630,7 +604,6 @@ namespace cryptonote
KV_SERIALIZE(not_rct)
KV_SERIALIZE(too_few_outputs)
KV_SERIALIZE(sanity_check_failed)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -638,7 +611,7 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_START_MINING
{
- struct request_t
+ struct request_t: public rpc_request_base
{
std::string miner_address;
uint64_t threads_count;
@@ -646,6 +619,7 @@ namespace cryptonote
bool ignore_battery;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(miner_address)
KV_SERIALIZE(threads_count)
KV_SERIALIZE(do_background_mining)
@@ -654,12 +628,10 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -667,17 +639,16 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_GET_INFO
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
-
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base);
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
uint64_t height;
uint64_t target_height;
uint64_t difficulty;
@@ -707,7 +678,6 @@ namespace cryptonote
uint64_t start_time;
uint64_t free_space;
bool offline;
- bool untrusted;
std::string bootstrap_daemon_address;
uint64_t height_without_bootstrap;
bool was_bootstrap_ever_used;
@@ -716,7 +686,7 @@ namespace cryptonote
std::string version;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(height)
KV_SERIALIZE(target_height)
KV_SERIALIZE(difficulty)
@@ -746,7 +716,6 @@ namespace cryptonote
KV_SERIALIZE(start_time)
KV_SERIALIZE(free_space)
KV_SERIALIZE(offline)
- KV_SERIALIZE(untrusted)
KV_SERIALIZE(bootstrap_daemon_address)
KV_SERIALIZE(height_without_bootstrap)
KV_SERIALIZE(was_bootstrap_ever_used)
@@ -762,18 +731,17 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_GET_NET_STATS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
-
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
uint64_t start_time;
uint64_t total_packets_in;
uint64_t total_bytes_in;
@@ -781,7 +749,7 @@ namespace cryptonote
uint64_t total_bytes_out;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(start_time)
KV_SERIALIZE(total_packets_in)
KV_SERIALIZE(total_bytes_in)
@@ -795,21 +763,19 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_STOP_MINING
{
- struct request_t
+ struct request_t: public rpc_request_base
{
-
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -818,18 +784,17 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_MINING_STATUS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
-
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
bool active;
uint64_t speed;
uint32_t threads_count;
@@ -847,7 +812,7 @@ namespace cryptonote
uint64_t difficulty_top64;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(active)
KV_SERIALIZE(speed)
KV_SERIALIZE(threads_count)
@@ -871,21 +836,19 @@ namespace cryptonote
//-----------------------------------------------
struct COMMAND_RPC_SAVE_BC
{
- struct request_t
+ struct request_t: public rpc_request_base
{
-
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -896,14 +859,13 @@ namespace cryptonote
{
typedef std::list<std::string> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
uint64_t count;
- std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(count)
- KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -919,7 +881,7 @@ namespace cryptonote
struct COMMAND_RPC_GETBLOCKTEMPLATE
{
- struct request_t
+ struct request_t: public rpc_request_base
{
uint64_t reserve_size; //max 255 bytes
std::string wallet_address;
@@ -927,6 +889,7 @@ namespace cryptonote
std::string extra_nonce;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(reserve_size)
KV_SERIALIZE(wallet_address)
KV_SERIALIZE(prev_block)
@@ -935,7 +898,7 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
uint64_t difficulty;
std::string wide_difficulty;
@@ -944,12 +907,14 @@ namespace cryptonote
uint64_t reserved_offset;
uint64_t expected_reward;
std::string prev_hash;
+ uint64_t seed_height;
+ std::string seed_hash;
+ std::string next_seed_hash;
blobdata blocktemplate_blob;
blobdata blockhashing_blob;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(difficulty)
KV_SERIALIZE(wide_difficulty)
KV_SERIALIZE(difficulty_top64)
@@ -957,10 +922,11 @@ namespace cryptonote
KV_SERIALIZE(reserved_offset)
KV_SERIALIZE(expected_reward)
KV_SERIALIZE(prev_hash)
+ KV_SERIALIZE(seed_height)
KV_SERIALIZE(blocktemplate_blob)
KV_SERIALIZE(blockhashing_blob)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
+ KV_SERIALIZE(seed_hash)
+ KV_SERIALIZE(next_seed_hash)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -970,12 +936,10 @@ namespace cryptonote
{
typedef std::vector<std::string> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -983,7 +947,7 @@ namespace cryptonote
struct COMMAND_RPC_GENERATEBLOCKS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
uint64_t amount_of_blocks;
std::string wallet_address;
@@ -991,6 +955,7 @@ namespace cryptonote
uint32_t starting_nonce;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(amount_of_blocks)
KV_SERIALIZE(wallet_address)
KV_SERIALIZE(prev_block)
@@ -999,16 +964,15 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
uint64_t height;
std::vector<std::string> blocks;
- std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(height)
KV_SERIALIZE(blocks)
- KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1067,26 +1031,24 @@ namespace cryptonote
struct COMMAND_RPC_GET_LAST_BLOCK_HEADER
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
block_header_response block_header;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(block_header)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1095,13 +1057,14 @@ namespace cryptonote
struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::string hash;
std::vector<std::string> hashes;
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(hash)
KV_SERIALIZE(hashes)
KV_SERIALIZE_OPT(fill_pow_hash, false);
@@ -1109,18 +1072,15 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
block_header_response block_header;
std::vector<block_header_response> block_headers;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(block_header)
KV_SERIALIZE(block_headers)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1128,28 +1088,26 @@ namespace cryptonote
struct COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
uint64_t height;
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(height)
KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
block_header_response block_header;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(block_header)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1157,13 +1115,14 @@ namespace cryptonote
struct COMMAND_RPC_GET_BLOCK
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::string hash;
uint64_t height;
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(hash)
KV_SERIALIZE(height)
KV_SERIALIZE_OPT(fill_pow_hash, false);
@@ -1171,24 +1130,21 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
block_header_response block_header;
std::string miner_tx_hash;
std::vector<std::string> tx_hashes;
std::string blob;
std::string json;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(block_header)
KV_SERIALIZE(miner_tx_hash)
KV_SERIALIZE(tx_hashes)
- KV_SERIALIZE(status)
KV_SERIALIZE(blob)
KV_SERIALIZE(json)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1200,19 +1156,20 @@ namespace cryptonote
uint32_t ip;
uint16_t port;
uint16_t rpc_port;
+ uint32_t rpc_credits_per_hash;
uint64_t last_seen;
uint32_t pruning_seed;
peer() = default;
- peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
- : id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash)
+ : id(id), host(host), ip(0), port(0), rpc_port(rpc_port), rpc_credits_per_hash(rpc_credits_per_hash), last_seen(last_seen), pruning_seed(pruning_seed)
{}
- peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
- : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash)
+ : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), rpc_credits_per_hash(rpc_credits_per_hash), last_seen(last_seen), pruning_seed(pruning_seed)
{}
- peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
- : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port, uint32_t rpc_credits_per_hash)
+ : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), rpc_credits_per_hash(rpc_credits_per_hash), last_seen(last_seen), pruning_seed(pruning_seed)
{}
BEGIN_KV_SERIALIZE_MAP()
@@ -1221,6 +1178,7 @@ namespace cryptonote
KV_SERIALIZE(ip)
KV_SERIALIZE(port)
KV_SERIALIZE_OPT(rpc_port, (uint16_t)0)
+ KV_SERIALIZE_OPT(rpc_credits_per_hash, (uint32_t)0)
KV_SERIALIZE(last_seen)
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
END_KV_SERIALIZE_MAP()
@@ -1228,24 +1186,24 @@ namespace cryptonote
struct COMMAND_RPC_GET_PEER_LIST
{
- struct request_t
+ struct request_t: public rpc_request_base
{
bool public_only;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(public_only, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
std::vector<peer> white_list;
std::vector<peer> gray_list;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(white_list)
KV_SERIALIZE(gray_list)
END_KV_SERIALIZE_MAP()
@@ -1258,42 +1216,44 @@ namespace cryptonote
std::string host;
uint64_t last_seen;
uint16_t rpc_port;
+ uint32_t rpc_credits_per_hash;
- public_node() = delete;
+ public_node(): last_seen(0), rpc_port(0), rpc_credits_per_hash(0) {}
public_node(const peer &peer)
- : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port)
+ : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port), rpc_credits_per_hash(peer.rpc_credits_per_hash)
{}
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(host)
KV_SERIALIZE(last_seen)
KV_SERIALIZE(rpc_port)
+ KV_SERIALIZE(rpc_credits_per_hash)
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_GET_PUBLIC_NODES
{
- struct request_t
+ struct request_t: public rpc_request_base
{
bool gray;
bool white;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(gray, false)
KV_SERIALIZE_OPT(white, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
std::vector<public_node> gray;
std::vector<public_node> white;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(gray)
KV_SERIALIZE(white)
END_KV_SERIALIZE_MAP()
@@ -1303,21 +1263,21 @@ namespace cryptonote
struct COMMAND_RPC_SET_LOG_HASH_RATE
{
- struct request_t
+ struct request_t: public rpc_request_base
{
bool visible;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(visible)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1325,21 +1285,21 @@ namespace cryptonote
struct COMMAND_RPC_SET_LOG_LEVEL
{
- struct request_t
+ struct request_t: public rpc_request_base
{
int8_t level;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(level)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1347,23 +1307,23 @@ namespace cryptonote
struct COMMAND_RPC_SET_LOG_CATEGORIES
{
- struct request_t
+ struct request_t: public rpc_request_base
{
std::string categories;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(categories)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
std::string categories;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(categories)
END_KV_SERIALIZE_MAP()
};
@@ -1422,25 +1382,23 @@ namespace cryptonote
struct COMMAND_RPC_GET_TRANSACTION_POOL
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::vector<tx_info> transactions;
std::vector<spent_key_image_info> spent_key_images;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(transactions)
KV_SERIALIZE(spent_key_images)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1448,23 +1406,21 @@ namespace cryptonote
struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::vector<crypto::hash> tx_hashes;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1472,23 +1428,21 @@ namespace cryptonote
struct COMMAND_RPC_GET_TRANSACTION_POOL_HASHES
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::vector<std::string> tx_hashes;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(tx_hashes)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1503,23 +1457,21 @@ namespace cryptonote
struct COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::vector<tx_backlog_entry> backlog;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(backlog)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1566,30 +1518,28 @@ namespace cryptonote
KV_SERIALIZE(num_10m)
KV_SERIALIZE(num_not_relayed)
KV_SERIALIZE(histo_98pc)
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(histo)
+ KV_SERIALIZE(histo)
KV_SERIALIZE(num_double_spends)
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_GET_TRANSACTION_POOL_STATS
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
txpool_stats pool_stats;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(pool_stats)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1597,20 +1547,20 @@ namespace cryptonote
struct COMMAND_RPC_GET_CONNECTIONS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
std::list<connection_info> connections;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(connections)
END_KV_SERIALIZE_MAP()
};
@@ -1619,13 +1569,14 @@ namespace cryptonote
struct COMMAND_RPC_GET_BLOCK_HEADERS_RANGE
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
uint64_t start_height;
uint64_t end_height;
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(start_height)
KV_SERIALIZE(end_height)
KV_SERIALIZE_OPT(fill_pow_hash, false);
@@ -1633,16 +1584,13 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::vector<block_header_response> headers;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(headers)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1677,19 +1625,18 @@ namespace cryptonote
struct COMMAND_RPC_STOP_DAEMON
{
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1697,19 +1644,18 @@ namespace cryptonote
struct COMMAND_RPC_FAST_EXIT
{
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1717,25 +1663,23 @@ namespace cryptonote
struct COMMAND_RPC_GET_LIMIT
{
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
uint64_t limit_up;
uint64_t limit_down;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(limit_up)
KV_SERIALIZE(limit_down)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1743,26 +1687,26 @@ namespace cryptonote
struct COMMAND_RPC_SET_LIMIT
{
- struct request_t
+ struct request_t: public rpc_request_base
{
int64_t limit_down; // all limits (for get and set) are kB/s
int64_t limit_up;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(limit_down)
KV_SERIALIZE(limit_up)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
int64_t limit_up;
int64_t limit_down;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(limit_up)
KV_SERIALIZE(limit_down)
END_KV_SERIALIZE_MAP()
@@ -1772,25 +1716,26 @@ namespace cryptonote
struct COMMAND_RPC_OUT_PEERS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
bool set;
uint32_t out_peers;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(out_peers)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
uint32_t out_peers;
- std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(out_peers)
- KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1798,25 +1743,25 @@ namespace cryptonote
struct COMMAND_RPC_IN_PEERS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
bool set;
uint32_t in_peers;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(in_peers)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
uint32_t in_peers;
- std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(in_peers)
- KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1824,17 +1769,18 @@ namespace cryptonote
struct COMMAND_RPC_HARD_FORK_INFO
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
uint8_t version;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(version)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
uint8_t version;
bool enabled;
@@ -1844,10 +1790,9 @@ namespace cryptonote
uint8_t voting;
uint32_t state;
uint64_t earliest_height;
- std::string status;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(version)
KV_SERIALIZE(enabled)
KV_SERIALIZE(window)
@@ -1856,8 +1801,6 @@ namespace cryptonote
KV_SERIALIZE(voting)
KV_SERIALIZE(state)
KV_SERIALIZE(earliest_height)
- KV_SERIALIZE(status)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1878,20 +1821,20 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
std::vector<ban> bans;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(bans)
END_KV_SERIALIZE_MAP()
};
@@ -1915,22 +1858,21 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct request_t
+ struct request_t: public rpc_request_base
{
std::vector<ban> bans;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(bans)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1965,22 +1907,21 @@ namespace cryptonote
struct COMMAND_RPC_FLUSH_TRANSACTION_POOL
{
- struct request_t
+ struct request_t: public rpc_request_base
{
std::vector<std::string> txids;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE(txids)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -1988,7 +1929,7 @@ namespace cryptonote
struct COMMAND_RPC_GET_OUTPUT_HISTOGRAM
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<uint64_t> amounts;
uint64_t min_count;
@@ -1997,6 +1938,7 @@ namespace cryptonote
uint64_t recent_cutoff;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base);
KV_SERIALIZE(amounts);
KV_SERIALIZE(min_count);
KV_SERIALIZE(max_count);
@@ -2025,16 +1967,13 @@ namespace cryptonote
entry() {}
};
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::vector<entry> histogram;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(histogram)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2042,25 +1981,23 @@ namespace cryptonote
struct COMMAND_RPC_GET_VERSION
{
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
uint32_t version;
bool release;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(version)
KV_SERIALIZE(release)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2068,26 +2005,26 @@ namespace cryptonote
struct COMMAND_RPC_GET_COINBASE_TX_SUM
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
uint64_t height;
uint64_t count;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base);
KV_SERIALIZE(height);
KV_SERIALIZE(count);
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
uint64_t emission_amount;
uint64_t fee_amount;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(emission_amount)
KV_SERIALIZE(fee_amount)
END_KV_SERIALIZE_MAP()
@@ -2097,28 +2034,26 @@ namespace cryptonote
struct COMMAND_RPC_GET_BASE_FEE_ESTIMATE
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
uint64_t grace_blocks;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(grace_blocks)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
uint64_t fee;
uint64_t quantization_mask;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(fee)
KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1)
- KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2126,9 +2061,10 @@ namespace cryptonote
struct COMMAND_RPC_GET_ALTERNATE_CHAINS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -2156,13 +2092,12 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
std::vector<chain_info> chains;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(chains)
END_KV_SERIALIZE_MAP()
};
@@ -2171,21 +2106,21 @@ namespace cryptonote
struct COMMAND_RPC_UPDATE
{
- struct request_t
+ struct request_t: public rpc_request_base
{
std::string command;
std::string path;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(command);
- KV_SERIALIZE(path);
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ KV_SERIALIZE(command)
+ KV_SERIALIZE(path)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
bool update;
std::string version;
std::string user_uri;
@@ -2194,7 +2129,7 @@ namespace cryptonote
std::string path;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(update)
KV_SERIALIZE(version)
KV_SERIALIZE(user_uri)
@@ -2208,22 +2143,21 @@ namespace cryptonote
struct COMMAND_RPC_RELAY_TX
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<std::string> txids;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(txids)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
-
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2231,9 +2165,10 @@ namespace cryptonote
struct COMMAND_RPC_SYNC_INFO
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -2268,9 +2203,8 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
uint64_t height;
uint64_t target_height;
uint32_t next_needed_pruning_seed;
@@ -2279,7 +2213,7 @@ namespace cryptonote
std::string overview;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(height)
KV_SERIALIZE(target_height)
KV_SERIALIZE(next_needed_pruning_seed)
@@ -2293,7 +2227,7 @@ namespace cryptonote
struct COMMAND_RPC_GET_OUTPUT_DISTRIBUTION
{
- struct request_t
+ struct request_t: public rpc_access_request_base
{
std::vector<uint64_t> amounts;
uint64_t from_height;
@@ -2303,6 +2237,7 @@ namespace cryptonote
bool compress;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
KV_SERIALIZE(amounts)
KV_SERIALIZE_OPT(from_height, (uint64_t)0)
KV_SERIALIZE_OPT(to_height, (uint64_t)0)
@@ -2355,16 +2290,213 @@ namespace cryptonote
END_KV_SERIALIZE_MAP()
};
- struct response_t
+ struct response_t: public rpc_access_response_base
{
- std::string status;
std::vector<distribution> distributions;
- bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
KV_SERIALIZE(distributions)
- KV_SERIALIZE(untrusted)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ struct COMMAND_RPC_ACCESS_INFO
+ {
+ struct request_t: public rpc_access_request_base
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t: public rpc_access_response_base
+ {
+ std::string hashing_blob;
+ uint64_t seed_height;
+ std::string seed_hash;
+ std::string next_seed_hash;
+ uint32_t cookie;
+ uint64_t diff;
+ uint64_t credits_per_hash_found;
+ uint64_t height;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
+ KV_SERIALIZE(hashing_blob)
+ KV_SERIALIZE(seed_height)
+ KV_SERIALIZE(seed_hash)
+ KV_SERIALIZE(next_seed_hash)
+ KV_SERIALIZE(cookie)
+ KV_SERIALIZE(diff)
+ KV_SERIALIZE(credits_per_hash_found)
+ KV_SERIALIZE(height)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ struct COMMAND_RPC_ACCESS_SUBMIT_NONCE
+ {
+ struct request_t: public rpc_access_request_base
+ {
+ uint32_t nonce;
+ uint32_t cookie;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
+ KV_SERIALIZE(nonce)
+ KV_SERIALIZE(cookie)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t: public rpc_access_response_base
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ struct COMMAND_RPC_ACCESS_PAY
+ {
+ struct request_t: public rpc_access_request_base
+ {
+ std::string paying_for;
+ uint64_t payment;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_request_base)
+ KV_SERIALIZE(paying_for)
+ KV_SERIALIZE(payment)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t: public rpc_access_response_base
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_access_response_base)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ struct COMMAND_RPC_ACCESS_TRACKING
+ {
+ struct request_t: public rpc_request_base
+ {
+ bool clear;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ KV_SERIALIZE(clear)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct entry
+ {
+ std::string rpc;
+ uint64_t count;
+ uint64_t time;
+ uint64_t credits;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(rpc)
+ KV_SERIALIZE(count)
+ KV_SERIALIZE(time)
+ KV_SERIALIZE(credits)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response_t: public rpc_response_base
+ {
+ std::vector<entry> data;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
+ KV_SERIALIZE(data)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ struct COMMAND_RPC_ACCESS_DATA
+ {
+ struct request_t: public rpc_request_base
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct entry
+ {
+ std::string client;
+ uint64_t balance;
+ uint64_t last_update_time;
+ uint64_t credits_total;
+ uint64_t credits_used;
+ uint64_t nonces_good;
+ uint64_t nonces_stale;
+ uint64_t nonces_bad;
+ uint64_t nonces_dupe;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(client)
+ KV_SERIALIZE(balance)
+ KV_SERIALIZE(last_update_time)
+ KV_SERIALIZE(credits_total)
+ KV_SERIALIZE(credits_used)
+ KV_SERIALIZE(nonces_good)
+ KV_SERIALIZE(nonces_stale)
+ KV_SERIALIZE(nonces_bad)
+ KV_SERIALIZE(nonces_dupe)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response_t: public rpc_response_base
+ {
+ std::list<entry> entries;
+ uint32_t hashrate;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
+ KV_SERIALIZE(entries)
+ KV_SERIALIZE(hashrate)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ struct COMMAND_RPC_ACCESS_ACCOUNT
+ {
+ struct request_t: public rpc_request_base
+ {
+ std::string client;
+ int64_t delta_balance;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ KV_SERIALIZE(client)
+ KV_SERIALIZE_OPT(delta_balance, (int64_t)0)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t: public rpc_response_base
+ {
+ uint64_t credits;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_response_base)
+ KV_SERIALIZE(credits)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -2372,23 +2504,23 @@ namespace cryptonote
struct COMMAND_RPC_POP_BLOCKS
{
- struct request_t
+ struct request_t: public rpc_request_base
{
uint64_t nblocks;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(nblocks);
+ KV_SERIALIZE_PARENT(rpc_request_base)
+ KV_SERIALIZE(nblocks)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
- std::string status;
uint64_t height;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(height)
END_KV_SERIALIZE_MAP()
};
@@ -2397,24 +2529,24 @@ namespace cryptonote
struct COMMAND_RPC_PRUNE_BLOCKCHAIN
{
- struct request_t
+ struct request_t: public rpc_request_base
{
bool check;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_PARENT(rpc_request_base)
KV_SERIALIZE_OPT(check, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
- struct response_t
+ struct response_t: public rpc_response_base
{
bool pruned;
uint32_t pruning_seed;
- std::string status;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
+ KV_SERIALIZE_PARENT(rpc_response_base)
KV_SERIALIZE(pruned)
KV_SERIALIZE(pruning_seed)
END_KV_SERIALIZE_MAP()
@@ -2422,4 +2554,27 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_FLUSH_CACHE
+ {
+ struct request_t
+ {
+ bool bad_txs;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(bad_txs, false)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
}
diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h
index b13049e61..2fd42f43f 100644
--- a/src/rpc/core_rpc_server_error_codes.h
+++ b/src/rpc/core_rpc_server_error_codes.h
@@ -43,5 +43,34 @@
#define CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC -11
#define CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS -12
#define CORE_RPC_ERROR_CODE_REGTEST_REQUIRED -13
+#define CORE_RPC_ERROR_CODE_PAYMENT_REQUIRED -14
+#define CORE_RPC_ERROR_CODE_INVALID_CLIENT -15
+#define CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW -16
+#define CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT -17
+#define CORE_RPC_ERROR_CODE_STALE_PAYMENT -18
+static inline const char *get_rpc_server_error_message(int64_t code)
+{
+ switch (code)
+ {
+ case CORE_RPC_ERROR_CODE_WRONG_PARAM: return "Invalid parameter";
+ case CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT: return "Height is too large";
+ case CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE: return "Reserve size is too large";
+ case CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS: return "Wrong wallet address";
+ case CORE_RPC_ERROR_CODE_INTERNAL_ERROR: return "Internal error";
+ case CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB: return "Wrong block blob";
+ case CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED: return "Block not accepted";
+ case CORE_RPC_ERROR_CODE_CORE_BUSY: return "Core is busy";
+ case CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB_SIZE: return "Wrong block blob size";
+ case CORE_RPC_ERROR_CODE_UNSUPPORTED_RPC: return "Unsupported RPC";
+ case CORE_RPC_ERROR_CODE_MINING_TO_SUBADDRESS: return "Mining to subaddress is not supported";
+ case CORE_RPC_ERROR_CODE_REGTEST_REQUIRED: return "Regtest mode required";
+ case CORE_RPC_ERROR_CODE_PAYMENT_REQUIRED: return "Payment required";
+ case CORE_RPC_ERROR_CODE_INVALID_CLIENT: return "Invalid client";
+ case CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW: return "Payment too low";
+ case CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT: return "Duplicate payment";
+ case CORE_RPC_ERROR_CODE_STALE_PAYMENT: return "Stale payment";
+ default: MERROR("Unknown error: " << code); return "Unknown error";
+ }
+}
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 890380dc8..24800ff20 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -28,6 +28,7 @@
#include "daemon_handler.h"
+#include <boost/uuid/nil_generator.hpp>
// likely included by daemon_handler.h's includes,
// but including here for clarity
#include "cryptonote_core/cryptonote_core.h"
@@ -141,7 +142,7 @@ namespace rpc
auto& chain = m_core.get_blockchain_storage();
- if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height, false))
+ if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, NULL, res.start_height, res.current_height, false))
{
res.status = Message::STATUS_FAILED;
res.error_details = "Blockchain::find_blockchain_supplement() returned false";
@@ -288,10 +289,9 @@ namespace rpc
return;
}
- cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
- if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, !relay) || tvc.m_verifivation_failed)
+ if(!m_core.handle_incoming_tx({tx_blob, crypto::null_hash}, tvc, (relay ? relay_method::local : relay_method::none), false) || tvc.m_verifivation_failed)
{
if (tvc.m_verifivation_failed)
{
@@ -311,42 +311,42 @@ namespace rpc
if (tvc.m_double_spend)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "double spend";
+ res.error_details += "double spend";
}
if (tvc.m_invalid_input)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "invalid input";
+ res.error_details += "invalid input";
}
if (tvc.m_invalid_output)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "invalid output";
+ res.error_details += "invalid output";
}
if (tvc.m_too_big)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "too big";
+ res.error_details += "too big";
}
if (tvc.m_overspend)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "overspend";
+ res.error_details += "overspend";
}
if (tvc.m_fee_too_low)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "fee too low";
+ res.error_details += "fee too low";
}
if (tvc.m_not_rct)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "tx is not ringct";
+ res.error_details += "tx is not ringct";
}
if (tvc.m_too_few_outputs)
{
if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details = "too few outputs";
+ res.error_details += "too few outputs";
}
if (res.error_details.empty())
{
@@ -368,7 +368,7 @@ namespace rpc
NOTIFY_NEW_TRANSACTIONS::request r;
r.txs.push_back(tx_blob);
- m_core.get_protocol()->relay_transactions(r, fake_context);
+ m_core.get_protocol()->relay_transactions(r, boost::uuids::nil_uuid(), epee::net_utils::zone::invalid);
//TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes
res.status = Message::STATUS_OK;
diff --git a/src/rpc/message_data_structs.h b/src/rpc/message_data_structs.h
index 2a43811cf..e64f5f163 100644
--- a/src/rpc/message_data_structs.h
+++ b/src/rpc/message_data_structs.h
@@ -80,6 +80,7 @@ namespace rpc
uint32_t ip;
uint16_t port;
uint16_t rpc_port;
+ uint32_t rpc_credits_per_hash;
uint64_t last_seen;
uint32_t pruning_seed;
};
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 68b33cb8c..0eaa0ef0e 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -92,7 +92,7 @@ namespace cryptonote
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"})
, rpc_bind_ipv6_address({"rpc-bind-ipv6-address", rpc_args::tr("Specify IPv6 address to bind RPC server"), "::1"})
, rpc_use_ipv6({"rpc-use-ipv6", rpc_args::tr("Allow IPv6 for RPC"), false})
- , rpc_require_ipv4({"rpc-require-ipv4", rpc_args::tr("Require successful IPv4 bind for RPC"), true})
+ , rpc_ignore_ipv4({"rpc-ignore-ipv4", rpc_args::tr("Ignore unsuccessful IPv4 bind for RPC"), false})
, 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")})
, rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
@@ -113,7 +113,7 @@ namespace cryptonote
command_line::add_arg(desc, arg.rpc_bind_ip);
command_line::add_arg(desc, arg.rpc_bind_ipv6_address);
command_line::add_arg(desc, arg.rpc_use_ipv6);
- command_line::add_arg(desc, arg.rpc_require_ipv4);
+ command_line::add_arg(desc, arg.rpc_ignore_ipv4);
command_line::add_arg(desc, arg.rpc_login);
command_line::add_arg(desc, arg.confirm_external_bind);
command_line::add_arg(desc, arg.rpc_access_control_origins);
@@ -135,7 +135,7 @@ namespace cryptonote
config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip);
config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address);
config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6);
- config.require_ipv4 = command_line::get_arg(vm, arg.rpc_require_ipv4);
+ config.require_ipv4 = !command_line::get_arg(vm, arg.rpc_ignore_ipv4);
if (!config.bind_ip.empty())
{
// always parse IP here for error consistency
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index cd154a4d0..bdb9c70d5 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -54,7 +54,7 @@ namespace cryptonote
const command_line::arg_descriptor<std::string> rpc_bind_ip;
const command_line::arg_descriptor<std::string> rpc_bind_ipv6_address;
const command_line::arg_descriptor<bool> rpc_use_ipv6;
- const command_line::arg_descriptor<bool> rpc_require_ipv4;
+ const command_line::arg_descriptor<bool> rpc_ignore_ipv4;
const command_line::arg_descriptor<std::string> rpc_login;
const command_line::arg_descriptor<bool> confirm_external_bind;
const command_line::arg_descriptor<std::string> rpc_access_control_origins;
diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp
new file mode 100644
index 000000000..0637db728
--- /dev/null
+++ b/src/rpc/rpc_payment.cpp
@@ -0,0 +1,402 @@
+// Copyright (c) 2018-2019, 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 <boost/archive/portable_binary_iarchive.hpp>
+#include <boost/archive/portable_binary_oarchive.hpp>
+#include "cryptonote_config.h"
+#include "include_base_utils.h"
+#include "string_tools.h"
+#include "file_io_utils.h"
+#include "int-util.h"
+#include "common/util.h"
+#include "serialization/crypto.h"
+#include "common/unordered_containers_boost_serialization.h"
+#include "cryptonote_basic/cryptonote_boost_serialization.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "cryptonote_basic/difficulty.h"
+#include "core_rpc_server_error_codes.h"
+#include "rpc_payment.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc.payment"
+
+#define STALE_THRESHOLD 15 /* seconds */
+
+#define PENALTY_FOR_STALE 0
+#define PENALTY_FOR_BAD_HASH 20
+#define PENALTY_FOR_DUPLICATE 20
+
+#define DEFAULT_FLUSH_AGE (3600 * 24 * 180) // half a year
+#define DEFAULT_ZERO_FLUSH_AGE (60 * 2) // 2 minutes
+
+#define RPC_PAYMENT_NONCE_TAIL 0x58
+
+namespace cryptonote
+{
+ rpc_payment::client_info::client_info():
+ cookie(0),
+ top(crypto::null_hash),
+ previous_top(crypto::null_hash),
+ credits(0),
+ update_time(time(NULL)),
+ last_request_timestamp(0),
+ block_template_update_time(0),
+ credits_total(0),
+ credits_used(0),
+ nonces_good(0),
+ nonces_stale(0),
+ nonces_bad(0),
+ nonces_dupe(0)
+ {
+ }
+
+ rpc_payment::rpc_payment(const cryptonote::account_public_address &address, uint64_t diff, uint64_t credits_per_hash_found):
+ m_address(address),
+ m_diff(diff),
+ m_credits_per_hash_found(credits_per_hash_found),
+ m_credits_total(0),
+ m_credits_used(0),
+ m_nonces_good(0),
+ m_nonces_stale(0),
+ m_nonces_bad(0),
+ m_nonces_dupe(0)
+ {
+ }
+
+ uint64_t rpc_payment::balance(const crypto::public_key &client, int64_t delta)
+ {
+ client_info &info = m_client_info[client]; // creates if not found
+ uint64_t credits = info.credits;
+ if (delta > 0 && credits > std::numeric_limits<uint64_t>::max() - delta)
+ credits = std::numeric_limits<uint64_t>::max();
+ else if (delta < 0 && credits < (uint64_t)-delta)
+ credits = 0;
+ else
+ credits += delta;
+ if (delta)
+ MINFO("Client " << client << ": balance change from " << info.credits << " to " << credits);
+ return info.credits = credits;
+ }
+
+ bool rpc_payment::pay(const crypto::public_key &client, uint64_t ts, uint64_t payment, const std::string &rpc, bool same_ts, uint64_t &credits)
+ {
+ client_info &info = m_client_info[client]; // creates if not found
+ if (ts < info.last_request_timestamp || (ts == info.last_request_timestamp && !same_ts))
+ {
+ MDEBUG("Invalid ts: " << ts << " <= " << info.last_request_timestamp);
+ return false;
+ }
+ info.last_request_timestamp = ts;
+ if (info.credits < payment)
+ {
+ MDEBUG("Not enough credits: " << info.credits << " < " << payment);
+ credits = info.credits;
+ return false;
+ }
+ info.credits -= payment;
+ add64clamp(&info.credits_used, payment);
+ add64clamp(&m_credits_used, payment);
+ MDEBUG("client " << client << " paying " << payment << " for " << rpc << ", " << info.credits << " left");
+ credits = info.credits;
+ return true;
+ }
+
+ bool rpc_payment::get_info(const crypto::public_key &client, const std::function<bool(const cryptonote::blobdata&, cryptonote::block&, uint64_t &seed_height, crypto::hash &seed_hash)> &get_block_template, cryptonote::blobdata &hashing_blob, uint64_t &seed_height, crypto::hash &seed_hash, const crypto::hash &top, uint64_t &diff, uint64_t &credits_per_hash_found, uint64_t &credits, uint32_t &cookie)
+ {
+ client_info &info = m_client_info[client]; // creates if not found
+ const uint64_t now = time(NULL);
+ bool need_template = top != info.top || now >= info.block_template_update_time + STALE_THRESHOLD;
+ if (need_template)
+ {
+ cryptonote::block new_block;
+ uint64_t new_seed_height;
+ crypto::hash new_seed_hash;
+ cryptonote::blobdata extra_nonce("\x42\x42\x42\x42", 4);
+ if (!get_block_template(extra_nonce, new_block, new_seed_height, new_seed_hash))
+ return false;
+ if(!remove_field_from_tx_extra(new_block.miner_tx.extra, typeid(cryptonote::tx_extra_nonce)))
+ return false;
+ char data[33];
+ memcpy(data, &client, 32);
+ data[32] = RPC_PAYMENT_NONCE_TAIL;
+ crypto::hash hash;
+ cn_fast_hash(data, sizeof(data), hash);
+ extra_nonce = cryptonote::blobdata((const char*)&hash, 4);
+ if(!add_extra_nonce_to_tx_extra(new_block.miner_tx.extra, extra_nonce))
+ return false;
+ info.previous_block = std::move(info.block);
+ info.block = std::move(new_block);
+ hashing_blob = get_block_hashing_blob(info.block);
+ info.previous_hashing_blob = info.hashing_blob;
+ info.hashing_blob = hashing_blob;
+ info.previous_top = info.top;
+ info.previous_seed_height = info.seed_height;
+ info.seed_height = new_seed_height;
+ info.previous_seed_hash = info.seed_hash;
+ info.seed_hash = new_seed_hash;
+ std::swap(info.previous_payments, info.payments);
+ info.payments.clear();
+ ++info.cookie;
+ info.block_template_update_time = now;
+ }
+ info.top = top;
+ info.update_time = now;
+ hashing_blob = info.hashing_blob;
+ diff = m_diff;
+ credits_per_hash_found = m_credits_per_hash_found;
+ credits = info.credits;
+ seed_height = info.seed_height;
+ seed_hash = info.seed_hash;
+ cookie = info.cookie;
+ return true;
+ }
+
+ bool rpc_payment::submit_nonce(const crypto::public_key &client, uint32_t nonce, const crypto::hash &top, int64_t &error_code, std::string &error_message, uint64_t &credits, crypto::hash &hash, cryptonote::block &block, uint32_t cookie, bool &stale)
+ {
+ client_info &info = m_client_info[client]; // creates if not found
+ if (cookie != info.cookie && cookie != info.cookie - 1)
+ {
+ MWARNING("Very stale nonce");
+ ++m_nonces_stale;
+ ++info.nonces_stale;
+ sub64clamp(&info.credits, PENALTY_FOR_STALE * m_credits_per_hash_found);
+ error_code = CORE_RPC_ERROR_CODE_STALE_PAYMENT;
+ error_message = "Very stale payment";
+ return false;
+ }
+ const bool is_current = cookie == info.cookie;
+ MINFO("client " << client << " sends nonce: " << nonce << ", " << (is_current ? "current" : "stale"));
+ std::unordered_set<uint64_t> &payments = is_current ? info.payments : info.previous_payments;
+ if (!payments.insert(nonce).second)
+ {
+ MWARNING("Duplicate nonce " << nonce << " from " << (is_current ? "current" : "previous"));
+ ++m_nonces_dupe;
+ ++info.nonces_dupe;
+ sub64clamp(&info.credits, PENALTY_FOR_DUPLICATE * m_credits_per_hash_found);
+ error_code = CORE_RPC_ERROR_CODE_DUPLICATE_PAYMENT;
+ error_message = "Duplicate payment";
+ return false;
+ }
+
+ const uint64_t now = time(NULL);
+ if (!is_current)
+ {
+ if (now > info.update_time + STALE_THRESHOLD)
+ {
+ MWARNING("Nonce is stale (top " << top << ", should be " << info.top << " or within " << STALE_THRESHOLD << " seconds");
+ ++m_nonces_stale;
+ ++info.nonces_stale;
+ sub64clamp(&info.credits, PENALTY_FOR_STALE * m_credits_per_hash_found);
+ error_code = CORE_RPC_ERROR_CODE_STALE_PAYMENT;
+ error_message = "stale payment";
+ return false;
+ }
+ }
+
+ cryptonote::blobdata hashing_blob = is_current ? info.hashing_blob : info.previous_hashing_blob;
+ if (hashing_blob.size() < 43)
+ {
+ // not initialized ?
+ error_code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB;
+ error_message = "not initialized";
+ return false;
+ }
+
+ block = is_current ? info.block : info.previous_block;
+ *(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(nonce);
+ if (block.major_version >= RX_BLOCK_VERSION)
+ {
+ const uint64_t seed_height = is_current ? info.seed_height : info.previous_seed_height;
+ const crypto::hash &seed_hash = is_current ? info.seed_hash : info.previous_seed_hash;
+ const uint64_t height = cryptonote::get_block_height(block);
+ crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, 0, 0);
+ }
+ else
+ {
+ const int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0;
+ crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash, cn_variant, cryptonote::get_block_height(block));
+ }
+ if (!check_hash(hash, m_diff))
+ {
+ MWARNING("Payment too low");
+ ++m_nonces_bad;
+ ++info.nonces_bad;
+ error_code = CORE_RPC_ERROR_CODE_PAYMENT_TOO_LOW;
+ error_message = "Hash does not meet difficulty (could be wrong PoW hash, or mining at lower difficulty than required, or attempt to defraud)";
+ sub64clamp(&info.credits, PENALTY_FOR_BAD_HASH * m_credits_per_hash_found);
+ return false;
+ }
+
+ add64clamp(&info.credits, m_credits_per_hash_found);
+ MINFO("client " << client << " credited for " << m_credits_per_hash_found << ", now " << info.credits << (is_current ? "" : " (close)"));
+
+ m_hashrate[now] += m_diff;
+ add64clamp(&m_credits_total, m_credits_per_hash_found);
+ add64clamp(&info.credits_total, m_credits_per_hash_found);
+ ++m_nonces_good;
+ ++info.nonces_good;
+
+ credits = info.credits;
+ block = info.block;
+ block.nonce = nonce;
+ stale = !is_current;
+ return true;
+ }
+
+ bool rpc_payment::foreach(const std::function<bool(const crypto::public_key &client, const client_info &info)> &f) const
+ {
+ for (std::unordered_map<crypto::public_key, client_info>::const_iterator i = m_client_info.begin(); i != m_client_info.end(); ++i)
+ {
+ if (!f(i->first, i->second))
+ return false;
+ }
+ return true;
+ }
+
+ bool rpc_payment::load(std::string directory)
+ {
+ TRY_ENTRY();
+ m_directory = std::move(directory);
+ std::string state_file_path = directory + "/" + RPC_PAYMENTS_DATA_FILENAME;
+ MINFO("loading rpc payments data from " << state_file_path);
+ std::ifstream data;
+ data.open(state_file_path, std::ios_base::binary | std::ios_base::in);
+ if (!data.fail())
+ {
+ try
+ {
+ boost::archive::portable_binary_iarchive a(data);
+ a >> *this;
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to load RPC payments file: " << e.what());
+ m_client_info.clear();
+ }
+ }
+ else
+ {
+ m_client_info.clear();
+ }
+
+ CATCH_ENTRY_L0("rpc_payment::load", false);
+ return true;
+ }
+
+ bool rpc_payment::store(const std::string &directory_) const
+ {
+ TRY_ENTRY();
+ const std::string &directory = directory_.empty() ? m_directory : directory_;
+ MDEBUG("storing rpc payments data to " << directory);
+ if (!tools::create_directories_if_necessary(directory))
+ {
+ MWARNING("Failed to create data directory: " << directory);
+ return false;
+ }
+ const boost::filesystem::path state_file_path = (boost::filesystem::path(directory) / RPC_PAYMENTS_DATA_FILENAME);
+ if (boost::filesystem::exists(state_file_path))
+ {
+ std::string state_file_path_old = state_file_path.string() + ".old";
+ boost::system::error_code ec;
+ boost::filesystem::remove(state_file_path_old, ec);
+ std::error_code e = tools::replace_file(state_file_path.string(), state_file_path_old);
+ if (e)
+ MWARNING("Failed to rename " << state_file_path << " to " << state_file_path_old << ": " << e);
+ }
+ std::ofstream data;
+ data.open(state_file_path.string(), std::ios_base::binary | std::ios_base::out | std::ios::trunc);
+ if (data.fail())
+ {
+ MWARNING("Failed to save RPC payments to file " << state_file_path);
+ return false;
+ };
+ boost::archive::portable_binary_oarchive a(data);
+ a << *this;
+ return true;
+ CATCH_ENTRY_L0("rpc_payment::store", false);
+ }
+
+ unsigned int rpc_payment::flush_by_age(time_t seconds)
+ {
+ unsigned int count = 0;
+ const time_t now = time(NULL);
+ time_t seconds0 = seconds;
+ if (seconds == 0)
+ {
+ seconds = DEFAULT_FLUSH_AGE;
+ seconds0 = DEFAULT_ZERO_FLUSH_AGE;
+ }
+ const time_t threshold = seconds > now ? 0 : now - seconds;
+ const time_t threshold0 = seconds0 > now ? 0 : now - seconds0;
+ for (std::unordered_map<crypto::public_key, client_info>::iterator i = m_client_info.begin(); i != m_client_info.end(); )
+ {
+ std::unordered_map<crypto::public_key, client_info>::iterator j = i++;
+ const time_t t = std::max(j->second.last_request_timestamp, j->second.update_time);
+ const bool erase = t < ((j->second.credits == 0) ? threshold0 : threshold);
+ if (erase)
+ {
+ MINFO("Erasing " << j->first << " with " << j->second.credits << " credits, inactive for " << (now-t)/86400 << " days");
+ m_client_info.erase(j);
+ ++count;
+ }
+ }
+ return count;
+ }
+
+ uint64_t rpc_payment::get_hashes(unsigned int seconds) const
+ {
+ const uint64_t now = time(NULL);
+ uint64_t hashes = 0;
+ for (std::map<uint64_t, uint64_t>::const_reverse_iterator i = m_hashrate.crbegin(); i != m_hashrate.crend(); ++i)
+ {
+ if (now > i->first + seconds)
+ break;
+ hashes += i->second;
+ }
+ return hashes;
+ }
+
+ void rpc_payment::prune_hashrate(unsigned int seconds)
+ {
+ const uint64_t now = time(NULL);
+ std::map<uint64_t, uint64_t>::iterator i;
+ for (i = m_hashrate.begin(); i != m_hashrate.end(); ++i)
+ {
+ if (now <= i->first + seconds)
+ break;
+ }
+ m_hashrate.erase(m_hashrate.begin(), i);
+ }
+
+ bool rpc_payment::on_idle()
+ {
+ flush_by_age();
+ prune_hashrate(3600);
+ return true;
+ }
+}
diff --git a/src/rpc/rpc_payment.h b/src/rpc/rpc_payment.h
new file mode 100644
index 000000000..f6832fd34
--- /dev/null
+++ b/src/rpc/rpc_payment.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2018-2019, 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>
+#include <unordered_set>
+#include <unordered_map>
+#include <boost/serialization/version.hpp>
+#include "cryptonote_basic/blobdatatype.h"
+#include "cryptonote_basic/cryptonote_basic.h"
+
+namespace cryptonote
+{
+ class rpc_payment
+ {
+ public:
+ struct client_info
+ {
+ cryptonote::block block;
+ cryptonote::block previous_block;
+ cryptonote::blobdata hashing_blob;
+ cryptonote::blobdata previous_hashing_blob;
+ uint64_t previous_seed_height;
+ uint64_t seed_height;
+ crypto::hash previous_seed_hash;
+ crypto::hash seed_hash;
+ uint32_t cookie;
+ crypto::hash top;
+ crypto::hash previous_top;
+ uint64_t credits;
+ std::unordered_set<uint64_t> payments;
+ std::unordered_set<uint64_t> previous_payments;
+ uint64_t update_time;
+ uint64_t last_request_timestamp;
+ uint64_t block_template_update_time;
+ uint64_t credits_total;
+ uint64_t credits_used;
+ uint64_t nonces_good;
+ uint64_t nonces_stale;
+ uint64_t nonces_bad;
+ uint64_t nonces_dupe;
+
+ client_info();
+
+ template <class t_archive>
+ inline void serialize(t_archive &a, const unsigned int ver)
+ {
+ a & block;
+ a & previous_block;
+ a & hashing_blob;
+ a & previous_hashing_blob;
+ a & seed_height;
+ a & previous_seed_height;
+ a & seed_hash;
+ a & previous_seed_hash;
+ a & cookie;
+ a & top;
+ a & previous_top;
+ a & credits;
+ a & payments;
+ a & previous_payments;
+ a & update_time;
+ a & last_request_timestamp;
+ a & block_template_update_time;
+ a & credits_total;
+ a & credits_used;
+ a & nonces_good;
+ a & nonces_stale;
+ a & nonces_bad;
+ a & nonces_dupe;
+ }
+ };
+
+ public:
+ rpc_payment(const cryptonote::account_public_address &address, uint64_t diff, uint64_t credits_per_hash_found);
+ uint64_t balance(const crypto::public_key &client, int64_t delta = 0);
+ bool pay(const crypto::public_key &client, uint64_t ts, uint64_t payment, const std::string &rpc, bool same_ts, uint64_t &credits);
+ bool get_info(const crypto::public_key &client, const std::function<bool(const cryptonote::blobdata&, cryptonote::block&, uint64_t &seed_height, crypto::hash &seed_hash)> &get_block_template, cryptonote::blobdata &hashing_blob, uint64_t &seed_height, crypto::hash &seed_hash, const crypto::hash &top, uint64_t &diff, uint64_t &credits_per_hash_found, uint64_t &credits, uint32_t &cookie);
+ bool submit_nonce(const crypto::public_key &client, uint32_t nonce, const crypto::hash &top, int64_t &error_code, std::string &error_message, uint64_t &credits, crypto::hash &hash, cryptonote::block &block, uint32_t cookie, bool &stale);
+ const cryptonote::account_public_address &get_payment_address() const { return m_address; }
+ bool foreach(const std::function<bool(const crypto::public_key &client, const client_info &info)> &f) const;
+ unsigned int flush_by_age(time_t seconds = 0);
+ uint64_t get_hashes(unsigned int seconds) const;
+ void prune_hashrate(unsigned int seconds);
+ bool on_idle();
+
+ template <class t_archive>
+ inline void serialize(t_archive &a, const unsigned int ver)
+ {
+ a & m_client_info;
+ a & m_hashrate;
+ a & m_credits_total;
+ a & m_credits_used;
+ a & m_nonces_good;
+ a & m_nonces_stale;
+ a & m_nonces_bad;
+ a & m_nonces_dupe;
+ }
+
+ bool load(std::string directory);
+ bool store(const std::string &directory = std::string()) const;
+
+ private:
+ cryptonote::account_public_address m_address;
+ uint64_t m_diff;
+ uint64_t m_credits_per_hash_found;
+ std::unordered_map<crypto::public_key, client_info> m_client_info;
+ std::string m_directory;
+ std::map<uint64_t, uint64_t> m_hashrate;
+ uint64_t m_credits_total;
+ uint64_t m_credits_used;
+ uint64_t m_nonces_good;
+ uint64_t m_nonces_stale;
+ uint64_t m_nonces_bad;
+ uint64_t m_nonces_dupe;
+ };
+}
+
+BOOST_CLASS_VERSION(cryptonote::rpc_payment, 0);
+BOOST_CLASS_VERSION(cryptonote::rpc_payment::client_info, 0);
diff --git a/src/rpc/rpc_payment_costs.h b/src/rpc/rpc_payment_costs.h
new file mode 100644
index 000000000..3b27bf286
--- /dev/null
+++ b/src/rpc/rpc_payment_costs.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2019, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#pragma once
+
+#define COST_PER_BLOCK 0.05
+#define COST_PER_TX_RELAY 100
+#define COST_PER_OUT 1
+#define COST_PER_OUTPUT_INDEXES 1
+#define COST_PER_TX 0.5
+#define COST_PER_KEY_IMAGE 0.01
+#define COST_PER_POOL_HASH 0.01
+#define COST_PER_TX_POOL_STATS 0.2
+#define COST_PER_BLOCK_HEADER 0.1
+#define COST_PER_GET_INFO 1
+#define COST_PER_OUTPUT_HISTOGRAM 25000
+#define COST_PER_FULL_OUTPUT_HISTOGRAM 5000000
+#define COST_PER_OUTPUT_DISTRIBUTION_0 20
+#define COST_PER_OUTPUT_DISTRIBUTION 50000
+#define COST_PER_COINBASE_TX_SUM_BLOCK 2
+#define COST_PER_BLOCK_HASH 0.002
+#define COST_PER_FEE_ESTIMATE 1
+#define COST_PER_SYNC_INFO 2
+#define COST_PER_HARD_FORK_INFO 1
+#define COST_PER_PEER_LIST 2
diff --git a/src/rpc/rpc_payment_signature.cpp b/src/rpc/rpc_payment_signature.cpp
new file mode 100644
index 000000000..2e8b54b4f
--- /dev/null
+++ b/src/rpc/rpc_payment_signature.cpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2018-2019, 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 <cinttypes>
+#include <stdlib.h>
+#include <chrono>
+#include "include_base_utils.h"
+#include "string_tools.h"
+#include "rpc_payment_signature.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc.payment"
+
+#define TIMESTAMP_LEEWAY (60 * 1000000) /* 60 seconds, in microseconds */
+
+namespace cryptonote
+{
+ std::string make_rpc_payment_signature(const crypto::secret_key &skey)
+ {
+ std::string s;
+ crypto::public_key pkey;
+ crypto::secret_key_to_public_key(skey, pkey);
+ crypto::signature sig;
+ const uint64_t now = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+ char ts[17];
+ int ret = snprintf(ts, sizeof(ts), "%16.16" PRIx64, now);
+ CHECK_AND_ASSERT_MES(ret == 16, "", "snprintf failed");
+ ts[16] = 0;
+ CHECK_AND_ASSERT_MES(strlen(ts) == 16, "", "Invalid time conversion");
+ crypto::hash hash;
+ crypto::cn_fast_hash(ts, 16, hash);
+ crypto::generate_signature(hash, pkey, skey, sig);
+ s = epee::string_tools::pod_to_hex(pkey) + ts + epee::string_tools::pod_to_hex(sig);
+ return s;
+ }
+
+ bool verify_rpc_payment_signature(const std::string &message, crypto::public_key &pkey, uint64_t &ts)
+ {
+ if (message.size() != 2 * sizeof(crypto::public_key) + 16 + 2 * sizeof(crypto::signature))
+ {
+ MDEBUG("Bad message size: " << message.size());
+ return false;
+ }
+ const std::string pkey_string = message.substr(0, 2 * sizeof(crypto::public_key));
+ const std::string ts_string = message.substr(2 * sizeof(crypto::public_key), 16);
+ const std::string signature_string = message.substr(2 * sizeof(crypto::public_key) + 16);
+ if (!epee::string_tools::hex_to_pod(pkey_string, pkey))
+ {
+ MDEBUG("Bad client id");
+ return false;
+ }
+ crypto::signature signature;
+ if (!epee::string_tools::hex_to_pod(signature_string, signature))
+ {
+ MDEBUG("Bad signature");
+ return false;
+ }
+ crypto::hash hash;
+ crypto::cn_fast_hash(ts_string.data(), 16, hash);
+ if (!crypto::check_signature(hash, pkey, signature))
+ {
+ MDEBUG("signature does not verify");
+ return false;
+ }
+ char *endptr = NULL;
+ errno = 0;
+ unsigned long long ull = strtoull(ts_string.c_str(), &endptr, 16);
+ if (ull == ULLONG_MAX && errno == ERANGE)
+ {
+ MDEBUG("bad timestamp");
+ return false;
+ }
+ ts = ull;
+ const uint64_t now = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
+ if (ts > now + TIMESTAMP_LEEWAY)
+ {
+ MDEBUG("Timestamp is in the future");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/rpc/rpc_payment_signature.h b/src/rpc/rpc_payment_signature.h
new file mode 100644
index 000000000..4a2fe2ea3
--- /dev/null
+++ b/src/rpc/rpc_payment_signature.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2018-2019, 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 <stdint.h>
+#include <string>
+#include "crypto/crypto.h"
+
+namespace cryptonote
+{
+ std::string make_rpc_payment_signature(const crypto::secret_key &skey);
+ bool verify_rpc_payment_signature(const std::string &message, crypto::public_key &pkey, uint64_t &ts);
+}
diff --git a/src/rpc/rpc_version_str.cpp b/src/rpc/rpc_version_str.cpp
new file mode 100644
index 000000000..c60cf4891
--- /dev/null
+++ b/src/rpc/rpc_version_str.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2019, 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_version_str.h"
+#include "version.h"
+#include <regex>
+
+namespace cryptonote
+{
+
+namespace rpc
+{
+
+// Expected format of Monero software version string:
+// 1) Four numbers, one to two digits each, separated by periods
+// 2) Optionally, one of the following suffixes:
+// a) -release
+// b) -<hash> where <hash> is exactly nine lowercase hex digits
+
+bool is_version_string_valid(const std::string& str)
+{
+ return std::regex_match(str, std::regex(
+ "^\\d{1,2}(\\.\\d{1,2}){3}(-(release|[0-9a-f]{9}))?$",
+ std::regex_constants::nosubs
+ ));
+}
+
+} // namespace rpc
+
+} // namespace cryptonote
diff --git a/src/blockchain_db/db_types.h b/src/rpc/rpc_version_str.h
index 04cadbb10..930c807d2 100644
--- a/src/blockchain_db/db_types.h
+++ b/src/rpc/rpc_version_str.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2019, The Monero Project
//
// All rights reserved.
//
@@ -26,11 +26,18 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
-// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
+#include <string>
+
namespace cryptonote
{
- bool blockchain_valid_db_type(const std::string& db_type);
- std::string blockchain_db_types(const std::string& sep);
+
+namespace rpc
+{
+
+bool is_version_string_valid(const std::string& str);
+
+} // namespace rpc
+
} // namespace cryptonote
diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp
index 668a2e5cd..1ee55673e 100644
--- a/src/rpc/zmq_server.cpp
+++ b/src/rpc/zmq_server.cpp
@@ -28,18 +28,29 @@
#include "zmq_server.h"
+#include <chrono>
+#include <cstdint>
+#include <system_error>
+
namespace cryptonote
{
+namespace
+{
+ constexpr const int num_zmq_threads = 1;
+ constexpr const std::int64_t max_message_size = 10 * 1024 * 1024; // 10 MiB
+ constexpr const std::chrono::seconds linger_timeout{2}; // wait period for pending out messages
+}
+
namespace rpc
{
ZmqServer::ZmqServer(RpcHandler& h) :
handler(h),
- stop_signal(false),
- running(false),
- context(DEFAULT_NUM_ZMQ_THREADS) // TODO: make this configurable
+ context(zmq_init(num_zmq_threads))
{
+ if (!context)
+ MONERO_ZMQ_THROW("Unable to create ZMQ context");
}
ZmqServer::~ZmqServer()
@@ -48,71 +59,88 @@ ZmqServer::~ZmqServer()
void ZmqServer::serve()
{
-
- while (1)
+ try
{
- try
+ // socket must close before `zmq_term` will exit.
+ const net::zmq::socket socket = std::move(rep_socket);
+ if (!socket)
{
- zmq::message_t message;
-
- if (!rep_socket)
- {
- throw std::runtime_error("ZMQ RPC server reply socket is null");
- }
- while (rep_socket->recv(&message, 0))
- {
- std::string message_string(reinterpret_cast<const char *>(message.data()), message.size());
-
- MDEBUG(std::string("Received RPC request: \"") + message_string + "\"");
-
- std::string response = handler.handle(message_string);
-
- zmq::message_t reply(response.size());
- memcpy((void *) reply.data(), response.c_str(), response.size());
-
- rep_socket->send(reply);
- MDEBUG(std::string("Sent RPC reply: \"") + response + "\"");
-
- }
- }
- catch (const boost::thread_interrupted& e)
- {
- MDEBUG("ZMQ Server thread interrupted.");
+ MERROR("ZMQ RPC server reply socket is null");
+ return;
}
- catch (const zmq::error_t& e)
+
+ while (1)
{
- MERROR(std::string("ZMQ error: ") + e.what());
+ const std::string message = MONERO_UNWRAP(net::zmq::receive(socket.get()));
+ MDEBUG("Received RPC request: \"" << message << "\"");
+ const std::string& response = handler.handle(message);
+
+ MONERO_UNWRAP(net::zmq::send(epee::strspan<std::uint8_t>(response), socket.get()));
+ MDEBUG("Sent RPC reply: \"" << response << "\"");
}
- boost::this_thread::interruption_point();
+ }
+ catch (const std::system_error& e)
+ {
+ if (e.code() != net::zmq::make_error_code(ETERM))
+ MERROR("ZMQ RPC Server Error: " << e.what());
+ }
+ catch (const std::exception& e)
+ {
+ MERROR("ZMQ RPC Server Error: " << e.what());
+ }
+ catch (...)
+ {
+ MERROR("Unknown error in ZMQ RPC server");
}
}
-bool ZmqServer::addIPCSocket(std::string address, std::string port)
+bool ZmqServer::addIPCSocket(const boost::string_ref address, const boost::string_ref port)
{
MERROR("ZmqServer::addIPCSocket not yet implemented!");
return false;
}
-bool ZmqServer::addTCPSocket(std::string address, std::string port)
+bool ZmqServer::addTCPSocket(boost::string_ref address, boost::string_ref port)
{
- try
+ if (!context)
{
- std::string addr_prefix("tcp://");
+ MERROR("ZMQ RPC Server already shutdown");
+ return false;
+ }
- rep_socket.reset(new zmq::socket_t(context, ZMQ_REP));
+ rep_socket.reset(zmq_socket(context.get(), ZMQ_REP));
+ if (!rep_socket)
+ {
+ MONERO_LOG_ZMQ_ERROR("ZMQ RPC Server socket create failed");
+ return false;
+ }
- rep_socket->setsockopt(ZMQ_RCVTIMEO, &DEFAULT_RPC_RECV_TIMEOUT_MS, sizeof(DEFAULT_RPC_RECV_TIMEOUT_MS));
+ if (zmq_setsockopt(rep_socket.get(), ZMQ_MAXMSGSIZE, std::addressof(max_message_size), sizeof(max_message_size)) != 0)
+ {
+ MONERO_LOG_ZMQ_ERROR("Failed to set maximum incoming message size");
+ return false;
+ }
- if (address.empty())
- address = "*";
- if (port.empty())
- port = "*";
- std::string bind_address = addr_prefix + address + std::string(":") + port;
- rep_socket->bind(bind_address.c_str());
+ static constexpr const int linger_value = std::chrono::milliseconds{linger_timeout}.count();
+ if (zmq_setsockopt(rep_socket.get(), ZMQ_LINGER, std::addressof(linger_value), sizeof(linger_value)) != 0)
+ {
+ MONERO_LOG_ZMQ_ERROR("Failed to set linger timeout");
+ return false;
}
- catch (const std::exception& e)
+
+ if (address.empty())
+ address = "*";
+ if (port.empty())
+ port = "*";
+
+ std::string bind_address = "tcp://";
+ bind_address.append(address.data(), address.size());
+ bind_address += ":";
+ bind_address.append(port.data(), port.size());
+
+ if (zmq_bind(rep_socket.get(), bind_address.c_str()) < 0)
{
- MERROR(std::string("Error creating ZMQ Socket: ") + e.what());
+ MONERO_LOG_ZMQ_ERROR("ZMQ RPC Server bind failed");
return false;
}
return true;
@@ -120,22 +148,16 @@ bool ZmqServer::addTCPSocket(std::string address, std::string port)
void ZmqServer::run()
{
- running = true;
run_thread = boost::thread(boost::bind(&ZmqServer::serve, this));
}
void ZmqServer::stop()
{
- if (!running) return;
-
- stop_signal = true;
+ if (!run_thread.joinable())
+ return;
- run_thread.interrupt();
+ context.reset(); // destroying context terminates all calls
run_thread.join();
-
- running = false;
-
- return;
}
diff --git a/src/rpc/zmq_server.h b/src/rpc/zmq_server.h
index 1b1e4c7cf..ce7892dab 100644
--- a/src/rpc/zmq_server.h
+++ b/src/rpc/zmq_server.h
@@ -29,12 +29,10 @@
#pragma once
#include <boost/thread/thread.hpp>
-#include <zmq.hpp>
-#include <string>
-#include <memory>
+#include <boost/utility/string_ref.hpp>
#include "common/command_line.h"
-
+#include "net/zmq.h"
#include "rpc_handler.h"
namespace cryptonote
@@ -43,9 +41,6 @@ namespace cryptonote
namespace rpc
{
-static constexpr int DEFAULT_NUM_ZMQ_THREADS = 1;
-static constexpr int DEFAULT_RPC_RECV_TIMEOUT_MS = 1000;
-
class ZmqServer
{
public:
@@ -58,8 +53,8 @@ class ZmqServer
void serve();
- bool addIPCSocket(std::string address, std::string port);
- bool addTCPSocket(std::string address, std::string port);
+ bool addIPCSocket(boost::string_ref address, boost::string_ref port);
+ bool addTCPSocket(boost::string_ref address, boost::string_ref port);
void run();
void stop();
@@ -67,14 +62,11 @@ class ZmqServer
private:
RpcHandler& handler;
- volatile bool stop_signal;
- volatile bool running;
-
- zmq::context_t context;
+ net::zmq::context context;
boost::thread run_thread;
- std::unique_ptr<zmq::socket_t> rep_socket;
+ net::zmq::socket rep_socket;
};
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index cc52bde58..ea67209dc 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -571,6 +571,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& in
INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip);
INSERT_INTO_JSON_OBJECT(val, doc, port, info.port);
INSERT_INTO_JSON_OBJECT(val, doc, rpc_port, info.rpc_port);
+ INSERT_INTO_JSON_OBJECT(val, doc, rpc_credits_per_hash, info.rpc_credits_per_hash);
INSERT_INTO_JSON_OBJECT(val, doc, peer_id, info.peer_id);
@@ -607,6 +608,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf
GET_FROM_JSON_OBJECT(val, info.ip, ip);
GET_FROM_JSON_OBJECT(val, info.port, port);
GET_FROM_JSON_OBJECT(val, info.rpc_port, rpc_port);
+ GET_FROM_JSON_OBJECT(val, info.rpc_credits_per_hash, rpc_credits_per_hash);
GET_FROM_JSON_OBJECT(val, info.peer_id, peer_id);
@@ -627,6 +629,25 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf
GET_FROM_JSON_OBJECT(val, info.current_upload, current_upload);
}
+void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_blob_entry& tx, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, blob, tx.blob);
+ INSERT_INTO_JSON_OBJECT(val, doc, prunable_hash, tx.prunable_hash);
+}
+
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, tx.blob, blob);
+ GET_FROM_JSON_OBJECT(val, tx.prunable_hash, prunable_hash);
+}
+
void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val)
{
val.SetObject();
@@ -737,6 +758,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::peer& peer, ra
INSERT_INTO_JSON_OBJECT(val, doc, ip, peer.ip);
INSERT_INTO_JSON_OBJECT(val, doc, port, peer.port);
INSERT_INTO_JSON_OBJECT(val, doc, rpc_port, peer.rpc_port);
+ INSERT_INTO_JSON_OBJECT(val, doc, rpc_credits_per_hash, peer.rpc_credits_per_hash);
INSERT_INTO_JSON_OBJECT(val, doc, last_seen, peer.last_seen);
INSERT_INTO_JSON_OBJECT(val, doc, pruning_seed, peer.pruning_seed);
}
@@ -753,6 +775,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::peer& peer)
GET_FROM_JSON_OBJECT(val, peer.ip, ip);
GET_FROM_JSON_OBJECT(val, peer.port, port);
GET_FROM_JSON_OBJECT(val, peer.rpc_port, rpc_port);
+ GET_FROM_JSON_OBJECT(val, peer.rpc_credits_per_hash, rpc_credits_per_hash);
GET_FROM_JSON_OBJECT(val, peer.last_seen, last_seen);
GET_FROM_JSON_OBJECT(val, peer.pruning_seed, pruning_seed);
}
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index c804d148b..5ef75b863 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -221,6 +221,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_out& txout);
void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& info, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& info);
+void toJsonValue(rapidjson::Document& doc, const cryptonote::tx_blob_entry& tx, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, cryptonote::tx_blob_entry& tx);
+
void toJsonValue(rapidjson::Document& doc, const cryptonote::block_complete_entry& blk, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, cryptonote::block_complete_entry& blk);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index eee2fa61d..87bbf62d3 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -58,6 +58,7 @@
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "storages/http_abstract_invoke.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "rpc/rpc_payment_signature.h"
#include "crypto/crypto.h" // for crypto::secret_key definition
#include "mnemonics/electrum-words.h"
#include "rapidjson/document.h"
@@ -99,12 +100,17 @@ typedef cryptonote::simple_wallet sw;
#define LOCK_IDLE_SCOPE() \
bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
- /* stop any background refresh, and take over */ \
+ /* stop any background refresh and other processes, and take over */ \
+ m_suspend_rpc_payment_mining.store(true, std::memory_order_relaxed); \
m_wallet->stop(); \
boost::unique_lock<boost::mutex> lock(m_idle_mutex); \
m_idle_cond.notify_all(); \
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ \
+ /* m_idle_mutex is still locked here */ \
m_auto_refresh_enabled.store(auto_refresh_enabled, std::memory_order_relaxed); \
+ m_suspend_rpc_payment_mining.store(false, std::memory_order_relaxed);; \
+ m_rpc_payment_checker.trigger(); \
+ m_idle_cond.notify_one(); \
})
#define SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(code) \
@@ -119,23 +125,30 @@ typedef cryptonote::simple_wallet sw;
#define LONG_PAYMENT_ID_SUPPORT_CHECK() \
do { \
- if (!m_long_payment_id_support) { \
- fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \
- fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \
- fail_msg_writer() << tr("Use --long-payment-id-support-bad-for-privacy if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \
- return true; \
- } \
+ fail_msg_writer() << tr("Error: Long payment IDs are obsolete."); \
+ fail_msg_writer() << tr("Long payment IDs were not encrypted on the blockchain and would harm your privacy."); \
+ fail_msg_writer() << tr("If the party you're sending to still requires a long payment ID, please notify them."); \
+ return true; \
} while(0)
+#define REFRESH_PERIOD 90 // seconds
+
+#define CREDITS_TARGET 50000
+#define MAX_PAYMENT_DIFF 10000
+#define MIN_PAYMENT_RATE 0.01f // per hash
+
enum TransferType {
Transfer,
TransferLocked,
};
+static std::string get_human_readable_timespan(std::chrono::seconds seconds);
+
namespace
{
const std::array<const char* const, 5> allowed_priority_strings = {{"default", "unimportant", "normal", "elevated", "priority"}};
const auto arg_wallet_file = wallet_args::arg_wallet_file();
+ const auto arg_rpc_client_secret_key = wallet_args::arg_rpc_client_secret_key();
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_device = {"generate-from-device", sw::tr("Generate new wallet from device 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"), ""};
@@ -146,6 +159,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_mnemonic_language = {"mnemonic-language", sw::tr("Language for mnemonic"), ""};
const command_line::arg_descriptor<std::string> arg_electrum_seed = {"electrum-seed", sw::tr("Specify Electrum seed for wallet recovery/creation"), ""};
const command_line::arg_descriptor<bool> arg_restore_deterministic_wallet = {"restore-deterministic-wallet", sw::tr("Recover wallet using Electrum-style mnemonic seed"), false};
+ const command_line::arg_descriptor<bool> arg_restore_from_seed = {"restore-from-seed", sw::tr("alias for --restore-deterministic-wallet"), false};
const command_line::arg_descriptor<bool> arg_restore_multisig_wallet = {"restore-multisig-wallet", sw::tr("Recover multisig wallet using Electrum-style mnemonic seed"), false};
const command_line::arg_descriptor<bool> arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false};
const command_line::arg_descriptor<bool> arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false};
@@ -155,7 +169,6 @@ namespace
const command_line::arg_descriptor<bool> arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false};
const command_line::arg_descriptor<std::string> arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to <major>:<minor>"), ""};
const command_line::arg_descriptor<bool> arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false};
- const command_line::arg_descriptor<bool> arg_long_payment_id_support = {"long-payment-id-support-bad-for-privacy", sw::tr("Support obsolete long (unencrypted) payment ids (using them harms your privacy)"), false};
const command_line::arg_descriptor< std::vector<std::string> > arg_command = {"command", ""};
@@ -249,7 +262,11 @@ namespace
const char* USAGE_FROZEN("frozen <key_image>");
const char* USAGE_LOCK("lock");
const char* USAGE_NET_STATS("net_stats");
+ const char* USAGE_PUBLIC_NODES("public_nodes");
const char* USAGE_WELCOME("welcome");
+ const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info");
+ const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc");
+ const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc");
const char* USAGE_VERSION("version");
const char* USAGE_HELP("help [<command>]");
@@ -492,22 +509,28 @@ namespace
fail_msg_writer() << sw::tr("invalid format for subaddress lookahead; must be <major>:<minor>");
return r;
}
+}
- void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon)
- {
+void simple_wallet::handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon)
+{
bool warn_of_possible_attack = !trusted_daemon;
try
{
std::rethrow_exception(e);
}
- catch (const tools::error::daemon_busy&)
+ catch (const tools::error::payment_required&)
{
- fail_msg_writer() << sw::tr("daemon is busy. Please try again later.");
+ fail_msg_writer() << tr("Payment required, see the 'rpc_payment_info' command");
+ m_need_payment = true;
}
catch (const tools::error::no_connection_to_daemon&)
{
fail_msg_writer() << sw::tr("no connection to daemon. Please make sure daemon is running.");
}
+ catch (const tools::error::daemon_busy&)
+ {
+ fail_msg_writer() << tr("daemon is busy. Please try again later.");
+ }
catch (const tools::error::wallet_rpc_error& e)
{
LOG_ERROR("RPC error: " << e.to_string());
@@ -604,8 +627,10 @@ namespace
if (warn_of_possible_attack)
fail_msg_writer() << sw::tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information.");
- }
+}
+namespace
+{
bool check_file_overwrite(const std::string &filename)
{
boost::system::error_code errcode;
@@ -887,16 +912,6 @@ bool simple_wallet::change_password(const std::vector<std::string> &args)
bool simple_wallet::payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
LONG_PAYMENT_ID_SUPPORT_CHECK();
-
- crypto::hash payment_id;
- if (args.size() > 0)
- {
- PRINT_USAGE(USAGE_PAYMENT_ID);
- return true;
- }
- payment_id = crypto::rand<crypto::hash>();
- success_msg_writer() << tr("Random payment ID: ") << payment_id;
- return true;
}
bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
@@ -1910,6 +1925,77 @@ bool simple_wallet::unset_ring(const std::vector<std::string> &args)
return true;
}
+bool simple_wallet::rpc_payment_info(const std::vector<std::string> &args)
+{
+ if (!try_connect_to_daemon())
+ return true;
+
+ LOCK_IDLE_SCOPE();
+
+ try
+ {
+ bool payment_required;
+ uint64_t credits, diff, credits_per_hash_found, height, seed_height;
+ uint32_t cookie;
+ std::string hashing_blob;
+ crypto::hash seed_hash, next_seed_hash;
+ crypto::public_key pkey;
+ crypto::secret_key_to_public_key(m_wallet->get_rpc_client_secret_key(), pkey);
+ message_writer() << tr("RPC client ID: ") << pkey;
+ message_writer() << tr("RPC client secret key: ") << m_wallet->get_rpc_client_secret_key();
+ if (!m_wallet->get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie))
+ {
+ fail_msg_writer() << tr("Failed to query daemon");
+ return true;
+ }
+ if (payment_required)
+ {
+ uint64_t target = m_wallet->credits_target();
+ if (target == 0)
+ target = CREDITS_TARGET;
+ message_writer() << tr("Using daemon: ") << m_wallet->get_daemon_address();
+ message_writer() << tr("Payments required for node use, current credits: ") << credits;
+ message_writer() << tr("Credits target: ") << target;
+ uint64_t expected, discrepancy;
+ m_wallet->credit_report(expected, discrepancy);
+ message_writer() << tr("Credits spent this session: ") << expected;
+ if (expected)
+ message_writer() << tr("Credit discrepancy this session: ") << discrepancy << " (" << 100.0f * discrepancy / expected << "%)";
+ float cph = credits_per_hash_found / (float)diff;
+ message_writer() << tr("Difficulty: ") << diff << ", " << credits_per_hash_found << " " << tr("credits per hash found, ") << cph << " " << tr("credits/hash");;
+ const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
+ bool mining = (now - m_last_rpc_payment_mining_time).total_microseconds() < 1000000;
+ if (mining)
+ {
+ float hash_rate = m_rpc_payment_hash_rate;
+ if (hash_rate > 0)
+ {
+ message_writer() << (boost::format(tr("Mining for payment at %.1f H/s")) % hash_rate).str();
+ if (credits < target)
+ {
+ std::chrono::seconds seconds((unsigned)((target - credits) / cph / hash_rate));
+ std::string target_string = get_human_readable_timespan(seconds);
+ message_writer() << (boost::format(tr("Estimated time till %u credits target mined: %s")) % target % target_string).str();
+ }
+ }
+ else
+ message_writer() << tr("Mining for payment");
+ }
+ else
+ message_writer() << tr("Not mining");
+ }
+ else
+ message_writer() << tr("No payment needed for node use");
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR("unexpected error: " << e.what());
+ fail_msg_writer() << tr("unexpected error: ") << e.what();
+ }
+
+ return true;
+}
+
bool simple_wallet::blackball(const std::vector<std::string> &args)
{
uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets;
@@ -2152,13 +2238,52 @@ bool simple_wallet::net_stats(const std::vector<std::string> &args)
return true;
}
+bool simple_wallet::public_nodes(const std::vector<std::string> &args)
+{
+ try
+ {
+ auto nodes = m_wallet->get_public_nodes(false);
+ m_claimed_cph.clear();
+ if (nodes.empty())
+ {
+ fail_msg_writer() << tr("No known public nodes");
+ return true;
+ }
+ std::sort(nodes.begin(), nodes.end(), [](const public_node &node0, const public_node &node1) {
+ if (node0.rpc_credits_per_hash && node1.rpc_credits_per_hash == 0)
+ return true;
+ if (node0.rpc_credits_per_hash && node1.rpc_credits_per_hash)
+ return node0.rpc_credits_per_hash < node1.rpc_credits_per_hash;
+ return false;
+ });
+
+ const uint64_t now = time(NULL);
+ message_writer() << boost::format("%32s %12s %16s") % tr("address") % tr("credits/hash") % tr("last_seen");
+ for (const auto &node: nodes)
+ {
+ const float cph = node.rpc_credits_per_hash / RPC_CREDITS_PER_HASH_SCALE;
+ char cphs[9];
+ snprintf(cphs, sizeof(cphs), "%.3f", cph);
+ const std::string last_seen = node.last_seen == 0 ? tr("never") : get_human_readable_timespan(std::chrono::seconds(now - node.last_seen));
+ std::string host = node.host + ":" + std::to_string(node.rpc_port);
+ message_writer() << boost::format("%32s %12s %16s") % host % cphs % last_seen;
+ m_claimed_cph[host] = node.rpc_credits_per_hash;
+ }
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("Error retrieving public node list: ") << e.what();
+ }
+ return true;
+}
+
bool simple_wallet::welcome(const std::vector<std::string> &args)
{
message_writer() << tr("Welcome to Monero, the private cryptocurrency.");
message_writer() << "";
message_writer() << tr("Monero, like Bitcoin, is a cryptocurrency. That is, it is digital money.");
- message_writer() << tr("Unlike Bitcoin, your Monero transactions and balance stay private, and not visible to the world by default.");
- message_writer() << tr("However, you have the option of making those available to select parties, if you choose to.");
+ message_writer() << tr("Unlike Bitcoin, your Monero transactions and balance stay private and are not visible to the world by default.");
+ message_writer() << tr("However, you have the option of making those available to select parties if you choose to.");
message_writer() << "";
message_writer() << tr("Monero protects your privacy on the blockchain, and while Monero strives to improve all the time,");
message_writer() << tr("no privacy technology can be 100% perfect, Monero included.");
@@ -2166,7 +2291,7 @@ bool simple_wallet::welcome(const std::vector<std::string> &args)
message_writer() << tr("Flaws in Monero may be discovered in the future, and attacks may be developed to peek under some");
message_writer() << tr("of the layers of privacy Monero provides. Be safe and practice defense in depth.");
message_writer() << "";
- message_writer() << tr("Welcome to Monero and financial privacy. For more information, see https://getmonero.org/");
+ message_writer() << tr("Welcome to Monero and financial privacy. For more information see https://GetMonero.org");
return true;
}
@@ -2216,6 +2341,50 @@ bool simple_wallet::cold_sign_tx(const std::vector<tools::wallet2::pending_tx>&
return m_wallet->import_key_images(exported_txs, 0, true);
}
+bool simple_wallet::start_mining_for_rpc(const std::vector<std::string> &args)
+{
+ if (!try_connect_to_daemon())
+ return true;
+
+ LOCK_IDLE_SCOPE();
+
+ bool payment_required;
+ uint64_t credits, diff, credits_per_hash_found, height, seed_height;
+ uint32_t cookie;
+ std::string hashing_blob;
+ crypto::hash seed_hash, next_seed_hash;
+ if (!m_wallet->get_rpc_payment_info(true, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie))
+ {
+ fail_msg_writer() << tr("Failed to query daemon");
+ return true;
+ }
+ if (!payment_required)
+ {
+ fail_msg_writer() << tr("Daemon does not require payment for RPC access");
+ return true;
+ }
+
+ m_rpc_payment_mining_requested = true;
+ m_rpc_payment_checker.trigger();
+ const float cph = credits_per_hash_found / (float)diff;
+ bool low = (diff > MAX_PAYMENT_DIFF || cph < MIN_PAYMENT_RATE);
+ success_msg_writer() << (boost::format(tr("Starting mining for RPC access: diff %llu, %f credits/hash%s")) % diff % cph % (low ? " - this is low" : "")).str();
+ success_msg_writer() << tr("Run stop_mining_for_rpc to stop");
+ return true;
+}
+
+bool simple_wallet::stop_mining_for_rpc(const std::vector<std::string> &args)
+{
+ if (!try_connect_to_daemon())
+ return true;
+
+ LOCK_IDLE_SCOPE();
+ m_rpc_payment_mining_requested = false;
+ m_last_rpc_payment_mining_time = boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1));
+ m_rpc_payment_hash_rate = -1.0f;
+ return true;
+}
+
bool simple_wallet::set_always_confirm_transfers(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@@ -2283,9 +2452,16 @@ bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/*
}
if (ring_size != 0 && ring_size != DEFAULT_MIX+1)
- message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
- else if (ring_size == DEFAULT_MIX)
- message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored.");
+ {
+ if (m_wallet->use_fork_rules(8, 0))
+ {
+ message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored.");
+ }
+ else
+ {
+ message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
+ }
+ }
const auto pwd_container = get_and_verify_password();
if (pwd_container)
@@ -2399,21 +2575,6 @@ bool simple_wallet::set_refresh_type(const std::vector<std::string> &args/* = st
return true;
}
-bool simple_wallet::set_confirm_missing_payment_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
-{
- LONG_PAYMENT_ID_SUPPORT_CHECK();
-
- const auto pwd_container = get_and_verify_password();
- if (pwd_container)
- {
- parse_bool_and_use(args[1], [&](bool r) {
- m_wallet->confirm_missing_payment_id(r);
- 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();
@@ -2612,6 +2773,53 @@ bool simple_wallet::set_segregate_pre_fork_outputs(const std::vector<std::string
return true;
}
+bool simple_wallet::set_persistent_rpc_client_id(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ parse_bool_and_use(args[1], [&](bool r) {
+ m_wallet->persistent_rpc_client_id(r);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ });
+ }
+ return true;
+}
+
+bool simple_wallet::set_auto_mine_for_rpc_payment_threshold(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ float threshold;
+ if (!epee::string_tools::get_xtype_from_string(threshold, args[1]) || threshold < 0.0f)
+ {
+ fail_msg_writer() << tr("Invalid threshold");
+ return true;
+ }
+ m_wallet->auto_mine_for_rpc_payment_threshold(threshold);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
+bool simple_wallet::set_credits_target(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ uint64_t target;
+ if (!epee::string_tools::get_xtype_from_string(target, args[1]))
+ {
+ fail_msg_writer() << tr("Invalid target");
+ return true;
+ }
+ m_wallet->credits_target(target);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
bool simple_wallet::set_key_reuse_mitigation2(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@@ -2670,6 +2878,43 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector<std::string>
return true;
}
+
+bool simple_wallet::set_ignore_outputs_above(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ uint64_t amount;
+ if (!cryptonote::parse_amount(amount, args[1]))
+ {
+ fail_msg_writer() << tr("Invalid amount");
+ return true;
+ }
+ if (amount == 0)
+ amount = MONEY_SUPPLY;
+ m_wallet->ignore_outputs_above(amount);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
+bool simple_wallet::set_ignore_outputs_below(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ const auto pwd_container = get_and_verify_password();
+ if (pwd_container)
+ {
+ uint64_t amount;
+ if (!cryptonote::parse_amount(amount, args[1]))
+ {
+ fail_msg_writer() << tr("Invalid amount");
+ return true;
+ }
+ m_wallet->ignore_outputs_below(amount);
+ m_wallet->rewrite(m_wallet_file, pwd_container->password());
+ }
+ return true;
+}
+
bool simple_wallet::set_track_uses(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
const auto pwd_container = get_and_verify_password();
@@ -2817,6 +3062,12 @@ simple_wallet::simple_wallet()
, m_last_activity_time(time(NULL))
, m_locked(false)
, m_in_command(false)
+ , m_need_payment(false)
+ , m_rpc_payment_mining_requested(false)
+ , m_last_rpc_payment_mining_time(boost::gregorian::date(1970, 1, 1))
+ , m_daemon_rpc_payment_message_displayed(false)
+ , m_rpc_payment_hash_rate(-1.0f)
+ , m_suspend_rpc_payment_mining(false)
{
m_cmd_binder.set_handler("start_mining",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::start_mining, _1),
@@ -2980,11 +3231,14 @@ simple_wallet::simple_wallet()
" Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n "
"subaddress-lookahead <major>:<minor>\n "
" Set the lookahead sizes for the subaddress hash table.\n "
- " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n "
"segregation-height <n>\n "
" Set to the height of a key reusing fork you want to use, 0 to use default.\n "
"ignore-fractional-outputs <1|0>\n "
" Whether to ignore fractional outputs that result in net loss when spending due to fee.\n "
+ "ignore-outputs-above <amount>\n "
+ " Ignore outputs of amount above this threshold when spending. Value 0 is translated to the maximum value (18 million) which disables this filter.\n "
+ "ignore-outputs-below <amount>\n "
+ " Ignore outputs of amount below this threshold when spending.\n "
"track-uses <1|0>\n "
" Whether to keep track of owned outputs uses.\n "
"setup-background-mining <1|0>\n "
@@ -2992,7 +3246,13 @@ simple_wallet::simple_wallet()
"device-name <device_name[:device_spec]>\n "
" Device name for hardware wallet.\n "
"export-format <\"binary\"|\"ascii\">\n "
- " Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n "));
+ " Save all exported files as binary (cannot be copied and pasted) or ascii (can be).\n "
+ "persistent-client-id <1|0>\n "
+ " Whether to keep using the same client id for RPC payment over wallet restarts.\n"
+ "auto-mine-for-rpc-payment-threshold <float>\n "
+ " Whether to automatically start mining for RPC payment if the daemon requires it.\n"
+ "credits-target <unsigned int>\n"
+ " The RPC payment credits balance to target (0 for default)."));
m_cmd_binder.set_handler("encrypted_seed",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::encrypted_seed, _1),
tr("Display the encrypted Electrum-style mnemonic seed."));
@@ -3295,6 +3555,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::on_command, this, &simple_wallet::net_stats, _1),
tr(USAGE_NET_STATS),
tr("Prints simple network stats"));
+ m_cmd_binder.set_handler("public_nodes",
+ boost::bind(&simple_wallet::public_nodes, this, _1),
+ tr(USAGE_PUBLIC_NODES),
+ tr("Lists known public nodes"));
m_cmd_binder.set_handler("welcome",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::welcome, _1),
tr(USAGE_WELCOME),
@@ -3303,6 +3567,18 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::on_command, this, &simple_wallet::version, _1),
tr(USAGE_VERSION),
tr("Returns version information"));
+ m_cmd_binder.set_handler("rpc_payment_info",
+ boost::bind(&simple_wallet::rpc_payment_info, this, _1),
+ tr(USAGE_RPC_PAYMENT_INFO),
+ tr("Get info about RPC payments to current node"));
+ m_cmd_binder.set_handler("start_mining_for_rpc",
+ boost::bind(&simple_wallet::start_mining_for_rpc, this, _1),
+ tr(USAGE_START_MINING_FOR_RPC),
+ tr("Start mining to pay for RPC access"));
+ m_cmd_binder.set_handler("stop_mining_for_rpc",
+ boost::bind(&simple_wallet::stop_mining_for_rpc, this, _1),
+ tr(USAGE_STOP_MINING_FOR_RPC),
+ tr("Stop mining to pay for RPC access"));
m_cmd_binder.set_handler("help",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::help, _1),
tr(USAGE_HELP),
@@ -3345,7 +3621,6 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "auto-refresh = " << m_wallet->auto_refresh();
success_msg_writer() << "refresh-type = " << get_refresh_type_name(m_wallet->get_refresh_type());
success_msg_writer() << "priority = " << priority<< " (" << priority_string << ")";
- success_msg_writer() << "confirm-missing-payment-id = " << m_wallet->confirm_missing_payment_id();
success_msg_writer() << "ask-password = " << m_wallet->ask_password() << " (" << ask_password_string << ")";
success_msg_writer() << "unit = " << cryptonote::get_unit(cryptonote::get_default_decimal_point());
success_msg_writer() << "min-outputs-count = " << m_wallet->get_min_output_count();
@@ -3362,6 +3637,8 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second;
success_msg_writer() << "segregation-height = " << m_wallet->segregation_height();
success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs();
+ success_msg_writer() << "ignore-outputs-above = " << cryptonote::print_money(m_wallet->ignore_outputs_above());
+ success_msg_writer() << "ignore-outputs-below = " << cryptonote::print_money(m_wallet->ignore_outputs_below());
success_msg_writer() << "track-uses = " << m_wallet->track_uses();
success_msg_writer() << "setup-background-mining = " << setup_background_mining_string;
success_msg_writer() << "device-name = " << m_wallet->device_name();
@@ -3371,6 +3648,9 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
<< " (disabled on Windows)"
#endif
;
+ success_msg_writer() << "persistent-rpc-client-id = " << m_wallet->persistent_rpc_client_id();
+ success_msg_writer() << "auto-mine-for-rpc-payment-threshold = " << m_wallet->auto_mine_for_rpc_payment_threshold();
+ success_msg_writer() << "credits-target = " << m_wallet->credits_target();
return true;
}
else
@@ -3410,7 +3690,6 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
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, or one of ") << join_priority_strings(", "));
- 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|1|2 (or never|action|decrypt)"));
CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanonero, piconero"));
CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer"));
@@ -3426,11 +3705,16 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
CHECK_SIMPLE_VARIABLE("subaddress-lookahead", set_subaddress_lookahead, tr("<major>:<minor>"));
CHECK_SIMPLE_VARIABLE("segregation-height", set_segregation_height, tr("unsigned integer"));
CHECK_SIMPLE_VARIABLE("ignore-fractional-outputs", set_ignore_fractional_outputs, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("ignore-outputs-above", set_ignore_outputs_above, tr("amount"));
+ CHECK_SIMPLE_VARIABLE("ignore-outputs-below", set_ignore_outputs_below, tr("amount"));
CHECK_SIMPLE_VARIABLE("track-uses", set_track_uses, tr("0 or 1"));
CHECK_SIMPLE_VARIABLE("inactivity-lock-timeout", set_inactivity_lock_timeout, tr("unsigned integer (seconds, 0 to disable)"));
CHECK_SIMPLE_VARIABLE("setup-background-mining", set_setup_background_mining, tr("1/yes or 0/no"));
CHECK_SIMPLE_VARIABLE("device-name", set_device_name, tr("<device_name[:device_spec]>"));
CHECK_SIMPLE_VARIABLE("export-format", set_export_format, tr("\"binary\" or \"ascii\""));
+ CHECK_SIMPLE_VARIABLE("persistent-rpc-client-id", set_persistent_rpc_client_id, tr("0 or 1"));
+ CHECK_SIMPLE_VARIABLE("auto-mine-for-rpc-payment-threshold", set_auto_mine_for_rpc_payment_threshold, tr("floating point >= 0"));
+ CHECK_SIMPLE_VARIABLE("credits-target", set_credits_target, tr("unsigned integer"));
}
fail_msg_writer() << tr("set: unrecognized argument(s)");
return true;
@@ -4062,7 +4346,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (std::cin.eof() || !command_line::is_yes(confirm))
CHECK_AND_ASSERT_MES(false, false, tr("account creation aborted"));
- m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height());
+ m_wallet->set_refresh_from_block_height(m_wallet->estimate_blockchain_height() > 0 ? m_wallet->estimate_blockchain_height() - 1 : 0);
m_wallet->explicit_refresh_from_block_height(true);
m_restore_height = m_wallet->get_refresh_from_block_height();
}
@@ -4195,6 +4479,17 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
return false;
}
+ if (!command_line::is_arg_defaulted(vm, arg_rpc_client_secret_key))
+ {
+ crypto::secret_key rpc_client_secret_key;
+ if (!epee::string_tools::hex_to_pod(command_line::get_arg(vm, arg_rpc_client_secret_key), rpc_client_secret_key))
+ {
+ fail_msg_writer() << tr("RPC client secret key should be 32 byte in hex format");
+ return false;
+ }
+ m_wallet->set_rpc_client_secret_key(rpc_client_secret_key);
+ }
+
if (!m_wallet->is_trusted_daemon())
{
message_writer(console_color_red, true) << (boost::format(tr("Warning: using an untrusted daemon at %s")) % m_wallet->get_daemon_address()).str();
@@ -4225,14 +4520,6 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (welcome)
message_writer(console_color_yellow, true) << tr("If you are new to Monero, type \"welcome\" for a brief overview.");
- if (m_long_payment_id_support)
- {
- message_writer(console_color_red, false) <<
- tr("WARNING: obsolete long payment IDs are enabled. Sending transactions with those payment IDs are bad for your privacy.");
- message_writer(console_color_red, false) <<
- tr("It is recommended that you do not use them, and ask recipients who ask for one to not endanger your privacy.");
- }
-
m_last_activity_time = time(NULL);
return true;
}
@@ -4257,7 +4544,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_generate_from_json = command_line::get_arg(vm, arg_generate_from_json);
m_mnemonic_language = command_line::get_arg(vm, arg_mnemonic_language);
m_electrum_seed = command_line::get_arg(vm, arg_electrum_seed);
- m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet);
+ m_restore_deterministic_wallet = command_line::get_arg(vm, arg_restore_deterministic_wallet) || command_line::get_arg(vm, arg_restore_from_seed);
m_restore_multisig_wallet = command_line::get_arg(vm, arg_restore_multisig_wallet);
m_non_deterministic = command_line::get_arg(vm, arg_non_deterministic);
m_allow_mismatched_daemon_version = command_line::get_arg(vm, arg_allow_mismatched_daemon_version);
@@ -4266,7 +4553,6 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_do_not_relay = command_line::get_arg(vm, arg_do_not_relay);
m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead);
m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names);
- m_long_payment_id_support = command_line::get_arg(vm, arg_long_payment_id_support);
m_restoring = !m_generate_from_view_key.empty() ||
!m_generate_from_spend_key.empty() ||
!m_generate_from_keys.empty() ||
@@ -4461,7 +4747,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
}
success_msg_writer() << "**********************************************************************";
- return std::move(password);
+ return password;
}
//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
@@ -4510,7 +4796,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
}
- return std::move(password);
+ return password;
}
//----------------------------------------------------------------------------------------------------
@@ -4553,7 +4839,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
return {};
}
- return std::move(password);
+ return password;
}
//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
@@ -4608,7 +4894,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
return {};
}
- return std::move(password);
+ return password;
}
//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
@@ -4711,7 +4997,7 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p
tr("Use the \"help\" command to see the list of available commands.\n") <<
tr("Use \"help <command>\" to see a command's documentation.\n") <<
"**********************************************************************";
- return std::move(password);
+ return password;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::close_wallet()
@@ -4719,6 +5005,7 @@ bool simple_wallet::close_wallet()
if (m_idle_run.load(std::memory_order_relaxed))
{
m_idle_run.store(false, std::memory_order_relaxed);
+ m_suspend_rpc_payment_mining.store(true, std::memory_order_relaxed);
m_wallet->stop();
{
boost::unique_lock<boost::mutex> lock(m_idle_mutex);
@@ -4890,7 +5177,7 @@ void simple_wallet::check_background_mining(const epee::wipeable_string &passwor
if (setup == tools::wallet2::BackgroundMiningMaybe)
{
message_writer() << tr("The daemon is not set up to background mine.");
- message_writer() << tr("With background mining enabled, the daemon will mine when idle and not on batttery.");
+ message_writer() << tr("With background mining enabled, the daemon will mine when idle and not on battery.");
message_writer() << tr("Enabling this supports the network you are using, and makes you eligible for receiving new monero");
std::string accepted = input_line(tr("Do you want to do it now? (Y/Yes/N/No): "));
if (std::cin.eof() || !command_line::is_yes(accepted)) {
@@ -4986,6 +5273,40 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::check_daemon_rpc_prices(const std::string &daemon_url, uint32_t &actual_cph, uint32_t &claimed_cph)
+{
+ try
+ {
+ auto i = m_claimed_cph.find(daemon_url);
+ if (i == m_claimed_cph.end())
+ return false;
+
+ claimed_cph = m_claimed_cph[daemon_url];
+ bool payment_required;
+ uint64_t credits, diff, credits_per_hash_found, height, seed_height;
+ uint32_t cookie;
+ cryptonote::blobdata hashing_blob;
+ crypto::hash seed_hash, next_seed_hash;
+ if (m_wallet->get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required)
+ {
+ actual_cph = RPC_CREDITS_PER_HASH_SCALE * (credits_per_hash_found / (float)diff);
+ return true;
+ }
+ else
+ {
+ fail_msg_writer() << tr("Error checking daemon RPC access prices");
+ }
+ }
+ catch (const std::exception &e)
+ {
+ // can't check
+ fail_msg_writer() << tr("Error checking daemon RPC access prices: ") << e.what();
+ return false;
+ }
+ // no record found for this daemon
+ return false;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::set_daemon(const std::vector<std::string>& args)
{
std::string daemon_url;
@@ -5009,7 +5330,7 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
// If no port has been provided, use the default from config
if (!match[3].length())
{
- int daemon_port = get_config(m_wallet->nettype()).RPC_DEFAULT_PORT;
+ uint16_t daemon_port = get_config(m_wallet->nettype()).RPC_DEFAULT_PORT;
daemon_url = match[1] + match[2] + std::string(":") + std::to_string(daemon_port);
} else {
daemon_url = args[0];
@@ -5042,7 +5363,28 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
}
catch (const std::exception &e) { }
}
+
+ if (!try_connect_to_daemon())
+ {
+ fail_msg_writer() << tr("Failed to connect to daemon");
+ return true;
+ }
+
success_msg_writer() << boost::format("Daemon set to %s, %s") % daemon_url % (m_wallet->is_trusted_daemon() ? tr("trusted") : tr("untrusted"));
+
+ // check whether the daemon's prices match the claim, and disconnect if not, to disincentivize daemons lying
+ uint32_t actual_cph, claimed_cph;
+ if (check_daemon_rpc_prices(daemon_url, actual_cph, claimed_cph))
+ {
+ if (actual_cph < claimed_cph)
+ {
+ fail_msg_writer() << tr("Daemon RPC credits/hash is less than was claimed. Either this daemon is cheating, or it changed its setup recently.");
+ fail_msg_writer() << tr("Claimed: ") << claimed_cph / (float)RPC_CREDITS_PER_HASH_SCALE;
+ fail_msg_writer() << tr("Actual: ") << actual_cph / (float)RPC_CREDITS_PER_HASH_SCALE;
+ }
+ }
+
+ m_daemon_rpc_payment_message_displayed = false;
} else {
fail_msg_writer() << tr("This does not seem to be a valid daemon URL.");
}
@@ -5097,12 +5439,19 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid,
if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce))
{
crypto::hash payment_id = crypto::null_hash;
- if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
+ crypto::hash8 payment_id8 = crypto::null_hash8;
+ if (get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
+ {
+ if (payment_id8 != crypto::null_hash8)
+ message_writer() <<
+ tr("NOTE: this transaction uses an encrypted payment ID: consider using subaddresses instead");
+ }
+ else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
message_writer(console_color_red, false) <<
- (m_long_payment_id_support ? tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead.") : tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete. Support will be withdrawn in the future. Use subaddresses instead."));
+ tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete and ignored. Use subaddresses instead.");
}
}
- if (unlock_time)
+ if (unlock_time && !cryptonote::is_coinbase(tx))
message_writer() << tr("NOTE: This transaction is locked, see details with: show_transfer ") + epee::string_tools::pod_to_hex(txid);
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
@@ -5274,6 +5623,11 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
{
ss << tr("no connection to daemon. Please make sure daemon is running.");
}
+ catch (const tools::error::payment_required&)
+ {
+ ss << tr("payment required.");
+ m_need_payment = true;
+ }
catch (const tools::error::wallet_rpc_error& e)
{
LOG_ERROR("RPC error: " << e.to_string());
@@ -5305,6 +5659,9 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
fail_msg_writer() << tr("refresh failed: ") << ss.str() << ". " << tr("Blocks received: ") << fetched_blocks;
}
+ // prevent it from triggering the idle screen due to waiting for a foreground refresh
+ m_last_activity_time = time(NULL);
+
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -5597,6 +5954,11 @@ bool simple_wallet::rescan_spent(const std::vector<std::string> &args)
{
fail_msg_writer() << tr("no connection to daemon. Please make sure daemon is running.");
}
+ catch (const tools::error::payment_required&)
+ {
+ fail_msg_writer() << tr("payment required.");
+ m_need_payment = true;
+ }
catch (const tools::error::is_key_image_spent_error&)
{
fail_msg_writer() << tr("failed to get spent status");
@@ -5700,6 +6062,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
req.outputs[j].index = absolute_offsets[j];
}
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
+ req.client = cryptonote::make_rpc_payment_signature(m_wallet->get_rpc_client_secret_key());
bool r = m_wallet->invoke_http_bin("/get_outs.bin", req, res);
err = interpret_rpc_response(r, res.status);
if (!err.empty())
@@ -5874,14 +6237,11 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
priority = m_wallet->adjust_priority(priority);
- size_t fake_outs_count = 0;
+ size_t fake_outs_count = DEFAULT_MIX;
if(local_args.size() > 0) {
size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
{
- fake_outs_count = m_wallet->default_mixin();
- if (fake_outs_count == 0)
- fake_outs_count = DEFAULT_MIX;
}
else if (ring_size == 0)
{
@@ -5923,13 +6283,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (tools::wallet2::parse_long_payment_id(payment_id_str, payment_id))
{
LONG_PAYMENT_ID_SUPPORT_CHECK();
-
- std::string extra_nonce;
- set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
- r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
- local_args.pop_back();
- payment_id_seen = true;
- message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead");
}
if(!r)
{
@@ -6038,8 +6391,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
else if (tools::wallet2::parse_payment_id(payment_id_uri, payment_id))
{
LONG_PAYMENT_ID_SUPPORT_CHECK();
- set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
- message_writer() << tr("Warning: Unencrypted payment IDs will harm your privacy: ask the recipient to use subaddresses instead");
}
else
{
@@ -6058,20 +6409,6 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
dsts.push_back(de);
}
- // prompt is there is no payment id and confirmation is required
- if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && dsts.size() > num_subaddresses)
- {
- std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
- if (std::cin.eof())
- return false;
- if (!command_line::is_yes(accepted))
- {
- fail_msg_writer() << tr("transaction cancelled.");
-
- return false;
- }
- }
-
SCOPED_WALLET_UNLOCK_ON_BAD_PASSWORD(return false;);
try
@@ -6495,14 +6832,11 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
priority = m_wallet->adjust_priority(priority);
- size_t fake_outs_count = 0;
+ size_t fake_outs_count = DEFAULT_MIX;
if(local_args.size() > 0) {
size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
{
- fake_outs_count = m_wallet->default_mixin();
- if (fake_outs_count == 0)
- fake_outs_count = DEFAULT_MIX;
}
else if (ring_size == 0)
{
@@ -6592,11 +6926,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
if(r)
{
LONG_PAYMENT_ID_SUPPORT_CHECK();
-
- std::string extra_nonce;
- set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
- r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
- payment_id_seen = true;
}
if(!r && local_args.size() == 3)
@@ -6636,20 +6965,6 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
payment_id_seen = true;
}
- // prompt is there is no payment id and confirmation is required
- if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
- {
- std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
- if (std::cin.eof())
- return true;
- if (!command_line::is_yes(accepted))
- {
- fail_msg_writer() << tr("transaction cancelled.");
-
- return true;
- }
- }
-
SCOPED_WALLET_UNLOCK();
try
@@ -6794,14 +7109,11 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
priority = m_wallet->adjust_priority(priority);
- size_t fake_outs_count = 0;
+ size_t fake_outs_count = DEFAULT_MIX;
if(local_args.size() > 0) {
size_t ring_size;
if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0]))
{
- fake_outs_count = m_wallet->default_mixin();
- if (fake_outs_count == 0)
- fake_outs_count = DEFAULT_MIX;
}
else if (ring_size == 0)
{
@@ -6855,7 +7167,6 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
if (tools::wallet2::parse_long_payment_id(local_args.back(), payment_id))
{
LONG_PAYMENT_ID_SUPPORT_CHECK();
- set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id);
}
else
{
@@ -6911,22 +7222,6 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
payment_id_seen = true;
}
- // prompt if there is no payment id and confirmation is required
- if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress)
- {
- std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay?"), true);
- if (std::cin.eof())
- return true;
- if (!command_line::is_yes(accepted))
- {
- 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;
- }
- }
-
SCOPED_WALLET_UNLOCK();
try
@@ -8036,7 +8331,11 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
m_in_manual_refresh.store(true, std::memory_order_relaxed);
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){m_in_manual_refresh.store(false, std::memory_order_relaxed);});
- m_wallet->update_pool_state();
+ std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ m_wallet->update_pool_state(process_txs);
+ if (!process_txs.empty())
+ m_wallet->process_pool_state(process_txs);
+
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments, m_current_subaddress_account, subaddr_indices);
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
@@ -8483,21 +8782,41 @@ void simple_wallet::check_for_messages()
//----------------------------------------------------------------------------------------------------
void simple_wallet::wallet_idle_thread()
{
+ const boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::universal_time();
while (true)
{
boost::unique_lock<boost::mutex> lock(m_idle_mutex);
if (!m_idle_run.load(std::memory_order_relaxed))
break;
+ // if another thread was busy (ie, a foreground refresh thread), we'll end up here at
+ // some random time that's not what we slept for, so we should not call refresh now
+ // or we'll be leaking that fact through timing
+ const boost::posix_time::ptime now0 = boost::posix_time::microsec_clock::universal_time();
+ const uint64_t dt_actual = (now0 - start_time).total_microseconds() % 1000000;
+#ifdef _WIN32
+ static const uint64_t threshold = 10000;
+#else
+ static const uint64_t threshold = 2000;
+#endif
+ if (dt_actual < threshold) // if less than a threshold... would a very slow machine always miss it ?
+ {
#ifndef _WIN32
- m_inactivity_checker.do_call(boost::bind(&simple_wallet::check_inactivity, this));
+ m_inactivity_checker.do_call(boost::bind(&simple_wallet::check_inactivity, this));
#endif
- m_refresh_checker.do_call(boost::bind(&simple_wallet::check_refresh, this));
- m_mms_checker.do_call(boost::bind(&simple_wallet::check_mms, this));
+ m_refresh_checker.do_call(boost::bind(&simple_wallet::check_refresh, this));
+ m_mms_checker.do_call(boost::bind(&simple_wallet::check_mms, this));
+ m_rpc_payment_checker.do_call(boost::bind(&simple_wallet::check_rpc_payment, this));
- if (!m_idle_run.load(std::memory_order_relaxed))
- break;
- m_idle_cond.wait_for(lock, boost::chrono::seconds(1));
+ if (!m_idle_run.load(std::memory_order_relaxed))
+ break;
+ }
+
+ // aim for the next multiple of 1 second
+ const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
+ const auto dt = (now - start_time).total_microseconds();
+ const auto wait = 1000000 - dt % 1000000;
+ m_idle_cond.wait_for(lock, boost::chrono::microseconds(wait));
}
}
//----------------------------------------------------------------------------------------------------
@@ -8547,6 +8866,78 @@ bool simple_wallet::check_mms()
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::check_rpc_payment()
+{
+ if (!m_rpc_payment_mining_requested && m_wallet->auto_mine_for_rpc_payment_threshold() == 0.0f)
+ return true;
+
+ uint64_t target = m_wallet->credits_target();
+ if (target == 0)
+ target = CREDITS_TARGET;
+ if (m_rpc_payment_mining_requested)
+ target = std::numeric_limits<uint64_t>::max();
+ bool need_payment = m_need_payment || m_rpc_payment_mining_requested || (m_wallet->credits() < target && m_wallet->daemon_requires_payment());
+ if (need_payment)
+ {
+ const boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::universal_time();
+ auto startfunc = [this](uint64_t diff, uint64_t credits_per_hash_found)
+ {
+ const float cph = credits_per_hash_found / (float)diff;
+ bool low = (diff > MAX_PAYMENT_DIFF || cph < MIN_PAYMENT_RATE);
+ if (credits_per_hash_found > 0 && cph >= m_wallet->auto_mine_for_rpc_payment_threshold())
+ {
+ MINFO(std::to_string(cph) << " credits per hash is >= our threshold (" << m_wallet->auto_mine_for_rpc_payment_threshold() << "), starting mining");
+ return true;
+ }
+ else if (m_rpc_payment_mining_requested)
+ {
+ MINFO("Mining for RPC payment was requested, starting mining");
+ return true;
+ }
+ else
+ {
+ if (!m_daemon_rpc_payment_message_displayed)
+ {
+ success_msg_writer() << boost::format(tr("Daemon requests payment at diff %llu, with %f credits/hash%s. Run start_mining_for_rpc to start mining to pay for RPC access, or use another daemon")) %
+ diff % cph % (low ? " - this is low" : "");
+ m_cmd_binder.print_prompt();
+ m_daemon_rpc_payment_message_displayed = true;
+ }
+ return false;
+ }
+ };
+ auto contfunc = [&,this](unsigned n_hashes)
+ {
+ if (m_suspend_rpc_payment_mining.load(std::memory_order_relaxed))
+ return false;
+ const boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time();
+ m_last_rpc_payment_mining_time = now;
+ if ((now - start_time).total_microseconds() >= 2 * 1000000)
+ m_rpc_payment_hash_rate = n_hashes / (float)((now - start_time).total_seconds());
+ if ((now - start_time).total_microseconds() >= REFRESH_PERIOD * 1000000)
+ return false;
+ return true;
+ };
+ auto foundfunc = [this, target](uint64_t credits)
+ {
+ m_need_payment = false;
+ return credits < target;
+ };
+ auto errorfunc = [this](const std::string &error)
+ {
+ fail_msg_writer() << tr("Error mining to daemon: ") << error;
+ m_cmd_binder.print_prompt();
+ };
+ bool ret = m_wallet->search_for_rpc_payment(target, startfunc, contfunc, foundfunc, errorfunc);
+ if (!ret)
+ {
+ fail_msg_writer() << tr("Failed to start mining for RPC payment");
+ m_cmd_binder.print_prompt();
+ }
+ }
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
std::string simple_wallet::get_prompt() const
{
if (m_locked)
@@ -9005,7 +9396,6 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
if (tools::wallet2::parse_long_payment_id(args[3], payment_id))
{
LONG_PAYMENT_ID_SUPPORT_CHECK();
- description_start += 2;
}
else if (tools::wallet2::parse_short_payment_id(args[3], info.payment_id))
{
@@ -9609,7 +9999,11 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
try
{
- m_wallet->update_pool_state();
+ std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ m_wallet->update_pool_state(process_txs);
+ if (!process_txs.empty())
+ m_wallet->process_pool_state(process_txs);
+
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
m_wallet->get_unconfirmed_payments(pool_payments, m_current_subaddress_account);
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
@@ -9737,6 +10131,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_command);
command_line::add_arg(desc_params, arg_restore_deterministic_wallet );
+ command_line::add_arg(desc_params, arg_restore_from_seed );
command_line::add_arg(desc_params, arg_restore_multisig_wallet );
command_line::add_arg(desc_params, arg_non_deterministic );
command_line::add_arg(desc_params, arg_electrum_seed );
@@ -9747,7 +10142,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_create_address_file);
command_line::add_arg(desc_params, arg_subaddress_lookahead);
command_line::add_arg(desc_params, arg_use_english_language_names);
- command_line::add_arg(desc_params, arg_long_payment_id_support);
+ command_line::add_arg(desc_params, arg_rpc_client_secret_key);
po::positional_options_description positional_options;
positional_options.add(arg_command.name, -1);
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 22659e99e..75bd893d5 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -144,11 +144,16 @@ namespace cryptonote
bool set_subaddress_lookahead(const std::vector<std::string> &args = std::vector<std::string>());
bool set_segregation_height(const std::vector<std::string> &args = std::vector<std::string>());
bool set_ignore_fractional_outputs(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_ignore_outputs_above(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_ignore_outputs_below(const std::vector<std::string> &args = std::vector<std::string>());
bool set_track_uses(const std::vector<std::string> &args = std::vector<std::string>());
bool set_inactivity_lock_timeout(const std::vector<std::string> &args = std::vector<std::string>());
bool set_setup_background_mining(const std::vector<std::string> &args = std::vector<std::string>());
bool set_device_name(const std::vector<std::string> &args = std::vector<std::string>());
bool set_export_format(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_persistent_rpc_client_id(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_auto_mine_for_rpc_payment_threshold(const std::vector<std::string> &args = std::vector<std::string>());
+ bool set_credits_target(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);
@@ -248,7 +253,11 @@ namespace cryptonote
bool thaw(const std::vector<std::string>& args);
bool frozen(const std::vector<std::string>& args);
bool lock(const std::vector<std::string>& args);
+ bool rpc_payment_info(const std::vector<std::string> &args);
+ bool start_mining_for_rpc(const std::vector<std::string> &args);
+ bool stop_mining_for_rpc(const std::vector<std::string> &args);
bool net_stats(const std::vector<std::string>& args);
+ bool public_nodes(const std::vector<std::string>& args);
bool welcome(const std::vector<std::string>& args);
bool version(const std::vector<std::string>& args);
bool on_unknown_command(const std::vector<std::string>& args);
@@ -323,6 +332,11 @@ namespace cryptonote
bool check_inactivity();
bool check_refresh();
bool check_mms();
+ bool check_rpc_payment();
+
+ void handle_transfer_exception(const std::exception_ptr &e, bool trusted_daemon);
+
+ bool check_daemon_rpc_prices(const std::string &daemon_url, uint32_t &actual_cph, uint32_t &claimed_cph);
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
@@ -430,16 +444,26 @@ namespace cryptonote
std::atomic<bool> m_in_manual_refresh;
uint32_t m_current_subaddress_account;
- bool m_long_payment_id_support;
-
std::atomic<time_t> m_last_activity_time;
std::atomic<bool> m_locked;
std::atomic<bool> m_in_command;
+ template<uint64_t mini, uint64_t maxi> struct get_random_interval { public: uint64_t operator()() const { return crypto::rand_range(mini, maxi); } };
+
epee::math_helper::once_a_time_seconds<1> m_inactivity_checker;
- epee::math_helper::once_a_time_seconds<90> m_refresh_checker;
- epee::math_helper::once_a_time_seconds<90> m_mms_checker;
+ epee::math_helper::once_a_time_seconds_range<get_random_interval<80 * 1000000, 100 * 1000000>> m_refresh_checker;
+ epee::math_helper::once_a_time_seconds_range<get_random_interval<90 * 1000000, 110 * 1000000>> m_mms_checker;
+ epee::math_helper::once_a_time_seconds_range<get_random_interval<90 * 1000000, 115 * 1000000>> m_rpc_payment_checker;
+ std::atomic<bool> m_need_payment;
+ boost::posix_time::ptime m_last_rpc_payment_mining_time;
+ bool m_rpc_payment_mining_requested;
+ bool m_daemon_rpc_payment_message_displayed;
+ float m_rpc_payment_hash_rate;
+ std::atomic<bool> m_suspend_rpc_payment_mining;
+
+ std::unordered_map<std::string, uint32_t> m_claimed_cph;
+
// MMS
mms::message_store& get_message_store() const { return m_wallet->get_message_store(); };
mms::multisig_wallet_state get_multisig_wallet_state() const { return m_wallet->get_multisig_wallet_state(); };
diff --git a/src/version.cpp.in b/src/version.cpp.in
index 9121cdf85..ccb88f1fe 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,6 +1,6 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.14.1.2"
-#define DEF_MONERO_RELEASE_NAME "Boron Butterfly"
+#define DEF_MONERO_VERSION "0.15.0.0"
+#define DEF_MONERO_RELEASE_NAME "Carbon Chamaeleon"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index d0fc21f51..a0a166a93 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -37,6 +37,7 @@ set(wallet_sources
node_rpc_proxy.cpp
message_store.cpp
message_transporter.cpp
+ wallet_rpc_payments.cpp
)
set(wallet_private_headers
@@ -49,7 +50,8 @@ set(wallet_private_headers
ringdb.h
node_rpc_proxy.h
message_store.h
- message_transporter.h)
+ message_transporter.h
+ wallet_rpc_helpers.h)
monero_private_headers(wallet
${wallet_private_headers})
@@ -58,6 +60,7 @@ monero_add_library(wallet
${wallet_private_headers})
target_link_libraries(wallet
PUBLIC
+ rpc_base
multisig
common
cryptonote_core
@@ -74,55 +77,59 @@ target_link_libraries(wallet
PRIVATE
${EXTRA_LIBRARIES})
-set(wallet_rpc_sources
- wallet_rpc_server.cpp)
+if(NOT IOS)
+ set(wallet_rpc_sources
+ wallet_rpc_server.cpp)
-set(wallet_rpc_headers)
+ set(wallet_rpc_headers)
-set(wallet_rpc_private_headers
- wallet_rpc_server.h)
+ set(wallet_rpc_private_headers
+ wallet_rpc_server.h)
-monero_private_headers(wallet_rpc_server
- ${wallet_rpc_private_headers})
-monero_add_executable(wallet_rpc_server
- ${wallet_rpc_sources}
- ${wallet_rpc_headers}
- ${wallet_rpc_private_headers})
-
-target_link_libraries(wallet_rpc_server
- PRIVATE
- wallet
- rpc_base
- cryptonote_core
- cncrypto
- common
- version
- daemonizer
- ${EPEE_READLINE}
- ${Boost_CHRONO_LIBRARY}
- ${Boost_PROGRAM_OPTIONS_LIBRARY}
- ${Boost_FILESYSTEM_LIBRARY}
- ${Boost_THREAD_LIBRARY}
- ${CMAKE_THREAD_LIBS_INIT}
- ${EXTRA_LIBRARIES})
-set_property(TARGET wallet_rpc_server
- PROPERTY
- OUTPUT_NAME "monero-wallet-rpc")
-install(TARGETS wallet_rpc_server DESTINATION bin)
+ monero_private_headers(wallet_rpc_server
+ ${wallet_rpc_private_headers})
+ monero_add_executable(wallet_rpc_server
+ ${wallet_rpc_sources}
+ ${wallet_rpc_headers}
+ ${wallet_rpc_private_headers})
+ target_link_libraries(wallet_rpc_server
+ PRIVATE
+ wallet
+ rpc_base
+ cryptonote_core
+ cncrypto
+ common
+ version
+ daemonizer
+ ${EPEE_READLINE}
+ ${Boost_CHRONO_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_THREAD_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${EXTRA_LIBRARIES})
+ set_property(TARGET wallet_rpc_server
+ PROPERTY
+ OUTPUT_NAME "monero-wallet-rpc")
+ install(TARGETS wallet_rpc_server DESTINATION bin)
+endif()
# build and install libwallet_merged only if we building for GUI
if (BUILD_GUI_DEPS)
set(libs_to_merge
wallet_api
wallet
+ rpc_base
multisig
+ blockchain_db
cryptonote_core
cryptonote_basic
mnemonics
common
cncrypto
device
+ hardforks
ringct
ringct_basic
checkpoints
diff --git a/src/wallet/api/subaddress_account.cpp b/src/wallet/api/subaddress_account.cpp
index 9bc9d1d91..eaaddc11f 100644
--- a/src/wallet/api/subaddress_account.cpp
+++ b/src/wallet/api/subaddress_account.cpp
@@ -64,8 +64,8 @@ void SubaddressAccountImpl::refresh()
i,
m_wallet->m_wallet->get_subaddress_as_str({i,0}),
m_wallet->m_wallet->get_subaddress_label({i,0}),
- cryptonote::print_money(m_wallet->m_wallet->balance(i)),
- cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i))
+ cryptonote::print_money(m_wallet->m_wallet->balance(i, false)),
+ cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i, false))
));
}
}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index e632b8d23..6200c7a1f 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -993,12 +993,12 @@ void WalletImpl::setSubaddressLookahead(uint32_t major, uint32_t minor)
uint64_t WalletImpl::balance(uint32_t accountIndex) const
{
- return m_wallet->balance(accountIndex);
+ return m_wallet->balance(accountIndex, false);
}
uint64_t WalletImpl::unlockedBalance(uint32_t accountIndex) const
{
- return m_wallet->unlocked_balance(accountIndex);
+ return m_wallet->unlocked_balance(accountIndex, false);
}
uint64_t WalletImpl::blockChainHeight() const
@@ -1123,6 +1123,10 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file
UnsignedTransactionImpl * transaction = new UnsignedTransactionImpl(*this);
if (!m_wallet->load_unsigned_tx(unsigned_filename, transaction->m_unsigned_tx_set)){
setStatusError(tr("Failed to load unsigned transactions"));
+ transaction->m_status = UnsignedTransaction::Status::Status_Error;
+ transaction->m_errorString = errorString();
+
+ return transaction;
}
// Check tx data and construct confirmation message
diff --git a/src/wallet/message_store.cpp b/src/wallet/message_store.cpp
index 96d4ef3ce..6e2cb933f 100644
--- a/src/wallet/message_store.cpp
+++ b/src/wallet/message_store.cpp
@@ -699,7 +699,7 @@ void message_store::write_to_file(const multisig_wallet_state &state, const std:
crypto::chacha_key key;
crypto::generate_chacha_key(&state.view_secret_key, sizeof(crypto::secret_key), key, 1);
- file_data write_file_data = boost::value_initialized<file_data>();
+ file_data write_file_data = {};
write_file_data.magic_string = "MMS";
write_file_data.file_version = 0;
write_file_data.iv = crypto::rand<crypto::chacha_iv>();
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index 1d5078a11..005b0bafa 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -28,8 +28,22 @@
#include "node_rpc_proxy.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "rpc/rpc_payment_signature.h"
+#include "rpc/rpc_payment_costs.h"
#include "storages/http_abstract_invoke.h"
+#define RETURN_ON_RPC_RESPONSE_ERROR(r, error, res, method) \
+ do { \
+ CHECK_AND_ASSERT_MES(error.code == 0, error.message, error.message); \
+ handle_payment_changes(res, std::integral_constant<bool, HasCredits<decltype(res)>::Has>()); \
+ CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon"); \
+ /* empty string -> not connection */ \
+ CHECK_AND_ASSERT_MES(!res.status.empty(), res.status, "No connection to daemon"); \
+ CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, res.status, "Daemon busy"); \
+ CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_PAYMENT_REQUIRED, res.status, "Payment required"); \
+ CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, res.status, "Error calling " + std::string(method) + " daemon RPC"); \
+ } while(0)
+
using namespace epee;
namespace tools
@@ -37,8 +51,9 @@ 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::recursive_mutex &mutex)
+NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex)
: m_http_client(http_client)
+ , m_rpc_payment_state(rpc_payment_state)
, m_daemon_rpc_mutex(mutex)
, m_offline(false)
{
@@ -58,9 +73,14 @@ void NodeRPCProxy::invalidate()
m_target_height = 0;
m_block_weight_limit = 0;
m_get_info_time = 0;
+ m_rpc_payment_info_time = 0;
+ m_rpc_payment_seed_height = 0;
+ m_rpc_payment_seed_hash = crypto::null_hash;
+ m_rpc_payment_next_seed_hash = crypto::null_hash;
+ m_height_time = 0;
}
-boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const
+boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version)
{
if (m_offline)
return boost::optional<std::string>("offline");
@@ -68,12 +88,11 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version
{
cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_version", 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"), "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get daemon RPC version");
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t, m_http_client, rpc_timeout);
+ RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_version");
+ }
m_rpc_version = resp_t.version;
}
rpc_version = m_rpc_version;
@@ -83,9 +102,10 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version
void NodeRPCProxy::set_height(uint64_t h)
{
m_height = h;
+ m_height_time = time(NULL);
}
-boost::optional<std::string> NodeRPCProxy::get_info() const
+boost::optional<std::string> NodeRPCProxy::get_info()
{
if (m_offline)
return boost::optional<std::string>("offline");
@@ -95,23 +115,33 @@ boost::optional<std::string> NodeRPCProxy::get_info() const
cryptonote::COMMAND_RPC_GET_INFO::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_INFO::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_info", req_t, resp_t, m_http_client, rpc_timeout);
+ RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_info");
+ check_rpc_cost(m_rpc_payment_state, "get_info", resp_t.credits, pre_call_credits, COST_PER_GET_INFO);
+ }
- CHECK_AND_ASSERT_MES(r, std::string("Failed to connect to daemon"), "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height");
m_height = resp_t.height;
m_target_height = resp_t.target_height;
m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit;
m_get_info_time = now;
+ m_height_time = now;
}
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) const
+boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height)
{
+ const time_t now = time(NULL);
+ if (now < m_height_time + 30) // re-cache every 30 seconds
+ {
+ height = m_height;
+ return boost::optional<std::string>();
+ }
+
auto res = get_info();
if (res)
return res;
@@ -119,7 +149,7 @@ boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) const
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) const
+boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height)
{
auto res = get_info();
if (res)
@@ -128,7 +158,7 @@ boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) c
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &block_weight_limit) const
+boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &block_weight_limit)
{
auto res = get_info();
if (res)
@@ -137,7 +167,7 @@ boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &bloc
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) const
+boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height)
{
if (m_offline)
return boost::optional<std::string>("offline");
@@ -145,14 +175,17 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
{
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_HARD_FORK_INFO::response resp_t = AUTO_VAL_INIT(resp_t);
-
- m_daemon_rpc_mutex.lock();
req_t.version = version;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", 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"), "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status");
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout);
+ RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "hard_fork_info");
+ check_rpc_cost(m_rpc_payment_state, "hard_fork_info", resp_t.credits, pre_call_credits, COST_PER_HARD_FORK_INFO);
+ }
+
m_earliest_height[version] = resp_t.earliest_height;
}
@@ -160,7 +193,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const
+boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee)
{
uint64_t height;
@@ -174,14 +207,17 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_
{
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
-
- m_daemon_rpc_mutex.lock();
req_t.grace_blocks = grace_blocks;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", 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"), "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
+ RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_fee_estimate");
+ check_rpc_cost(m_rpc_payment_state, "get_fee_estimate", resp_t.credits, pre_call_credits, COST_PER_FEE_ESTIMATE);
+ }
+
m_dynamic_base_fee_estimate = resp_t.fee;
m_dynamic_base_fee_estimate_cached_height = height;
m_dynamic_base_fee_estimate_grace_blocks = grace_blocks;
@@ -192,7 +228,7 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_
return boost::optional<std::string>();
}
-boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask) const
+boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask)
{
uint64_t height;
@@ -206,14 +242,17 @@ boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &f
{
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
-
- m_daemon_rpc_mutex.lock();
req_t.grace_blocks = m_dynamic_base_fee_estimate_grace_blocks;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", 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"), "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
- CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
+ RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "get_fee_estimate");
+ check_rpc_cost(m_rpc_payment_state, "get_fee_estimate", resp_t.credits, pre_call_credits, COST_PER_FEE_ESTIMATE);
+ }
+
m_dynamic_base_fee_estimate = resp_t.fee;
m_dynamic_base_fee_estimate_cached_height = height;
m_fee_quantization_mask = resp_t.quantization_mask;
@@ -228,4 +267,65 @@ boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &f
return boost::optional<std::string>();
}
+boost::optional<std::string> NodeRPCProxy::get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie)
+{
+ const time_t now = time(NULL);
+ if (m_rpc_payment_state.stale || now >= m_rpc_payment_info_time + 5*60 || (mining && now >= m_rpc_payment_info_time + 10)) // re-cache every 10 seconds if mining, 5 minutes otherwise
+ {
+ cryptonote::COMMAND_RPC_ACCESS_INFO::request req_t = AUTO_VAL_INIT(req_t);
+ cryptonote::COMMAND_RPC_ACCESS_INFO::response resp_t = AUTO_VAL_INIT(resp_t);
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ req_t.client = cryptonote::make_rpc_payment_signature(m_client_id_secret_key);
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "rpc_access_info", req_t, resp_t, m_http_client, rpc_timeout);
+ RETURN_ON_RPC_RESPONSE_ERROR(r, epee::json_rpc::error{}, resp_t, "rpc_access_info");
+ m_rpc_payment_state.stale = false;
+ }
+
+ m_rpc_payment_diff = resp_t.diff;
+ m_rpc_payment_credits_per_hash_found = resp_t.credits_per_hash_found;
+ m_rpc_payment_height = resp_t.height;
+ m_rpc_payment_seed_height = resp_t.seed_height;
+ m_rpc_payment_cookie = resp_t.cookie;
+
+ if (!epee::string_tools::parse_hexstr_to_binbuff(resp_t.hashing_blob, m_rpc_payment_blob) || m_rpc_payment_blob.size() < 43)
+ {
+ MERROR("Invalid hashing blob: " << resp_t.hashing_blob);
+ return std::string("Invalid hashing blob");
+ }
+ if (resp_t.seed_hash.empty())
+ {
+ m_rpc_payment_seed_hash = crypto::null_hash;
+ }
+ else if (!epee::string_tools::hex_to_pod(resp_t.seed_hash, m_rpc_payment_seed_hash))
+ {
+ MERROR("Invalid seed_hash: " << resp_t.seed_hash);
+ return std::string("Invalid seed hash");
+ }
+ if (resp_t.next_seed_hash.empty())
+ {
+ m_rpc_payment_next_seed_hash = crypto::null_hash;
+ }
+ else if (!epee::string_tools::hex_to_pod(resp_t.next_seed_hash, m_rpc_payment_next_seed_hash))
+ {
+ MERROR("Invalid next_seed_hash: " << resp_t.next_seed_hash);
+ return std::string("Invalid next seed hash");
+ }
+ m_rpc_payment_info_time = now;
+ }
+
+ payment_required = m_rpc_payment_diff > 0;
+ credits = m_rpc_payment_state.credits;
+ diff = m_rpc_payment_diff;
+ credits_per_hash_found = m_rpc_payment_credits_per_hash_found;
+ blob = m_rpc_payment_blob;
+ height = m_rpc_payment_height;
+ seed_height = m_rpc_payment_seed_height;
+ seed_hash = m_rpc_payment_seed_hash;
+ next_seed_hash = m_rpc_payment_next_seed_hash;
+ cookie = m_rpc_payment_cookie;
+ return boost::none;
+}
+
}
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index 3b75c8b94..65ca40640 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -32,6 +32,8 @@
#include <boost/thread/mutex.hpp>
#include "include_base_utils.h"
#include "net/http_client.h"
+#include "rpc/core_rpc_server_commands_defs.h"
+#include "wallet_rpc_helpers.h"
namespace tools
{
@@ -39,37 +41,63 @@ namespace tools
class NodeRPCProxy
{
public:
- NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::recursive_mutex &mutex);
+ NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, rpc_payment_state_t &rpc_payment_state, boost::recursive_mutex &mutex);
+ void set_client_secret_key(const crypto::secret_key &skey) { m_client_id_secret_key = skey; }
void invalidate();
void set_offline(bool offline) { m_offline = offline; }
- boost::optional<std::string> get_rpc_version(uint32_t &version) const;
- boost::optional<std::string> get_height(uint64_t &height) const;
+ 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_target_height(uint64_t &height) const;
- boost::optional<std::string> get_block_weight_limit(uint64_t &block_weight_limit) const;
- boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const;
- boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const;
- boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask) const;
+ boost::optional<std::string> get_target_height(uint64_t &height);
+ boost::optional<std::string> get_block_weight_limit(uint64_t &block_weight_limit);
+ boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height);
+ boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee);
+ boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask);
+ boost::optional<std::string> get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie);
private:
- boost::optional<std::string> get_info() const;
+ template<typename T> void handle_payment_changes(const T &res, std::true_type) {
+ if (res.status == CORE_RPC_STATUS_OK || res.status == CORE_RPC_STATUS_PAYMENT_REQUIRED)
+ m_rpc_payment_state.credits = res.credits;
+ if (res.top_hash != m_rpc_payment_state.top_hash)
+ {
+ m_rpc_payment_state.top_hash = res.top_hash;
+ m_rpc_payment_state.stale = true;
+ }
+ }
+ template<typename T> void handle_payment_changes(const T &res, std::false_type) {}
+
+private:
+ boost::optional<std::string> get_info();
epee::net_utils::http::http_simple_client &m_http_client;
+ rpc_payment_state_t &m_rpc_payment_state;
boost::recursive_mutex &m_daemon_rpc_mutex;
+ crypto::secret_key m_client_id_secret_key;
bool m_offline;
- mutable uint64_t m_height;
- mutable uint64_t m_earliest_height[256];
- mutable uint64_t m_dynamic_base_fee_estimate;
- mutable uint64_t m_dynamic_base_fee_estimate_cached_height;
- mutable uint64_t m_dynamic_base_fee_estimate_grace_blocks;
- mutable uint64_t m_fee_quantization_mask;
- mutable uint32_t m_rpc_version;
- mutable uint64_t m_target_height;
- mutable uint64_t m_block_weight_limit;
- mutable time_t m_get_info_time;
+ uint64_t m_height;
+ uint64_t m_earliest_height[256];
+ uint64_t m_dynamic_base_fee_estimate;
+ uint64_t m_dynamic_base_fee_estimate_cached_height;
+ uint64_t m_dynamic_base_fee_estimate_grace_blocks;
+ uint64_t m_fee_quantization_mask;
+ uint32_t m_rpc_version;
+ uint64_t m_target_height;
+ uint64_t m_block_weight_limit;
+ time_t m_get_info_time;
+ time_t m_rpc_payment_info_time;
+ uint64_t m_rpc_payment_diff;
+ uint64_t m_rpc_payment_credits_per_hash_found;
+ cryptonote::blobdata m_rpc_payment_blob;
+ uint64_t m_rpc_payment_height;
+ uint64_t m_rpc_payment_seed_height;
+ crypto::hash m_rpc_payment_seed_hash;
+ crypto::hash m_rpc_payment_next_seed_hash;
+ uint32_t m_rpc_payment_cookie;
+ time_t m_height_time;
};
}
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 476c68c74..5e3a8a08f 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -32,7 +32,6 @@
#include <tuple>
#include <boost/format.hpp>
#include <boost/optional/optional.hpp>
-#include <boost/utility/value_init.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/split.hpp>
@@ -45,15 +44,20 @@
using namespace epee;
#include "cryptonote_config.h"
+#include "wallet_rpc_helpers.h"
#include "wallet2.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "rpc/core_rpc_server_error_codes.h"
+#include "rpc/rpc_payment_signature.h"
+#include "rpc/rpc_payment_costs.h"
#include "misc_language.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "multisig/multisig.h"
#include "common/boost_serialization_helper.h"
#include "common/command_line.h"
#include "common/threadpool.h"
+#include "int-util.h"
#include "profile_tools.h"
#include "crypto/crypto.h"
#include "serialization/binary_utils.h"
@@ -138,6 +142,8 @@ using namespace cryptonote;
#define DEFAULT_INACTIVITY_LOCK_TIMEOUT 90 // a minute and a half
+#define IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION 12
+
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
@@ -384,6 +390,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
{
auto parsed = tools::login::parse(
command_line::get_arg(vm, opts.daemon_login), false, [password_prompter](bool verify) {
+ if (!password_prompter)
+ {
+ MERROR("Password needed without prompt function");
+ return boost::optional<tools::password_container>();
+ }
return password_prompter("Daemon client password", verify);
}
);
@@ -1027,10 +1038,15 @@ uint64_t gamma_picker::pick()
return first_rct + crypto::rand_idx(n_rct);
};
+boost::mutex wallet_keys_unlocker::lockers_lock;
+unsigned int wallet_keys_unlocker::lockers = 0;
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
w(w),
locked(password != boost::none)
{
+ boost::lock_guard<boost::mutex> lock(lockers_lock);
+ if (lockers++ > 0)
+ locked = false;
if (!locked || w.is_unattended() || w.ask_password() != tools::wallet2::AskPasswordToDecrypt || w.watch_only())
{
locked = false;
@@ -1045,6 +1061,9 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::
w(w),
locked(locked)
{
+ boost::lock_guard<boost::mutex> lock(lockers_lock);
+ if (lockers++ > 0)
+ locked = false;
if (!locked)
return;
w.generate_chacha_key_from_password(password, key);
@@ -1053,9 +1072,19 @@ wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, bool locked, const epee::
wallet_keys_unlocker::~wallet_keys_unlocker()
{
- if (!locked)
- return;
- try { w.encrypt_keys(key); }
+ try
+ {
+ boost::lock_guard<boost::mutex> lock(lockers_lock);
+ if (lockers == 0)
+ {
+ MERROR("There are no lockers in wallet_keys_unlocker dtor");
+ return;
+ }
+ --lockers;
+ if (!locked)
+ return;
+ w.encrypt_keys(key);
+ }
catch (...)
{
MERROR("Failed to re-encrypt wallet keys");
@@ -1114,7 +1143,6 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_first_refresh_done(false),
m_refresh_from_block_height(0),
m_explicit_refresh_from_block_height(true),
- m_confirm_missing_payment_id(true),
m_confirm_non_default_ring_size(true),
m_ask_password(AskPasswordToDecrypt),
m_min_output_count(0),
@@ -1128,16 +1156,20 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_key_reuse_mitigation2(true),
m_segregation_height(0),
m_ignore_fractional_outputs(true),
+ m_ignore_outputs_above(MONEY_SUPPLY),
+ m_ignore_outputs_below(0),
m_track_uses(false),
m_inactivity_lock_timeout(DEFAULT_INACTIVITY_LOCK_TIMEOUT),
m_setup_background_mining(BackgroundMiningMaybe),
+ m_persistent_rpc_client_id(false),
+ m_auto_mine_for_rpc_payment_threshold(-1.0f),
m_is_initialized(false),
m_kdf_rounds(kdf_rounds),
is_old_file_format(false),
m_watch_only(false),
m_multisig(false),
m_multisig_threshold(0),
- m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex),
+ m_node_rpc_proxy(m_http_client, m_rpc_payment_state, m_daemon_rpc_mutex),
m_account_public_address{crypto::null_pkey, crypto::null_pkey},
m_subaddress_lookahead_major(SUBADDRESS_LOOKAHEAD_MAJOR),
m_subaddress_lookahead_minor(SUBADDRESS_LOOKAHEAD_MINOR),
@@ -1160,8 +1192,10 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_use_dns(true),
m_offline(false),
m_rpc_version(0),
- m_export_format(ExportFormat::Binary)
+ m_export_format(ExportFormat::Binary),
+ m_credits_target(0)
{
+ set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
}
wallet2::~wallet2()
@@ -1267,9 +1301,16 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
if(m_http_client.is_connected())
m_http_client.disconnect();
+ const bool changed = m_daemon_address != daemon_address;
m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login);
m_trusted_daemon = trusted_daemon;
+ if (changed)
+ {
+ m_rpc_payment_state.expected_spent = 0;
+ m_rpc_payment_state.discrepancy = 0;
+ m_node_rpc_proxy.invalidate();
+ }
MINFO("setting daemon to " << get_daemon_address());
return m_http_client.set_server(get_daemon_address(), get_daemon_login(), std::move(ssl_options));
@@ -1794,7 +1835,7 @@ void wallet2::cache_tx_data(const cryptonote::transaction& tx, const crypto::has
}
}
//----------------------------------------------------------------------------------------------------
-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, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
+void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache)
{
PERF_TIMER(process_new_transaction);
// In this function, tx (probably) only contains the base information
@@ -1999,7 +2040,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t amount = tx.vout[o].amount ? tx.vout[o].amount : tx_scan_info[o].amount;
if (!pool)
{
- m_transfers.push_back(boost::value_initialized<transfer_details>());
+ m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back();
td.m_block_height = height;
td.m_internal_output_index = o;
@@ -2286,8 +2327,18 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
- LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
- MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
+ bool ignore = block_version >= IGNORE_LONG_PAYMENT_ID_FROM_BLOCK_VERSION;
+ if (ignore)
+ {
+ LOG_PRINT_L2("Found unencrypted payment ID in tx " << txid << " (ignored)");
+ MWARNING("Found OBSOLETE AND IGNORED unencrypted payment ID: these are bad for privacy, use subaddresses instead");
+ payment_id = crypto::null_hash;
+ }
+ else
+ {
+ LOG_PRINT_L2("Found unencrypted payment ID: " << payment_id);
+ MWARNING("Found unencrypted payment ID: these are bad for privacy, consider using subaddresses instead");
+ }
}
}
@@ -2423,7 +2474,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
{
TIME_MEASURE_START(miner_tx_handle_time);
if (m_refresh_type != RefreshNoCoinbase)
- process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache);
+ process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, parsed_block.o_indices.indices[0].indices, height, b.major_version, b.timestamp, true, false, false, tx_cache_data[tx_cache_data_offset], output_tracker_cache);
++tx_cache_data_offset;
TIME_MEASURE_FINISH(miner_tx_handle_time);
@@ -2432,7 +2483,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
THROW_WALLET_EXCEPTION_IF(bche.txs.size() != parsed_block.txes.size(), error::wallet_internal_error, "Wrong amount of transactions for block");
for (size_t idx = 0; idx < b.tx_hashes.size(); ++idx)
{
- process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache);
+ process_new_transaction(b.tx_hashes[idx], parsed_block.txes[idx], parsed_block.o_indices.indices[idx+1].indices, height, b.major_version, b.timestamp, false, false, false, tx_cache_data[tx_cache_data_offset++], output_tracker_cache);
}
TIME_MEASURE_FINISH(txs_handle_time);
m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
@@ -2486,28 +2537,37 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl
error = !cryptonote::parse_and_validate_block_from_blob(blob, bl, bl_id);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices)
+void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t &current_height)
{
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res);
req.block_ids = short_chain_history;
+ MDEBUG("Pulling blocks: start_height " << start_height);
+
req.prune = true;
req.start_height = start_height;
req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_bin("/getblocks.bin", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(res.status));
- THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
- "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
- boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "getblocks.bin", error::get_blocks_error, get_rpc_status(res.status));
+ THROW_WALLET_EXCEPTION_IF(res.blocks.size() != res.output_indices.size(), error::wallet_internal_error,
+ "mismatched blocks (" + boost::lexical_cast<std::string>(res.blocks.size()) + ") and output_indices (" +
+ boost::lexical_cast<std::string>(res.output_indices.size()) + ") sizes from daemon");
+ check_rpc_cost("/getblocks.bin", res.credits, pre_call_credits, 1 + res.blocks.size() * COST_PER_BLOCK);
+ }
blocks_start_height = res.start_height;
blocks = std::move(res.blocks);
o_indices = std::move(res.output_indices);
+ current_height = res.current_height;
+
+ MDEBUG("Pulled blocks: blocks_start_height " << blocks_start_height << ", count " << blocks.size()
+ << ", height " << blocks_start_height + blocks.size() << ", node height " << res.current_height);
}
//----------------------------------------------------------------------------------------------------
void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes)
@@ -2517,12 +2577,15 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
req.block_ids = short_chain_history;
req.start_height = start_height;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_bin("/gethashes.bin", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_hashes_error, get_rpc_status(res.status));
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ req.client = get_client_signature();
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "gethashes.bin", error::get_hashes_error, get_rpc_status(res.status));
+ check_rpc_cost("/gethashes.bin", res.credits, pre_call_credits, 1 + res.m_block_ids.size() * COST_PER_BLOCK_HASH);
+ }
blocks_start_height = res.start_height;
hashes = std::move(res.m_block_ids);
@@ -2687,9 +2750,11 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
refresh(trusted_daemon, start_height, blocks_fetched, received_money);
}
//----------------------------------------------------------------------------------------------------
-void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error)
+void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception)
{
error = false;
+ last = false;
+ exception = NULL;
try
{
@@ -2706,7 +2771,8 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
// pull the new blocks
std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices;
- pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices);
+ uint64_t current_height;
+ pull_blocks(start_height, blocks_start_height, short_chain_history, blocks, o_indices, current_height);
THROW_WALLET_EXCEPTION_IF(blocks.size() != o_indices.size(), error::wallet_internal_error, "Mismatched sizes of blocks and o_indices");
tools::threadpool& tpool = tools::threadpool::getInstance();
@@ -2735,7 +2801,7 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
for (size_t j = 0; j < blocks[i].txs.size(); ++j)
{
tpool.submit(&waiter, [&, i, j](){
- if (!parse_and_validate_tx_base_from_blob(blocks[i].txs[j], parsed_blocks[i].txes[j]))
+ if (!parse_and_validate_tx_base_from_blob(blocks[i].txs[j].blob, parsed_blocks[i].txes[j]))
{
boost::unique_lock<boost::mutex> lock(error_lock);
error = true;
@@ -2744,10 +2810,12 @@ void wallet2::pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks
}
}
waiter.wait(&tpool);
+ last = !blocks.empty() && cryptonote::get_block_height(parsed_blocks.back().block) + 1 == current_height;
}
catch(...)
{
error = true;
+ exception = std::current_exception();
}
}
@@ -2779,7 +2847,7 @@ void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashe
}
//----------------------------------------------------------------------------------------------------
-void wallet2::update_pool_state(bool refreshed)
+void wallet2::update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed)
{
MTRACE("update_pool_state start");
@@ -2794,12 +2862,15 @@ void wallet2::update_pool_state(bool refreshed)
// get the pool state
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json("/get_transaction_pool_hashes.bin", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- 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);
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_transaction_pool_hashes.bin", error::get_tx_pool_error);
+ check_rpc_cost("/get_transaction_pool_hashes.bin", res.credits, pre_call_credits, 1 + res.tx_hashes.size() * COST_PER_POOL_HASH);
+ }
MTRACE("update_pool_state got pool");
// remove any pending tx that's not in the pool
@@ -2924,11 +2995,6 @@ void wallet2::update_pool_state(bool refreshed)
LOG_PRINT_L1("We sent that one");
}
}
- else
- {
- LOG_PRINT_L1("Already saw that one, it's for us");
- txids.push_back({txid, true});
- }
}
// get those txes
@@ -2941,9 +3007,17 @@ void wallet2::update_pool_state(bool refreshed)
MDEBUG("asking for " << txids.size() << " transactions");
req.decode_as_json = false;
req.prune = true;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
+
+ bool r;
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ if (r && res.status == CORE_RPC_STATUS_OK)
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
+ }
+
MDEBUG("Got " << r << " and " << res.status);
if (r && res.status == CORE_RPC_STATUS_OK)
{
@@ -2963,13 +3037,7 @@ void wallet2::update_pool_state(bool refreshed)
[tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; });
if (i != txids.end())
{
- process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen, {});
- m_scanned_pool_txs[0].insert(tx_hash);
- if (m_scanned_pool_txs[0].size() > 5000)
- {
- std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
- m_scanned_pool_txs[0].clear();
- }
+ process_txs.push_back(std::make_pair(tx, tx_entry.double_spend_seen));
}
else
{
@@ -3000,6 +3068,24 @@ void wallet2::update_pool_state(bool refreshed)
MTRACE("update_pool_state end");
}
//----------------------------------------------------------------------------------------------------
+void wallet2::process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs)
+{
+ const time_t now = time(NULL);
+ for (const auto &e: txs)
+ {
+ const cryptonote::transaction &tx = e.first;
+ const bool double_spend_seen = e.second;
+ const crypto::hash tx_hash = get_transaction_hash(tx);
+ process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, 0, now, false, true, double_spend_seen, {});
+ m_scanned_pool_txs[0].insert(tx_hash);
+ if (m_scanned_pool_txs[0].size() > 5000)
+ {
+ std::swap(m_scanned_pool_txs[0], m_scanned_pool_txs[1]);
+ m_scanned_pool_txs[0].clear();
+ }
+ }
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force)
{
std::vector<crypto::hash> hashes;
@@ -3080,6 +3166,21 @@ bool wallet2::add_address_book_row(const cryptonote::account_public_address &add
return false;
}
+bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
+{
+ wallet2::address_book_row a;
+ a.m_address = address;
+ a.m_payment_id = payment_id;
+ a.m_description = description;
+ a.m_is_subaddress = is_subaddress;
+
+ const auto size = m_address_book.size();
+ if (row_id >= size)
+ return false;
+ m_address_book[row_id] = a;
+ return true;
+}
+
bool wallet2::delete_address_book_row(std::size_t row_id) {
if(m_address_book.size() <= row_id)
return false;
@@ -3188,26 +3289,38 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
});
auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler([&](){hwdev.computing_key_images(false);});
- bool first = true;
+
+ // get updated pool state first, but do not process those txes just yet,
+ // since that might cause a password prompt, which would introduce a data
+ // leak allowing a passive adversary with traffic analysis capability to
+ // infer when we get an incoming output
+ std::vector<std::pair<cryptonote::transaction, bool>> process_pool_txs;
+ update_pool_state(process_pool_txs, true);
+
+ bool first = true, last = false;
while(m_run.load(std::memory_order_relaxed))
{
uint64_t next_blocks_start_height;
std::vector<cryptonote::block_complete_entry> next_blocks;
std::vector<parsed_block> next_parsed_blocks;
bool error;
+ std::exception_ptr exception;
try
{
// pull the next set of blocks while we're processing the current one
error = false;
+ exception = NULL;
next_blocks.clear();
next_parsed_blocks.clear();
added_blocks = 0;
if (!first && blocks.empty())
{
- refreshed = false;
+ m_node_rpc_proxy.set_height(m_blockchain.size());
+ refreshed = true;
break;
}
- tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, error);});
+ if (!last)
+ tpool.submit(&waiter, [&]{pull_and_parse_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, parsed_blocks, next_blocks, next_parsed_blocks, last, error, exception);});
if (!first)
{
@@ -3258,7 +3371,10 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
// handle error from async fetching thread
if (error)
{
- throw std::runtime_error("proxy exception in refresh thread");
+ if (exception)
+ std::rethrow_exception(exception);
+ else
+ throw std::runtime_error("proxy exception in refresh thread");
}
// if we've got at least 10 blocks to refresh, assume we're starting
@@ -3277,6 +3393,12 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
waiter.wait(&tpool);
throw;
}
+ catch (const error::payment_required&)
+ {
+ // no point in trying again, it'd just eat up credits
+ waiter.wait(&tpool);
+ throw;
+ }
catch (const std::exception&)
{
blocks_fetched += added_blocks;
@@ -3305,8 +3427,8 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
try
{
// If stop() is called we don't need to check pending transactions
- if (check_pool && m_run.load(std::memory_order_relaxed))
- update_pool_state(refreshed);
+ if (check_pool && m_run.load(std::memory_order_relaxed) && !process_pool_txs.empty())
+ process_pool_state(process_pool_txs);
}
catch (...)
{
@@ -3368,22 +3490,19 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
req.cumulative = false;
req.binary = true;
req.compress = true;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_bin("/get_output_distribution.bin", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- if (!r)
- {
- MWARNING("Failed to request output distribution: no connection to daemon");
- return false;
- }
- if (res.status == CORE_RPC_STATUS_BUSY)
+
+ bool r;
+ try
{
- MWARNING("Failed to request output distribution: daemon is busy");
- return false;
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_output_distribution.bin");
+ check_rpc_cost("/get_output_distribution.bin", res.credits, pre_call_credits, COST_PER_OUTPUT_DISTRIBUTION_0);
}
- if (res.status != CORE_RPC_STATUS_OK)
+ catch(...)
{
- MWARNING("Failed to request output distribution: " << res.status);
return false;
}
if (res.distributions.size() != 1)
@@ -3453,6 +3572,7 @@ void wallet2::detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, ui
THROW_WALLET_EXCEPTION_IF(it_pk == m_pub_keys.end(), error::wallet_internal_error, "public key not found");
m_pub_keys.erase(it_pk);
}
+ transfers_detached = std::distance(it, m_transfers.end());
m_transfers.erase(it, m_transfers.end());
size_t blocks_detached = m_blockchain.size() - height;
@@ -3557,7 +3677,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
bool r = epee::serialization::store_t_to_binary(account, account_data);
CHECK_AND_ASSERT_MES(r, false, "failed to serialize wallet keys");
- wallet2::keys_file_data keys_file_data = boost::value_initialized<wallet2::keys_file_data>();
+ wallet2::keys_file_data keys_file_data = {};
// Create a JSON object with "key_data" and "seed_language" as keys.
rapidjson::Document json;
@@ -3625,9 +3745,6 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint64(m_refresh_from_block_height);
json.AddMember("refresh_height", value2, json.GetAllocator());
- value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
- json.AddMember("confirm_missing_payment_id", value2, json.GetAllocator());
-
value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
@@ -3673,6 +3790,12 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetInt(m_ignore_fractional_outputs ? 1 : 0);
json.AddMember("ignore_fractional_outputs", value2, json.GetAllocator());
+ value2.SetUint64(m_ignore_outputs_above);
+ json.AddMember("ignore_outputs_above", value2, json.GetAllocator());
+
+ value2.SetUint64(m_ignore_outputs_below);
+ json.AddMember("ignore_outputs_below", value2, json.GetAllocator());
+
value2.SetInt(m_track_uses ? 1 : 0);
json.AddMember("track_uses", value2, json.GetAllocator());
@@ -3715,6 +3838,15 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
json.AddMember("original_view_secret_key", value, json.GetAllocator());
}
+ value2.SetInt(m_persistent_rpc_client_id ? 1 : 0);
+ json.AddMember("persistent_rpc_client_id", value2, json.GetAllocator());
+
+ value2.SetFloat(m_auto_mine_for_rpc_payment_threshold);
+ json.AddMember("auto_mine_for_rpc_payment", value2, json.GetAllocator());
+
+ value2.SetUint64(m_credits_target);
+ json.AddMember("credits_target", value2, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -3819,7 +3951,6 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_auto_refresh = true;
m_refresh_type = RefreshType::RefreshDefault;
m_refresh_from_block_height = 0;
- m_confirm_missing_payment_id = true;
m_confirm_non_default_ring_size = true;
m_ask_password = AskPasswordToDecrypt;
cryptonote::set_default_decimal_point(CRYPTONOTE_DISPLAY_DECIMAL_POINT);
@@ -3834,6 +3965,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_key_reuse_mitigation2 = true;
m_segregation_height = 0;
m_ignore_fractional_outputs = true;
+ m_ignore_outputs_above = MONEY_SUPPLY;
+ m_ignore_outputs_below = 0;
m_track_uses = false;
m_inactivity_lock_timeout = DEFAULT_INACTIVITY_LOCK_TIMEOUT;
m_setup_background_mining = BackgroundMiningMaybe;
@@ -3845,6 +3978,9 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_device_derivation_path = "";
m_key_device_type = hw::device::device_type::SOFTWARE;
encrypted_secret_keys = false;
+ m_persistent_rpc_client_id = false;
+ m_auto_mine_for_rpc_payment_threshold = -1.0f;
+ m_credits_target = 0;
}
else if(json.IsObject())
{
@@ -3954,8 +4090,6 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0);
m_refresh_from_block_height = field_refresh_height;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true);
- m_confirm_missing_payment_id = field_confirm_missing_payment_id;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, AskPasswordType, Int, false, AskPasswordToDecrypt);
@@ -3990,6 +4124,10 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = field_segregation_height;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_fractional_outputs, int, Int, false, true);
m_ignore_fractional_outputs = field_ignore_fractional_outputs;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_outputs_above, uint64_t, Uint64, false, MONEY_SUPPLY);
+ m_ignore_outputs_above = field_ignore_outputs_above;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ignore_outputs_below, uint64_t, Uint64, false, 0);
+ m_ignore_outputs_below = field_ignore_outputs_below;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, track_uses, int, Int, false, false);
m_track_uses = field_track_uses;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, inactivity_lock_timeout, uint32_t, Uint, false, DEFAULT_INACTIVITY_LOCK_TIMEOUT);
@@ -4051,6 +4189,14 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
{
m_original_keys_available = false;
}
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, persistent_rpc_client_id, int, Int, false, false);
+ m_persistent_rpc_client_id = field_persistent_rpc_client_id;
+ // save as float, load as double, because it can happen you can't load back as float...
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_mine_for_rpc_payment, float, Double, false, FLT_MAX);
+ m_auto_mine_for_rpc_payment_threshold = field_auto_mine_for_rpc_payment;
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, credits_target, uint64_t, Uint64, false, 0);
+ m_credits_target = field_credits_target;
}
else
{
@@ -5266,6 +5412,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
void wallet2::set_offline(bool offline)
{
m_offline = offline;
+ m_node_rpc_proxy.set_offline(offline);
m_http_client.set_auto_connect(!offline);
if (offline)
{
@@ -5400,6 +5547,9 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
error::wallet_files_doesnt_correspond, m_keys_file, m_wallet_file);
}
+ if (!m_persistent_rpc_client_id)
+ set_rpc_client_secret_key(rct::rct2sk(rct::skGen()));
+
cryptonote::block genesis;
generate_genesis(genesis);
crypto::hash genesis_hash = get_block_hash(genesis);
@@ -5451,10 +5601,18 @@ void wallet2::trim_hashchain()
MINFO("Fixing empty hashchain");
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response res = AUTO_VAL_INIT(res);
- m_daemon_rpc_mutex.lock();
- req.height = m_blockchain.size() - 1;
- bool r = invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
+
+ bool r;
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ req.height = m_blockchain.size() - 1;
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, m_http_client, rpc_timeout);
+ if (r && res.status == CORE_RPC_STATUS_OK)
+ check_rpc_cost("getblockheaderbyheight", res.credits, pre_call_credits, COST_PER_BLOCK_HEADER);
+ }
+
if (r && res.status == CORE_RPC_STATUS_OK)
{
crypto::hash hash;
@@ -5530,7 +5688,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
boost::archive::portable_binary_oarchive ar(oss);
ar << *this;
- wallet2::cache_file_data cache_file_data = boost::value_initialized<wallet2::cache_file_data>();
+ wallet2::cache_file_data cache_file_data = {};
cache_file_data.cache_data = oss.str();
std::string cipher;
cipher.resize(cache_file_data.cache_data.size());
@@ -5556,6 +5714,11 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
const std::string address_file = m_wallet_file + ".address.txt";
r = save_to_file(address_file, m_account.get_public_address_str(m_nettype), true);
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_wallet_file);
+ // remove old address file
+ r = boost::filesystem::remove(old_address_file);
+ if (!r) {
+ LOG_ERROR("error removing file: " << old_address_file);
+ }
}
// remove old wallet file
r = boost::filesystem::remove(old_file);
@@ -5567,11 +5730,6 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
if (!r) {
LOG_ERROR("error removing file: " << old_keys_file);
}
- // remove old address file
- r = boost::filesystem::remove(old_address_file);
- if (!r) {
- LOG_ERROR("error removing file: " << old_address_file);
- }
// remove old message store file
if (boost::filesystem::exists(old_mms_file))
{
@@ -5811,15 +5969,19 @@ void wallet2::rescan_spent()
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 = invoke_http_json("/is_key_image_spent", req, daemon_resp, 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, get_rpc_status(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));
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "is_key_image_spent", error::is_key_image_spent_error, get_rpc_status(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));
+ check_rpc_cost("/is_key_image_spent", daemon_resp.credits, pre_call_credits, n_outputs * COST_PER_KEY_IMAGE);
+ }
+
std::copy(daemon_resp.spent_status.begin(), daemon_resp.spent_status.end(), std::back_inserter(spent_status));
}
@@ -6139,12 +6301,13 @@ void wallet2::commit_tx(pending_tx& ptx)
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json("/submit_raw_tx", oreq, ores, rpc_timeout, "POST");
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx");
- // MyMonero and OpenMonero use different status strings
- THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error);
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, m_http_client, rpc_timeout, "POST");
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx");
+ // MyMonero and OpenMonero use different status strings
+ THROW_WALLET_EXCEPTION_IF(ores.status != "OK" && ores.status != "success" , error::tx_rejected, ptx.tx, get_rpc_status(ores.status), ores.error);
+ }
}
else
{
@@ -6154,12 +6317,16 @@ void wallet2::commit_tx(pending_tx& ptx)
req.do_not_relay = false;
req.do_sanity_checks = true;
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json("/sendrawtransaction", req, daemon_send_resp, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction");
- THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
- THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_send_resp, "sendrawtransaction", error::tx_rejected, ptx.tx, get_rpc_status(daemon_send_resp.status), get_text_reason(daemon_send_resp));
+ check_rpc_cost("/sendrawtransaction", daemon_send_resp.credits, pre_call_credits, COST_PER_TX_RELAY);
+ }
+
// sanity checks
for (size_t idx: ptx.selected_transfers)
{
@@ -6932,7 +7099,7 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
return sign_multisig_tx_to_file(exported_txs, filename, txids);
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
+uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm)
{
static const struct
{
@@ -6974,7 +7141,7 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
return 1;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_dynamic_base_fee_estimate() const
+uint64_t wallet2::get_dynamic_base_fee_estimate()
{
uint64_t fee;
boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
@@ -6985,7 +7152,7 @@ uint64_t wallet2::get_dynamic_base_fee_estimate() const
return base_fee;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_base_fee() const
+uint64_t wallet2::get_base_fee()
{
if(m_light_wallet)
{
@@ -7001,7 +7168,7 @@ uint64_t wallet2::get_base_fee() const
return get_dynamic_base_fee_estimate();
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_fee_quantization_mask() const
+uint64_t wallet2::get_fee_quantization_mask()
{
if(m_light_wallet)
{
@@ -7018,7 +7185,7 @@ uint64_t wallet2::get_fee_quantization_mask() const
return fee_quantization_mask;
}
//----------------------------------------------------------------------------------------------------
-int wallet2::get_fee_algorithm() const
+int wallet2::get_fee_algorithm()
{
// changes at v3, v5, v8
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0))
@@ -7030,7 +7197,7 @@ int wallet2::get_fee_algorithm() const
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_min_ring_size() const
+uint64_t wallet2::get_min_ring_size()
{
if (use_fork_rules(8, 10))
return 11;
@@ -7043,14 +7210,14 @@ uint64_t wallet2::get_min_ring_size() const
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_max_ring_size() const
+uint64_t wallet2::get_max_ring_size()
{
if (use_fork_rules(8, 10))
return 11;
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
-uint64_t wallet2::adjust_mixin(uint64_t mixin) const
+uint64_t wallet2::adjust_mixin(uint64_t mixin)
{
const uint64_t min_ring_size = get_min_ring_size();
if (mixin + 1 < min_ring_size)
@@ -7093,7 +7260,8 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
// get the current full reward zone
uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
- throw_on_rpc_response_error(result, "get_info");
+ if (result)
+ return priority;
const uint64_t full_reward_zone = block_weight_limit / 2;
// get the last N block headers and sum the block sizes
@@ -7105,14 +7273,18 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
}
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request getbh_req = AUTO_VAL_INIT(getbh_req);
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response getbh_res = AUTO_VAL_INIT(getbh_res);
- m_daemon_rpc_mutex.lock();
getbh_req.start_height = m_blockchain.size() - N;
getbh_req.end_height = m_blockchain.size() - 1;
- bool r = invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange");
- THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange");
- THROW_WALLET_EXCEPTION_IF(getbh_res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, get_rpc_status(getbh_res.status));
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ getbh_req.client = get_client_signature();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, getbh_res, "getblockheadersrange", error::get_blocks_error, get_rpc_status(getbh_res.status));
+ check_rpc_cost("/sendrawtransaction", getbh_res.credits, pre_call_credits, N * COST_PER_BLOCK_HEADER);
+ }
+
if (getbh_res.headers.size() != N)
{
MERROR("Bad blockheaders size");
@@ -7329,17 +7501,18 @@ bool wallet2::find_and_save_rings(bool force)
size_t ntxes = slice + SLICE_SIZE > txs_hashes.size() ? txs_hashes.size() - slice : SLICE_SIZE;
for (size_t s = slice; s < slice + ntxes; ++s)
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s]));
- bool r;
+
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
- r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
}
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.txs.size() != req.txs_hashes.size(), error::wallet_internal_error,
- "daemon returned wrong response for gettransactions, wrong txs count = " +
- std::to_string(res.txs.size()) + ", expected " + std::to_string(req.txs_hashes.size()));
MDEBUG("Scanning " << res.txs.size() << " transactions");
THROW_WALLET_EXCEPTION_IF(slice + res.txs.size() > txs_hashes.size(), error::wallet_internal_error, "Unexpected tx array size");
@@ -7457,8 +7630,8 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
MDEBUG("LIGHTWALLET - Getting random outs");
- cryptonote::COMMAND_RPC_GET_RANDOM_OUTS::request oreq;
- cryptonote::COMMAND_RPC_GET_RANDOM_OUTS::response ores;
+ tools::COMMAND_RPC_GET_RANDOM_OUTS::request oreq;
+ tools::COMMAND_RPC_GET_RANDOM_OUTS::response ores;
size_t light_wallet_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
@@ -7472,11 +7645,15 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
}
oreq.count = light_wallet_requested_outputs_count;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json("/get_random_outs", oreq, ores, rpc_timeout, "POST");
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs");
- THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, m_http_client, rpc_timeout, "POST");
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs");
+ THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
+ size_t n_outs = 0; for (const auto &e: ores.amount_outs) n_outs += e.outputs.size();
+ }
// Check if we got enough outputs for each amount
for(auto& out: ores.amount_outs) {
@@ -7573,7 +7750,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// check whether we're shortly after the fork
uint64_t height;
boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
- throw_on_rpc_response_error(result, "get_info");
+ THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get height");
bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
bool is_after_segregation_fork = height >= segregation_fork_height;
@@ -7612,12 +7789,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
req_t.amounts.resize(std::distance(req_t.amounts.begin(), end));
req_t.unlocked = true;
req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, 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.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
- THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, get_rpc_status(resp_t.status));
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = get_client_signature();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, get_rpc_status(resp_t.status));
+ check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size());
+ }
}
// if we want to segregate fake outs pre or post fork, get distribution
@@ -7635,12 +7815,17 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
req_t.to_height = segregation_fork_height + 1;
req_t.cumulative = true;
req_t.binary = true;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, rpc_timeout * 1000);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
- THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution");
- THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_output_distribution, get_rpc_status(resp_t.status));
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = get_client_signature();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout * 1000);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_distribution", error::get_output_distribution, get_rpc_status(resp_t.status));
+ uint64_t expected_cost = 0;
+ for (uint64_t amount: req_t.amounts) expected_cost += (amount ? COST_PER_OUTPUT_DISTRIBUTION : COST_PER_OUTPUT_DISTRIBUTION_0);
+ check_rpc_cost("get_output_distribution", resp_t.credits, pre_call_credits, expected_cost);
+ }
// check we got all data
for(size_t idx: selected_transfers)
@@ -7984,15 +8169,18 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// get the keys for those
req.get_txid = false;
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_bin("/get_outs.bin", req, daemon_resp, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_outs_error, get_rpc_status(daemon_resp.status));
- THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
- "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
- std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, daemon_resp, "get_outs.bin", error::get_outs_error, get_rpc_status(daemon_resp.status));
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != req.outputs.size(), error::wallet_internal_error,
+ "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
+ std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
+ check_rpc_cost("/get_outs.bin", daemon_resp.credits, pre_call_credits, daemon_resp.outs.size() * COST_PER_OUT);
+ }
std::unordered_map<uint64_t, uint64_t> scanty_outs;
size_t base = 0;
@@ -8585,6 +8773,11 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
const transfer_details& td = m_transfers[i];
if (!is_spent(td, false) && !td.m_frozen && td.is_rct() && td.amount() >= needed_money && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
+ if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
+ {
+ MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]");
+ continue;
+ }
LOG_PRINT_L2("We can use " << i << " alone: " << print_money(td.amount()));
picks.push_back(i);
return picks;
@@ -8600,10 +8793,20 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money, ui
const transfer_details& td = m_transfers[i];
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && td.is_rct() && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
+ if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
+ {
+ MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]");
+ continue;
+ }
LOG_PRINT_L2("Considering input " << i << ", " << print_money(td.amount()));
for (size_t j = i + 1; j < m_transfers.size(); ++j)
{
const transfer_details& td2 = m_transfers[j];
+ if (td2.amount() > m_ignore_outputs_above || td2.amount() < m_ignore_outputs_below)
+ {
+ MDEBUG("Ignoring output " << j << " of amount " << print_money(td2.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]");
+ continue;
+ }
if (!is_spent(td2, false) && !td2.m_frozen && !td.m_key_image_partial && td2.is_rct() && td.amount() + td2.amount() >= needed_money && is_transfer_unlocked(td2) && td2.m_subaddr_index == td.m_subaddr_index)
{
// update our picks if those outputs are less related than any we
@@ -8813,7 +9016,7 @@ void wallet2::light_wallet_get_unspent_outs()
if(!add_transfer)
continue;
- m_transfers.push_back(boost::value_initialized<transfer_details>());
+ m_transfers.push_back(transfer_details{});
transfer_details& td = m_transfers.back();
td.m_block_height = o.height;
@@ -9303,11 +9506,16 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
const transfer_details& td = m_transfers[i];
if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
{
- MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
+ MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below fractional threshold " << print_money(fractional_threshold));
continue;
}
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && subaddr_indices.count(td.m_subaddr_index.minor) == 1)
{
+ if (td.amount() > m_ignore_outputs_above || td.amount() < m_ignore_outputs_below)
+ {
+ MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is outside prescribed range [" << print_money(m_ignore_outputs_below) << ", " << print_money(m_ignore_outputs_above) << "]");
+ continue;
+ }
const uint32_t index_minor = td.m_subaddr_index.minor;
auto find_predicate = [&index_minor](const std::pair<uint32_t, std::vector<size_t>>& x) { return x.first == index_minor; };
if ((td.is_rct()) || is_valid_decomposed_amount(td.amount()))
@@ -9786,6 +9994,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
std::vector<size_t> unused_dust_indices;
const bool use_rct = use_fork_rules(4, 0);
+ // determine threshold for fractional amount
+ const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
+ const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ const uint64_t base_fee = get_base_fee();
+ const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
+ const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
+ const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
+ THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
+ const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
+ const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
+
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, false) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet");
std::map<uint32_t, std::pair<std::vector<size_t>, std::vector<size_t>>> unused_transfer_dust_indices_per_subaddr;
@@ -9795,6 +10014,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
for (size_t i = 0; i < m_transfers.size(); ++i)
{
const transfer_details& td = m_transfers[i];
+ if (m_ignore_fractional_outputs && td.amount() < fractional_threshold)
+ {
+ MDEBUG("Ignoring output " << i << " of amount " << print_money(td.amount()) << " which is below threshold " << print_money(fractional_threshold));
+ continue;
+ }
if (!is_spent(td, false) && !td.m_frozen && !td.m_key_image_partial && (use_rct ? true : !td.is_rct()) && is_transfer_unlocked(td) && td.m_subaddr_index.major == subaddr_account && (subaddr_indices.empty() || subaddr_indices.count(td.m_subaddr_index.minor) == 1))
{
fund_found = true;
@@ -10167,22 +10391,21 @@ uint8_t wallet2::get_current_hard_fork()
return resp_t.version;
}
//----------------------------------------------------------------------------------------------------
-void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
+void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height)
{
boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
- throw_on_rpc_response_error(result, "get_hard_fork_info");
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
+bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks)
{
// TODO: How to get fork rule info from light wallet node?
if(m_light_wallet)
return true;
uint64_t height, earliest_height;
boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
- throw_on_rpc_response_error(result, "get_info");
+ THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get height");
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
- throw_on_rpc_response_error(result, "get_hard_fork_info");
+ THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Failed to get earliest fork height");
bool close_enough = (int64_t)height >= (int64_t)earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
if (close_enough)
@@ -10192,7 +10415,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
return close_enough;
}
//----------------------------------------------------------------------------------------------------
-uint64_t wallet2::get_upper_transaction_weight_limit() const
+uint64_t wallet2::get_upper_transaction_weight_limit()
{
if (m_upper_transaction_weight_limit > 0)
return m_upper_transaction_weight_limit;
@@ -10223,7 +10446,7 @@ std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(c
return outputs;
}
//----------------------------------------------------------------------------------------------------
-std::vector<uint64_t> wallet2::get_unspent_amounts_vector(bool strict) const
+std::vector<uint64_t> wallet2::get_unspent_amounts_vector(bool strict)
{
std::set<uint64_t> set;
for (const auto &td: m_transfers)
@@ -10244,18 +10467,22 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
{
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
if (is_trusted_daemon())
req_t.amounts = get_unspent_amounts_vector(false);
req_t.min_count = count;
req_t.max_count = 0;
req_t.unlocked = unlocked;
req_t.recent_cutoff = 0;
- bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
- THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
- THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = get_client_signature();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status);
+ uint64_t cost = req_t.amounts.empty() ? COST_PER_FULL_OUTPUT_HISTOGRAM : (COST_PER_OUTPUT_HISTOGRAM * req_t.amounts.size());
+ check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, cost);
+ }
std::set<uint64_t> mixable;
for (const auto &i: resp_t.histogram)
@@ -10283,19 +10510,22 @@ uint64_t wallet2::get_num_rct_outputs()
{
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
- m_daemon_rpc_mutex.lock();
req_t.amounts.push_back(0);
req_t.min_count = 0;
req_t.max_count = 0;
req_t.unlocked = true;
req_t.recent_cutoff = 0;
- bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, 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.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
- THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.status);
- THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
- THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req_t.client = get_client_signature();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, resp_t, "get_output_histogram", error::get_histogram_error, resp_t.status);
+ THROW_WALLET_EXCEPTION_IF(resp_t.histogram.size() != 1, error::get_histogram_error, "Expected exactly one response");
+ THROW_WALLET_EXCEPTION_IF(resp_t.histogram[0].amount != 0, error::get_histogram_error, "Expected 0 amount");
+ check_rpc_cost("get_output_histogram", resp_t.credits, pre_call_credits, COST_PER_OUTPUT_HISTOGRAM);
+ }
return resp_t.histogram[0].total_instances;
}
@@ -10416,17 +10646,22 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
req.decode_as_json = false;
req.prune = true;
- m_daemon_rpc_mutex.lock();
- bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
- error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ req.client = get_client_signature();
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, res.txs.size() * COST_PER_TX);
+ }
cryptonote::transaction tx;
crypto::hash tx_hash{};
cryptonote::blobdata tx_data;
crypto::hash tx_prefix_hash{};
- ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
+ bool ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data);
THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon");
THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash),
error::wallet_internal_error, "Failed to validate transaction from daemon");
@@ -10464,16 +10699,19 @@ void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_
req.prune = true;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
+ uint64_t pre_call_credits;
{
- const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
- r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
}
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
- "daemon returned wrong response for gettransactions, wrong txs count = " +
- std::to_string(res.txs.size()) + ", expected 1");
+
cryptonote::transaction tx;
crypto::hash tx_hash;
THROW_WALLET_EXCEPTION_IF(!get_pruned_tx(res.txs[0], tx, tx_hash), error::wallet_internal_error,
@@ -10514,16 +10752,18 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
req.prune = true;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
+ uint64_t pre_call_credits;
{
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
- r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
+ pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
}
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
- "daemon returned wrong response for gettransactions, wrong txs count = " +
- std::to_string(res.txs.size()) + ", expected 1");
cryptonote::transaction tx;
crypto::hash tx_hash;
@@ -10576,16 +10816,18 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
}
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
bool r;
+ uint64_t pre_call_credits;
{
- const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
- r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status);
+ THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error,
+ "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
+ std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
+ check_rpc_cost("/get_outs.bin", res.credits, pre_call_credits, ring_size * COST_PER_OUT);
}
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(res.outs.size() != ring_size, error::wallet_internal_error,
- "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
- std::to_string(res.outs.size()) + ", expected " + std::to_string(ring_size));
// copy pubkey pointers
std::vector<const crypto::public_key *> p_output_keys;
@@ -10632,16 +10874,18 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
req.prune = true;
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
+ uint64_t pre_call_credits;
{
- const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
- r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong txs count = " +
+ std::to_string(res.txs.size()) + ", expected 1");
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
}
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(res.txs.size() != 1, error::wallet_internal_error,
- "daemon returned wrong response for gettransactions, wrong txs count = " +
- std::to_string(res.txs.size()) + ", expected 1");
cryptonote::transaction tx;
crypto::hash tx_hash;
@@ -10705,16 +10949,18 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
}
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
bool r;
+ uint64_t pre_call_credits;
{
- const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
- r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_outs.bin", error::get_outs_error, res.status);
+ THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
+ "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
+ std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
+ check_rpc_cost("/get_outs.bin", res.credits, pre_call_credits, req.outputs.size() * COST_PER_OUT);
}
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::wallet_internal_error, "get_outs.bin");
- THROW_WALLET_EXCEPTION_IF(res.outs.size() != req.outputs.size(), error::wallet_internal_error,
- "daemon returned wrong response for get_outs.bin, wrong amounts count = " +
- std::to_string(res.outs.size()) + ", expected " + std::to_string(req.outputs.size()));
// copy pointers
std::vector<const crypto::public_key *> p_output_keys;
@@ -10804,11 +11050,17 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
req.decode_as_json = false;
req.prune = true;
- m_daemon_rpc_mutex.lock();
- bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
- error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ bool ok;
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
+ }
cryptonote::transaction tx;
crypto::hash tx_hash;
@@ -10853,11 +11105,17 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
req.decode_as_json = false;
req.prune = true;
- m_daemon_rpc_mutex.lock();
- bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
- error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ bool ok;
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
+ }
cryptonote::transaction tx;
crypto::hash tx_hash;
@@ -11008,11 +11266,17 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid));
req.decode_as_json = false;
req.prune = true;
- m_daemon_rpc_mutex.lock();
- bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
- error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ bool ok;
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+ check_rpc_cost("/gettransactions", res.credits, pre_call_credits, COST_PER_TX);
+ }
cryptonote::transaction tx;
crypto::hash tx_hash;
@@ -11300,22 +11564,33 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
gettx_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(proofs[i].txid));
gettx_req.decode_as_json = false;
gettx_req.prune = true;
- m_daemon_rpc_mutex.lock();
- bool ok = invoke_http_json("/gettransactions", gettx_req, gettx_res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
- error::wallet_internal_error, "Failed to get transaction from daemon");
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ gettx_req.client = get_client_signature();
+ bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, m_http_client);
+ THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
+ error::wallet_internal_error, "Failed to get transaction from daemon");
+ check_rpc_cost("/gettransactions", gettx_res.credits, pre_call_credits, gettx_res.txs.size() * COST_PER_TX);
+ }
// check spent status
COMMAND_RPC_IS_KEY_IMAGE_SPENT::request kispent_req;
COMMAND_RPC_IS_KEY_IMAGE_SPENT::response kispent_res;
for (size_t i = 0; i < proofs.size(); ++i)
kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image));
- m_daemon_rpc_mutex.lock();
- ok = invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
- error::wallet_internal_error, "Failed to get key image spent status from daemon");
+
+ bool ok;
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ kispent_req.client = get_client_signature();
+ ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, m_http_client, rpc_timeout);
+ THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
+ error::wallet_internal_error, "Failed to get key image spent status from daemon");
+ check_rpc_cost("/is_key_image_spent", kispent_res.credits, pre_call_credits, kispent_res.spent_status.size() * COST_PER_KEY_IMAGE);
+ }
total = spent = 0;
for (size_t i = 0; i < proofs.size(); ++i)
@@ -11401,7 +11676,7 @@ std::string wallet2::get_daemon_address() const
return m_daemon_address;
}
-uint64_t wallet2::get_daemon_blockchain_height(string &err) const
+uint64_t wallet2::get_daemon_blockchain_height(string &err)
{
uint64_t height;
@@ -11867,15 +12142,18 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
if(check_spent)
{
PERF_TIMER(import_key_images_RPC);
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, 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() != signed_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(signed_key_images.size()));
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, daemon_resp, "is_key_image_spent");
+ THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_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(signed_key_images.size()));
+ check_rpc_cost("/is_key_image_spent", daemon_resp.credits, pre_call_credits, daemon_resp.spent_status.size() * COST_PER_KEY_IMAGE);
+ }
+
for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n)
{
transfer_details &td = m_transfers[n + offset];
@@ -11953,13 +12231,16 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
PERF_TIMER_START(import_key_images_E);
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json("/gettransactions", gettxs_req, gettxs_res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
- THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
- "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ gettxs_req.client = get_client_signature();
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, gettxs_res, "gettransactions");
+ THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
+ "daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
+ check_rpc_cost("/gettransactions", gettxs_res.credits, pre_call_credits, spent_txids.size() * COST_PER_TX);
+ }
PERF_TIMER_STOP(import_key_images_E);
// process each outgoing tx
@@ -12888,7 +13169,17 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
height_mid,
height_max
};
- bool r = invoke_http_bin("/getblocks_by_height.bin", req, res, rpc_timeout);
+
+ bool r;
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ 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)
+ check_rpc_cost("/getblocks_by_height.bin", res.credits, pre_call_credits, 3 * COST_PER_BLOCK);
+ }
+
if (!r || res.status != CORE_RPC_STATUS_OK)
{
std::ostringstream oss;
@@ -12937,10 +13228,10 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
}
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::is_synced() const
+bool wallet2::is_synced()
{
uint64_t height;
- boost::optional<std::string> result = m_node_rpc_proxy.get_target_height(height);
+ boost::optional<std::string> result = m_node_rpc_proxy.get_height(height);
if (result && *result != CORE_RPC_STATUS_OK)
return false;
return get_blockchain_current_height() >= height;
@@ -12957,16 +13248,19 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
// get txpool backlog
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response res = AUTO_VAL_INIT(res);
- m_daemon_rpc_mutex.lock();
- bool r = invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, rpc_timeout);
- m_daemon_rpc_mutex.unlock();
- THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon");
- THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
- THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR(r, {}, res, "get_txpool_backlog", error::get_tx_pool_error);
+ check_rpc_cost("get_txpool_backlog", res.credits, pre_call_credits, COST_PER_TX_POOL_STATS * res.backlog.size());
+ }
uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
- throw_on_rpc_response_error(result, "get_info");
+ THROW_WALLET_EXCEPTION_IF(result, error::wallet_internal_error, "Invalid block weight limit from daemon");
uint64_t full_reward_zone = block_weight_limit / 2;
THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
@@ -13138,22 +13432,22 @@ std::string wallet2::get_rpc_status(const std::string &s) const
{
if (m_trusted_daemon)
return s;
+ if (s == CORE_RPC_STATUS_OK)
+ return s;
+ if (s == CORE_RPC_STATUS_BUSY || s == CORE_RPC_STATUS_PAYMENT_REQUIRED)
+ return s;
return "<error>";
}
//----------------------------------------------------------------------------------------------------
-void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const
+void wallet2::throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const
{
- // no error
- if (!status)
- return;
-
- MERROR("RPC error: " << method << ": status " << *status);
-
+ THROW_WALLET_EXCEPTION_IF(error.code, tools::error::wallet_coded_rpc_error, method, error.code, get_rpc_server_error_message(error.code));
+ THROW_WALLET_EXCEPTION_IF(!r, tools::error::no_connection_to_daemon, method);
// empty string -> not connection
- THROW_WALLET_EXCEPTION_IF(status->empty(), tools::error::no_connection_to_daemon, method);
+ THROW_WALLET_EXCEPTION_IF(status.empty(), tools::error::no_connection_to_daemon, method);
- THROW_WALLET_EXCEPTION_IF(*status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, method);
- THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error");
+ THROW_WALLET_EXCEPTION_IF(status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, method);
+ THROW_WALLET_EXCEPTION_IF(status == CORE_RPC_STATUS_PAYMENT_REQUIRED, tools::error::payment_required, method);
}
//----------------------------------------------------------------------------------------------------
@@ -13165,6 +13459,12 @@ bool wallet2::save_to_file(const std::string& path_to_file, const std::string& r
}
FILE *fp = fopen(path_to_file.c_str(), "w+");
+ if (!fp)
+ {
+ MERROR("Failed to open wallet file for writing: " << path_to_file << ": " << strerror(errno));
+ return false;
+ }
+
// Save the result b/c we need to close the fp before returning success/failure.
int write_result = PEM_write(fp, ASCII_OUTPUT_MAGIC.c_str(), "", (const unsigned char *) raw.c_str(), raw.length());
fclose(fp);
@@ -13299,4 +13599,43 @@ uint64_t wallet2::get_bytes_received() const
{
return m_http_client.get_bytes_received();
}
+//----------------------------------------------------------------------------------------------------
+std::vector<cryptonote::public_node> wallet2::get_public_nodes(bool white_only)
+{
+ cryptonote::COMMAND_RPC_GET_PUBLIC_NODES::request req = AUTO_VAL_INIT(req);
+ cryptonote::COMMAND_RPC_GET_PUBLIC_NODES::response res = AUTO_VAL_INIT(res);
+
+ req.white = true;
+ req.gray = !white_only;
+
+ {
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ bool r = epee::net_utils::invoke_http_json("/get_public_nodes", req, res, m_http_client, rpc_timeout);
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, {}, res, "/get_public_nodes");
+ }
+
+ std::vector<cryptonote::public_node> nodes;
+ nodes = res.white;
+ nodes.reserve(nodes.size() + res.gray.size());
+ std::copy(res.gray.begin(), res.gray.end(), std::back_inserter(nodes));
+ return nodes;
+}
+//----------------------------------------------------------------------------------------------------
+std::pair<size_t, uint64_t> wallet2::estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size)
+{
+ THROW_WALLET_EXCEPTION_IF(n_inputs <= 0, tools::error::wallet_internal_error, "Invalid n_inputs");
+ THROW_WALLET_EXCEPTION_IF(n_outputs < 0, tools::error::wallet_internal_error, "Invalid n_outputs");
+ THROW_WALLET_EXCEPTION_IF(ring_size < 0, tools::error::wallet_internal_error, "Invalid ring size");
+
+ if (ring_size == 0)
+ ring_size = get_min_ring_size();
+ if (n_outputs == 1)
+ n_outputs = 2; // extra dummy output
+
+ const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
+ size_t size = estimate_tx_size(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof);
+ uint64_t weight = estimate_tx_weight(use_rct, n_inputs, ring_size - 1, n_outputs, extra_size, bulletproof);
+ return std::make_pair(size, weight);
+}
+//----------------------------------------------------------------------------------------------------
}
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 95f6f507a..8f840a42d 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -64,10 +64,21 @@
#include "node_rpc_proxy.h"
#include "message_store.h"
#include "wallet_light_rpc.h"
+#include "wallet_rpc_helpers.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
+#define THROW_ON_RPC_RESPONSE_ERROR(r, error, res, method, ...) \
+ do { \
+ handle_payment_changes(res, std::integral_constant<bool, HasCredits<decltype(res)>::Has>()); \
+ throw_on_rpc_response_error(r, error, res.status, method); \
+ THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, ## __VA_ARGS__); \
+ } while(0)
+
+#define THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, err, res, method) \
+ THROW_ON_RPC_RESPONSE_ERROR(r, err, res, method, tools::error::wallet_generic_rpc_error, method, res.status)
+
class Serialization_portability_wallet_Test;
class wallet_accessor_test;
@@ -111,6 +122,8 @@ private:
wallet2 &w;
bool locked;
crypto::chacha_key key;
+ static boost::mutex lockers_lock;
+ static unsigned int lockers;
};
class i_wallet2_callback
@@ -875,6 +888,8 @@ private:
uint64_t get_last_block_reward() const { return m_last_block_reward; }
uint64_t get_device_last_key_image_sync() const { return m_device_last_key_image_sync; }
+ std::vector<cryptonote::public_node> get_public_nodes(bool white_only = true);
+
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
@@ -988,6 +1003,9 @@ private:
if(ver < 28)
return;
a & m_cold_key_images;
+ if(ver < 29)
+ return;
+ a & m_rpc_client_secret_key;
}
/*!
@@ -1019,8 +1037,6 @@ private:
void set_default_priority(uint32_t p) { m_default_priority = p; }
bool auto_refresh() const { return m_auto_refresh; }
void auto_refresh(bool r) { m_auto_refresh = r; }
- bool confirm_missing_payment_id() const { return m_confirm_missing_payment_id; }
- void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; }
AskPasswordType ask_password() const { return m_ask_password; }
void ask_password(AskPasswordType ask) { m_ask_password = ask; }
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
@@ -1047,6 +1063,10 @@ private:
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
+ uint64_t ignore_outputs_above() const { return m_ignore_outputs_above; }
+ void ignore_outputs_above(uint64_t value) { m_ignore_outputs_above = value; }
+ uint64_t ignore_outputs_below() const { return m_ignore_outputs_below; }
+ void ignore_outputs_below(uint64_t value) { m_ignore_outputs_below = value; }
bool track_uses() const { return m_track_uses; }
void track_uses(bool value) { m_track_uses = value; }
BackgroundMiningSetupType setup_background_mining() const { return m_setup_background_mining; }
@@ -1059,6 +1079,14 @@ private:
void device_derivation_path(const std::string &device_derivation_path) { m_device_derivation_path = device_derivation_path; }
const ExportFormat & export_format() const { return m_export_format; }
inline void set_export_format(const ExportFormat& export_format) { m_export_format = export_format; }
+ bool persistent_rpc_client_id() const { return m_persistent_rpc_client_id; }
+ void persistent_rpc_client_id(bool persistent) { m_persistent_rpc_client_id = persistent; }
+ void auto_mine_for_rpc_payment_threshold(float threshold) { m_auto_mine_for_rpc_payment_threshold = threshold; }
+ float auto_mine_for_rpc_payment_threshold() const { return m_auto_mine_for_rpc_payment_threshold; }
+ crypto::secret_key get_rpc_client_secret_key() const { return m_rpc_client_secret_key; }
+ void set_rpc_client_secret_key(const crypto::secret_key &key) { m_rpc_client_secret_key = key; m_node_rpc_proxy.set_client_secret_key(key); }
+ uint64_t credits_target() const { return m_credits_target; }
+ void credits_target(uint64_t threshold) { m_credits_target = threshold; }
bool get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
@@ -1098,6 +1126,7 @@ private:
*/
std::vector<address_book_row> get_address_book() const { return m_address_book; }
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress);
+ bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress);
bool delete_address_book_row(std::size_t row_id);
uint64_t get_num_rct_outputs();
@@ -1105,15 +1134,15 @@ private:
const transfer_details &get_transfer_details(size_t idx) const;
uint8_t get_current_hard_fork();
- void get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const;
- bool use_fork_rules(uint8_t version, int64_t early_blocks = 0) const;
- int get_fee_algorithm() const;
+ 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) const;
+ 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.
@@ -1192,7 +1221,8 @@ private:
bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false);
crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const;
- void update_pool_state(bool refreshed = false);
+ void update_pool_state(std::vector<std::pair<cryptonote::transaction, bool>> &process_txs, bool refreshed = false);
+ void process_pool_state(const std::vector<std::pair<cryptonote::transaction, bool>> &txs);
void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes);
std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const;
@@ -1208,21 +1238,39 @@ private:
uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31
- bool is_synced() const;
+ bool is_synced();
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
- uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const;
- uint64_t get_base_fee() const;
- uint64_t get_fee_quantization_mask() const;
- uint64_t get_min_ring_size() const;
- uint64_t get_max_ring_size() const;
- uint64_t adjust_mixin(uint64_t mixin) const;
+ uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
+ uint64_t get_base_fee();
+ uint64_t get_fee_quantization_mask();
+ uint64_t get_min_ring_size();
+ uint64_t get_max_ring_size();
+ uint64_t adjust_mixin(uint64_t mixin);
+
uint32_t adjust_priority(uint32_t priority);
bool is_unattended() const { return m_unattended; }
+ std::pair<size_t, uint64_t> estimate_tx_size_and_weight(bool use_rct, int n_inputs, int ring_size, int n_outputs, size_t extra_size);
+
+ bool get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie);
+ bool daemon_requires_payment();
+ bool make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance);
+ bool search_for_rpc_payment(uint64_t credits_target, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc = NULL, const std::function<void(const std::string&)> &errorfunc = NULL);
+ template<typename T> void handle_payment_changes(const T &res, std::true_type) {
+ if (res.status == CORE_RPC_STATUS_OK || res.status == CORE_RPC_STATUS_PAYMENT_REQUIRED)
+ m_rpc_payment_state.credits = res.credits;
+ if (res.top_hash != m_rpc_payment_state.top_hash)
+ {
+ m_rpc_payment_state.top_hash = res.top_hash;
+ m_rpc_payment_state.stale = true;
+ }
+ }
+ template<typename T> void handle_payment_changes(const T &res, std::false_type) {}
+
// Light wallet specific functions
// fetch unspent outs from lw node and store in m_transfers
void light_wallet_get_unspent_outs();
@@ -1335,6 +1383,9 @@ private:
void enable_dns(bool enable) { m_use_dns = enable; }
void set_offline(bool offline = true);
+ uint64_t credits() const { return m_rpc_payment_state.credits; }
+ void credit_report(uint64_t &expected_spent, uint64_t &discrepancy) const { expected_spent = m_rpc_payment_state.expected_spent; discrepancy = m_rpc_payment_state.discrepancy; }
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1350,17 +1401,17 @@ private:
* \param password Password of wallet file
*/
bool load_keys(const std::string& keys_file_name, const epee::wipeable_string& password);
- 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, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
+ void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint8_t block_version, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen, const tx_cache_data &tx_cache_data, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
bool should_skip_block(const cryptonote::block &b, uint64_t height) const;
void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const parsed_block &parsed_block, const crypto::hash& bl_id, uint64_t height, const std::vector<tx_cache_data> &tx_cache_data, size_t tx_cache_data_offset, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void detach_blockchain(uint64_t height, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
void get_short_chain_history(std::list<crypto::hash>& ids, uint64_t granularity = 1) const;
bool clear();
void clear_soft(bool keep_key_images=false);
- void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices);
+ void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> &o_indices, uint64_t &current_height);
void pull_hashes(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::vector<crypto::hash> &hashes);
void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false);
- void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &error);
+ void pull_and_parse_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception);
void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL);
uint64_t select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::vector<size_t>& selected_transfers) const;
bool prepare_file_names(const std::string& file_path);
@@ -1376,9 +1427,9 @@ private:
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
- uint64_t get_upper_transaction_weight_limit() const;
- std::vector<uint64_t> get_unspent_amounts_vector(bool strict) const;
- uint64_t get_dynamic_base_fee_estimate() const;
+ uint64_t get_upper_transaction_weight_limit();
+ std::vector<uint64_t> get_unspent_amounts_vector(bool strict);
+ uint64_t get_dynamic_base_fee_estimate();
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height);
@@ -1432,7 +1483,10 @@ private:
void on_device_progress(const hw::device_progress& event);
std::string get_rpc_status(const std::string &s) const;
- void throw_on_rpc_response_error(const boost::optional<std::string> &status, const char *method) const;
+ void throw_on_rpc_response_error(bool r, const epee::json_rpc::error &error, const std::string &status, const char *method) const;
+
+ std::string get_client_signature() const;
+ void check_rpc_cost(const char *call, uint64_t post_call_credits, uint64_t pre_credits, double expected_cost);
cryptonote::account_base m_account;
boost::optional<epee::net_utils::http::login> m_daemon_login;
@@ -1495,7 +1549,6 @@ private:
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
// m_refresh_from_block_height was defaulted to zero.*/
bool m_explicit_refresh_from_block_height;
- bool m_confirm_missing_payment_id;
bool m_confirm_non_default_ring_size;
AskPasswordType m_ask_password;
uint32_t m_min_output_count;
@@ -1509,9 +1562,13 @@ private:
bool m_key_reuse_mitigation2;
uint64_t m_segregation_height;
bool m_ignore_fractional_outputs;
+ uint64_t m_ignore_outputs_above;
+ uint64_t m_ignore_outputs_below;
bool m_track_uses;
uint32_t m_inactivity_lock_timeout;
BackgroundMiningSetupType m_setup_background_mining;
+ bool m_persistent_rpc_client_id;
+ float m_auto_mine_for_rpc_payment_threshold;
bool m_is_initialized;
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
@@ -1522,6 +1579,9 @@ private:
bool m_use_dns;
bool m_offline;
uint32_t m_rpc_version;
+ crypto::secret_key m_rpc_client_secret_key;
+ rpc_payment_state_t m_rpc_payment_state;
+ uint64_t m_credits_target;
// Aux transaction data from device
std::unordered_map<crypto::hash, std::string> m_tx_device;
@@ -1565,7 +1625,7 @@ private:
ExportFormat m_export_format;
};
}
-BOOST_CLASS_VERSION(tools::wallet2, 28)
+BOOST_CLASS_VERSION(tools::wallet2, 29)
BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index 9da9d109c..350f016c7 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -76,6 +76,10 @@ namespace wallet_args
{
return {"wallet-file", wallet_args::tr("Use wallet <arg>"), ""};
}
+ command_line::arg_descriptor<std::string> arg_rpc_client_secret_key()
+ {
+ return {"rpc-client-secret-key", wallet_args::tr("Set RPC client secret key for RPC payments"), ""};
+ }
const char* tr(const char* str)
{
diff --git a/src/wallet/wallet_args.h b/src/wallet/wallet_args.h
index c861dca11..59529662f 100644
--- a/src/wallet/wallet_args.h
+++ b/src/wallet/wallet_args.h
@@ -36,6 +36,7 @@ namespace wallet_args
{
command_line::arg_descriptor<std::string> arg_generate_from_json();
command_line::arg_descriptor<std::string> arg_wallet_file();
+ command_line::arg_descriptor<std::string> arg_rpc_client_secret_key();
const char* tr(const char* str);
diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h
index 6ebaaa395..3e94e604a 100644
--- a/src/wallet/wallet_errors.h
+++ b/src/wallet/wallet_errors.h
@@ -90,6 +90,7 @@ namespace tools
// is_key_image_spent_error
// get_histogram_error
// get_output_distribution
+ // payment_required
// wallet_files_doesnt_correspond
//
// * - class with protected ctor
@@ -781,6 +782,20 @@ namespace tools
const std::string m_status;
};
//----------------------------------------------------------------------------------------------------
+ struct wallet_coded_rpc_error : public wallet_rpc_error
+ {
+ explicit wallet_coded_rpc_error(std::string&& loc, const std::string& request, int code, const std::string& status)
+ : wallet_rpc_error(std::move(loc), std::string("error ") + std::to_string(code) + (" in ") + request + " RPC: " + status, request),
+ m_code(code), m_status(status)
+ {
+ }
+ int code() const { return m_code; }
+ const std::string& status() const { return m_status; }
+ private:
+ int m_code;
+ const std::string m_status;
+ };
+ //----------------------------------------------------------------------------------------------------
struct daemon_busy : public wallet_rpc_error
{
explicit daemon_busy(std::string&& loc, const std::string& request)
@@ -821,6 +836,14 @@ namespace tools
}
};
//----------------------------------------------------------------------------------------------------
+ struct payment_required: public wallet_rpc_error
+ {
+ explicit payment_required(std::string&& loc, const std::string& request)
+ : wallet_rpc_error(std::move(loc), "payment required", request)
+ {
+ }
+ };
+ //----------------------------------------------------------------------------------------------------
struct wallet_files_doesnt_correspond : public wallet_logic_error
{
explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file)
diff --git a/src/wallet/wallet_light_rpc.h b/src/wallet/wallet_light_rpc.h
index 1d35cec33..c2a7dc021 100644
--- a/src/wallet/wallet_light_rpc.h
+++ b/src/wallet/wallet_light_rpc.h
@@ -317,4 +317,51 @@ namespace tools
typedef epee::misc_utils::struct_init<response_t> response;
};
//-----------------------------------------------
+ struct COMMAND_RPC_GET_RANDOM_OUTS
+ {
+ struct request_t
+ {
+ std::vector<std::string> amounts;
+ uint32_t count;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amounts)
+ KV_SERIALIZE(count)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct output {
+ std::string public_key;
+ uint64_t global_index;
+ std::string rct; // 64+64+64 characters long (<rct commit> + <encrypted mask> + <rct amount>)
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(public_key)
+ KV_SERIALIZE(global_index)
+ KV_SERIALIZE(rct)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct amount_out {
+ uint64_t amount;
+ std::vector<output> outputs;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount)
+ KV_SERIALIZE(outputs)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct response_t
+ {
+ std::vector<amount_out> amount_outs;
+ std::string Error;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(amount_outs)
+ KV_SERIALIZE(Error)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+ //-----------------------------------------------
}
diff --git a/src/wallet/wallet_rpc_helpers.h b/src/wallet/wallet_rpc_helpers.h
new file mode 100644
index 000000000..4291a112d
--- /dev/null
+++ b/src/wallet/wallet_rpc_helpers.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2018-2019, 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 <type_traits>
+
+namespace
+{
+ // credits to yrp (https://stackoverflow.com/questions/87372/check-if-a-class-has-a-member-function-of-a-given-signature
+ template <typename T>
+ struct HasCredits
+ {
+ template<typename U, uint64_t (U::*)> struct SFINAE {};
+ template<typename U> static char Test(SFINAE<U, &U::credits>*);
+ template<typename U> static int Test(...);
+ static const bool Has = sizeof(Test<T>(0)) == sizeof(char);
+ };
+}
+
+namespace tools
+{
+ struct rpc_payment_state_t
+ {
+ uint64_t credits;
+ uint64_t expected_spent;
+ uint64_t discrepancy;
+ std::string top_hash;
+ bool stale;
+
+ rpc_payment_state_t(): credits(0), expected_spent(0), discrepancy(0), stale(true) {}
+ };
+
+ static inline void check_rpc_cost(rpc_payment_state_t &rpc_payment_state, const char *call, uint64_t post_call_credits, uint64_t pre_call_credits, double expected_cost)
+ {
+ uint64_t expected_credits = (uint64_t)expected_cost;
+ if (expected_credits == 0)
+ expected_credits = 1;
+
+ rpc_payment_state.credits = post_call_credits;
+ rpc_payment_state.expected_spent += expected_credits;
+
+ if (pre_call_credits <= post_call_credits)
+ return;
+
+ uint64_t cost = pre_call_credits - post_call_credits;
+
+ if (cost == expected_credits)
+ {
+ MDEBUG("Call " << call << " cost " << cost << " credits");
+ return;
+ }
+ MWARNING("Call " << call << " cost " << cost << " credits, expected " << expected_credits);
+
+ if (cost > expected_credits)
+ {
+ uint64_t d = cost - expected_credits;
+ if (rpc_payment_state.discrepancy > std::numeric_limits<uint64_t>::max() - d)
+ {
+ MERROR("Integer overflow in credit discrepancy calculation, setting to max");
+ rpc_payment_state.discrepancy = std::numeric_limits<uint64_t>::max();
+ }
+ else
+ {
+ rpc_payment_state.discrepancy += d;
+ }
+ }
+ }
+}
diff --git a/src/wallet/wallet_rpc_payments.cpp b/src/wallet/wallet_rpc_payments.cpp
new file mode 100644
index 000000000..41696d13b
--- /dev/null
+++ b/src/wallet/wallet_rpc_payments.cpp
@@ -0,0 +1,196 @@
+// Copyright (c) 2018-2019, 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 <boost/optional/optional.hpp>
+#include <boost/utility/value_init.hpp>
+#include "include_base_utils.h"
+#include "cryptonote_config.h"
+#include "wallet_rpc_helpers.h"
+#include "wallet2.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "rpc/core_rpc_server_commands_defs.h"
+#include "rpc/rpc_payment_signature.h"
+#include "misc_language.h"
+#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "int-util.h"
+#include "crypto/crypto.h"
+#include "cryptonote_basic/blobdatatype.h"
+#include "common/i18n.h"
+#include "common/util.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2.rpc_payments"
+
+#define RPC_PAYMENT_POLL_PERIOD 10 /* seconds*/
+
+namespace tools
+{
+//----------------------------------------------------------------------------------------------------
+std::string wallet2::get_client_signature() const
+{
+ return cryptonote::make_rpc_payment_signature(m_rpc_client_secret_key);
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::get_rpc_payment_info(bool mining, bool &payment_required, uint64_t &credits, uint64_t &diff, uint64_t &credits_per_hash_found, cryptonote::blobdata &hashing_blob, uint64_t &height, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, uint32_t &cookie)
+{
+ boost::optional<std::string> result = m_node_rpc_proxy.get_rpc_payment_info(mining, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie);
+ credits = m_rpc_payment_state.credits;
+ if (result && *result != CORE_RPC_STATUS_OK)
+ return false;
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::daemon_requires_payment()
+{
+ bool payment_required = false;
+ uint64_t credits, diff, credits_per_hash_found, height, seed_height;
+ uint32_t cookie;
+ cryptonote::blobdata blob;
+ crypto::hash seed_hash, next_seed_hash;
+ return get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::make_rpc_payment(uint32_t nonce, uint32_t cookie, uint64_t &credits, uint64_t &balance)
+{
+ cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::request req = AUTO_VAL_INIT(req);
+ cryptonote::COMMAND_RPC_ACCESS_SUBMIT_NONCE::response res = AUTO_VAL_INIT(res);
+ req.nonce = nonce;
+ req.cookie = cookie;
+ m_daemon_rpc_mutex.lock();
+ uint64_t pre_call_credits = m_rpc_payment_state.credits;
+ req.client = get_client_signature();
+ epee::json_rpc::error error;
+ bool r = epee::net_utils::invoke_http_json_rpc("/json_rpc", "rpc_access_submit_nonce", req, res, error, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_ON_RPC_RESPONSE_ERROR_GENERIC(r, error, res, "rpc_access_submit_nonce");
+ THROW_WALLET_EXCEPTION_IF(res.credits < pre_call_credits, error::wallet_internal_error, "RPC payment did not increase balance");
+ if (m_rpc_payment_state.top_hash != res.top_hash)
+ {
+ m_rpc_payment_state.top_hash = res.top_hash;
+ m_rpc_payment_state.stale = true;
+ }
+
+ m_rpc_payment_state.credits = res.credits;
+ balance = res.credits;
+ credits = balance - pre_call_credits;
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+bool wallet2::search_for_rpc_payment(uint64_t credits_target, const std::function<bool(uint64_t, uint64_t)> &startfunc, const std::function<bool(unsigned)> &contfunc, const std::function<bool(uint64_t)> &foundfunc, const std::function<void(const std::string&)> &errorfunc)
+{
+ bool need_payment = false;
+ bool payment_required;
+ uint64_t credits, diff, credits_per_hash_found, height, seed_height;
+ uint32_t cookie;
+ unsigned int n_hashes = 0;
+ cryptonote::blobdata hashing_blob;
+ crypto::hash seed_hash, next_seed_hash;
+ try
+ {
+ need_payment = get_rpc_payment_info(false, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required && credits < credits_target;
+ if (!need_payment)
+ return true;
+ if (!startfunc(diff, credits_per_hash_found))
+ return true;
+ }
+ catch (const std::exception &e) { return false; }
+
+ static std::atomic<uint32_t> nonce(0);
+ while (contfunc(n_hashes))
+ {
+ try
+ {
+ need_payment = get_rpc_payment_info(true, payment_required, credits, diff, credits_per_hash_found, hashing_blob, height, seed_height, seed_hash, next_seed_hash, cookie) && payment_required && credits < credits_target;
+ if (!need_payment)
+ return true;
+ }
+ catch (const std::exception &e) { return false; }
+ if (hashing_blob.empty())
+ {
+ MERROR("Bad hashing blob from daemon");
+ if (errorfunc)
+ errorfunc("Bad hashing blob from daemon, trying again");
+ epee::misc_utils::sleep_no_w(1000);
+ continue;
+ }
+
+ crypto::hash hash;
+ const uint32_t local_nonce = nonce++; // wrapping's OK
+ *(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce);
+ const uint8_t major_version = hashing_blob[0];
+ if (major_version >= RX_BLOCK_VERSION)
+ {
+ const int miners = 1;
+ crypto::rx_slow_hash(height, seed_height, seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash.data, miners, 0);
+ }
+ else
+ {
+ int cn_variant = hashing_blob[0] >= 7 ? hashing_blob[0] - 6 : 0;
+ crypto::cn_slow_hash(hashing_blob.data(), hashing_blob.size(), hash, cn_variant, height);
+ }
+ ++n_hashes;
+ if (cryptonote::check_hash(hash, diff))
+ {
+ uint64_t credits, balance;
+ try
+ {
+ make_rpc_payment(local_nonce, cookie, credits, balance);
+ if (credits != credits_per_hash_found)
+ {
+ MERROR("Found nonce, but daemon did not credit us with the expected amount");
+ if (errorfunc)
+ errorfunc("Found nonce, but daemon did not credit us with the expected amount");
+ return false;
+ }
+ MDEBUG("Found nonce " << local_nonce << " at diff " << diff << ", gets us " << credits_per_hash_found << ", now " << balance << " credits");
+ if (!foundfunc(credits))
+ break;
+ }
+ catch (const tools::error::wallet_coded_rpc_error &e)
+ {
+ MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
+ if (errorfunc)
+ errorfunc("Found nonce, but daemon errored out with error " + std::to_string(e.code()) + ": " + e.status() + ", continuing");
+ }
+ catch (const std::exception &e)
+ {
+ MWARNING("Found a local_nonce at diff " << diff << ", but failed to send it to the daemon");
+ if (errorfunc)
+ errorfunc("Found nonce, but daemon errored out with: '" + std::string(e.what()) + "', continuing");
+ }
+ }
+ }
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
+void wallet2::check_rpc_cost(const char *call, uint64_t post_call_credits, uint64_t pre_call_credits, double expected_cost)
+{
+ return tools::check_rpc_cost(m_rpc_payment_state, call, post_call_credits, pre_call_credits, expected_cost);
+}
+//----------------------------------------------------------------------------------------------------
+}
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 896f8f48e..eb89a96e9 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -151,6 +151,7 @@ namespace tools
if (m_wallet)
{
m_wallet->store();
+ m_wallet->deinit();
delete m_wallet;
m_wallet = NULL;
}
@@ -287,7 +288,7 @@ namespace tools
if (setup == tools::wallet2::BackgroundMiningMaybe)
{
MINFO("The daemon is not set up to background mine.");
- MINFO("With background mining enabled, the daemon will mine when idle and not on batttery.");
+ MINFO("With background mining enabled, the daemon will mine when idle and not on battery.");
MINFO("Enabling this supports the network you are using, and makes you eligible for receiving new monero");
MINFO("Set setup-background-mining to 1 in monero-wallet-cli to change.");
return;
@@ -306,7 +307,7 @@ namespace tools
return;
}
- MINFO("Background mining enabled. The daemon will mine when idle and not on batttery.");
+ MINFO("Background mining enabled. The daemon will mine when idle and not on battery.");
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::not_open(epee::json_rpc::error& er)
@@ -326,6 +327,7 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.unlock_time = pd.m_unlock_time;
+ entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
entry.fee = pd.m_fee;
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.type = pd.m_coinbase ? "block" : "in";
@@ -344,6 +346,7 @@ namespace tools
entry.height = pd.m_block_height;
entry.timestamp = pd.m_timestamp;
entry.unlock_time = pd.m_unlock_time;
+ entry.locked = !m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
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;
@@ -377,6 +380,7 @@ namespace tools
entry.fee = pd.m_amount_in - pd.m_amount_out;
entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
entry.unlock_time = pd.m_tx.unlock_time;
+ entry.locked = true;
entry.note = m_wallet->get_tx_note(txid);
for (const auto &d: pd.m_dests) {
@@ -405,6 +409,7 @@ namespace tools
entry.timestamp = pd.m_timestamp;
entry.amount = pd.m_amount;
entry.unlock_time = pd.m_unlock_time;
+ entry.locked = true;
entry.fee = pd.m_fee;
entry.note = m_wallet->get_tx_note(pd.m_tx_hash);
entry.double_spend_seen = ppd.m_double_spend_seen;
@@ -545,9 +550,29 @@ namespace tools
if (!m_wallet) return not_open(er);
try
{
- m_wallet->add_subaddress(req.account_index, req.label);
- res.address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
- res.address = m_wallet->get_subaddress_as_str({req.account_index, res.address_index});
+ if (req.count < 1 || req.count > 64) {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Count must be between 1 and 64.";
+ return false;
+ }
+
+ std::vector<std::string> addresses;
+ std::vector<uint32_t> address_indices;
+
+ addresses.reserve(req.count);
+ address_indices.reserve(req.count);
+
+ for (uint32_t i = 0; i < req.count; i++) {
+ m_wallet->add_subaddress(req.account_index, req.label);
+ uint32_t new_address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
+ address_indices.push_back(new_address_index);
+ addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index}));
+ }
+
+ res.address = addresses[0];
+ res.address_index = address_indices[0];
+ res.addresses = addresses;
+ res.address_indices = address_indices;
}
catch (const std::exception& e)
{
@@ -788,30 +813,9 @@ namespace tools
if (!payment_id.empty())
{
-
- /* Just to clarify */
- const std::string& payment_id_str = payment_id;
-
- crypto::hash long_payment_id;
- crypto::hash8 short_payment_id;
-
- /* Parse payment ID */
- if (wallet2::parse_long_payment_id(payment_id_str, long_payment_id)) {
- cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, long_payment_id);
- }
- else {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Payment id has invalid format: \"" + payment_id_str + "\", expected 64 character string";
- return false;
- }
-
- /* Append Payment ID data into extra */
- if (!cryptonote::add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
- er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Something went wrong with payment_id. Please check its format: \"" + payment_id_str + "\", expected 64-character string";
- return false;
- }
-
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
+ er.message = "Standalone payment IDs are obsolete. Use subaddresses or integrated addresses instead";
+ return false;
}
return true;
}
@@ -998,6 +1002,13 @@ namespace tools
std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, priority, extra, req.account_index, req.subaddr_indices);
LOG_PRINT_L2("on_transfer_split called create_transactions_2");
+ if (ptx_vector.empty())
+ {
+ er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
+ er.message = "No transaction created";
+ return false;
+ }
+
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.fee_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, er);
}
@@ -1189,8 +1200,11 @@ namespace tools
crypto::hash payment_id;
if(cryptonote::get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8))
{
- desc.payment_id = epee::string_tools::pod_to_hex(payment_id8);
- has_encrypted_payment_id = true;
+ if (payment_id8 != crypto::null_hash8)
+ {
+ desc.payment_id = epee::string_tools::pod_to_hex(payment_id8);
+ has_encrypted_payment_id = true;
+ }
}
else if (cryptonote::get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id))
{
@@ -1698,6 +1712,7 @@ namespace tools
rpc_payment.amount = payment.m_amount;
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
+ rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
rpc_payment.subaddr_index = payment.m_subaddr_index;
rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
res.payments.push_back(rpc_payment);
@@ -1727,6 +1742,7 @@ namespace tools
rpc_payment.unlock_time = payment.second.m_unlock_time;
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
+ rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.second.m_unlock_time, payment.second.m_block_height);
res.payments.push_back(std::move(rpc_payment));
}
@@ -1781,6 +1797,7 @@ namespace tools
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
+ rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
res.payments.push_back(std::move(rpc_payment));
}
}
@@ -2441,7 +2458,10 @@ namespace tools
if (req.pool)
{
- m_wallet->update_pool_state();
+ std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ m_wallet->update_pool_state(process_txs);
+ if (!process_txs.empty())
+ m_wallet->process_pool_state(process_txs);
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments;
m_wallet->get_unconfirmed_payments(payments, account_index, subaddr_indices);
@@ -2521,7 +2541,10 @@ namespace tools
}
}
- m_wallet->update_pool_state();
+ std::vector<std::pair<cryptonote::transaction, bool>> process_txs;
+ m_wallet->update_pool_state(process_txs);
+ if (!process_txs.empty())
+ m_wallet->process_pool_state(process_txs);
std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments;
m_wallet->get_unconfirmed_payments(pool_payments, req.account_index);
@@ -2816,6 +2839,108 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_edit_address_book(const wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (!m_wallet) return not_open(er);
+ if (m_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;
+ }
+
+ tools::wallet2::address_book_row entry = ab[req.index];
+
+ cryptonote::address_parse_info info;
+ crypto::hash payment_id = crypto::null_hash;
+ if (req.set_address)
+ {
+ er.message = "";
+ if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address,
+ [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
+ if (!dnssec_valid)
+ {
+ er.message = std::string("Invalid DNSSEC for ") + url;
+ return {};
+ }
+ if (addresses.empty())
+ {
+ er.message = std::string("No Monero address found at ") + url;
+ return {};
+ }
+ return addresses[0];
+ }))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
+ if (er.message.empty())
+ er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
+ return false;
+ }
+ entry.m_address = info.address;
+ entry.m_is_subaddress = info.is_subaddress;
+ if (info.has_payment_id)
+ {
+ memcpy(entry.m_payment_id.data, info.payment_id.data, 8);
+ memset(entry.m_payment_id.data + 8, 0, 24);
+ }
+ }
+
+ if (req.set_payment_id)
+ {
+ if (req.payment_id.empty())
+ {
+ payment_id = crypto::null_hash;
+ }
+ else
+ {
+ if (req.set_address && info.has_payment_id)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
+ er.message = "Separate payment ID given with integrated address";
+ return false;
+ }
+
+ if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
+ {
+ crypto::hash8 spid;
+ if (!wallet2::parse_short_payment_id(req.payment_id, spid))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
+ er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string";
+ return false;
+ }
+ else
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
+ er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
+ return false;
+ }
+ }
+ }
+
+ entry.m_payment_id = payment_id;
+ }
+
+ if (req.set_description)
+ entry.m_description = req.description;
+
+ if (!m_wallet->set_address_book_row(req.index, entry.m_address, entry.m_payment_id, entry.m_description, entry.m_is_subaddress))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to edit address book entry";
+ return false;
+ }
+ 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 connection_context *ctx)
{
if (!m_wallet) return not_open(er);
@@ -4186,6 +4311,25 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (!m_wallet) return not_open(er);
+ try
+ {
+ size_t extra_size = 34 /* pubkey */ + 10 /* encrypted payment id */; // typical makeup
+ const std::pair<size_t, uint64_t> sw = m_wallet->estimate_tx_size_and_weight(req.rct, req.n_inputs, req.ring_size, req.n_outputs, extra_size);
+ res.size = sw.first;
+ res.weight = sw.second;
+ }
+ catch (const std::exception &e)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+ er.message = "Failed to determine size and weight";
+ return false;
+ }
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
res.version = WALLET_RPC_VERSION;
@@ -4224,6 +4368,7 @@ public:
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json();
+ const auto arg_rpc_client_secret_key = wallet_args::arg_rpc_client_secret_key();
const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
const auto from_json = command_line::get_arg(vm, arg_from_json);
@@ -4272,6 +4417,17 @@ public:
return false;
}
+ if (!command_line::is_arg_defaulted(vm, arg_rpc_client_secret_key))
+ {
+ crypto::secret_key client_secret_key;
+ if (!epee::string_tools::hex_to_pod(command_line::get_arg(vm, arg_rpc_client_secret_key), client_secret_key))
+ {
+ MERROR(arg_rpc_client_secret_key.name << ": RPC client secret key should be 32 byte in hex format");
+ return false;
+ }
+ wal->set_rpc_client_secret_key(client_secret_key);
+ }
+
bool quit = false;
tools::signal_handler::install([&wal, &quit](int) {
assert(wal);
@@ -4370,6 +4526,7 @@ int main(int argc, char** argv) {
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json();
+ const auto arg_rpc_client_secret_key = wallet_args::arg_rpc_client_secret_key();
po::options_description hidden_options("Hidden");
@@ -4383,6 +4540,7 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_from_json);
command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password);
+ command_line::add_arg(desc_params, arg_rpc_client_secret_key);
daemonizer::init_options(hidden_options, desc_params);
desc_params.add(hidden_options);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index a327ed908..41f6879ef 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -127,6 +127,7 @@ namespace tools
MAP_JON_RPC_WE("parse_uri", on_parse_uri, wallet_rpc::COMMAND_RPC_PARSE_URI)
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("edit_address_book", on_edit_address_book, wallet_rpc::COMMAND_RPC_EDIT_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("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH)
MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH)
@@ -153,6 +154,7 @@ namespace tools
MAP_JON_RPC_WE("set_daemon", on_set_daemon, wallet_rpc::COMMAND_RPC_SET_DAEMON)
MAP_JON_RPC_WE("set_log_level", on_set_log_level, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL)
MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES)
+ MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT)
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -212,6 +214,7 @@ namespace tools
bool 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, const connection_context *ctx = NULL);
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, const connection_context *ctx = NULL);
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, const connection_context *ctx = NULL);
+ bool on_edit_address_book(const wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
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, const connection_context *ctx = NULL);
bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
@@ -238,6 +241,7 @@ namespace tools
bool on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
//json rpc v2
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index e9b8b60f5..8d601b050 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 14
+#define WALLET_RPC_VERSION_MINOR 17
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -182,11 +182,13 @@ namespace wallet_rpc
{
struct request_t
{
- uint32_t account_index;
+ uint32_t account_index;
+ uint32_t count;
std::string label;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
+ KV_SERIALIZE_OPT(count, 1U)
KV_SERIALIZE(label)
END_KV_SERIALIZE_MAP()
};
@@ -194,12 +196,16 @@ namespace wallet_rpc
struct response_t
{
- std::string address;
- uint32_t address_index;
+ std::string address;
+ uint32_t address_index;
+ std::vector<std::string> addresses;
+ std::vector<uint32_t> address_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(address_index)
+ KV_SERIALIZE(addresses)
+ KV_SERIALIZE(address_indices)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
@@ -916,6 +922,7 @@ namespace wallet_rpc
uint64_t amount;
uint64_t block_height;
uint64_t unlock_time;
+ bool locked;
cryptonote::subaddress_index subaddr_index;
std::string address;
@@ -925,6 +932,7 @@ namespace wallet_rpc
KV_SERIALIZE(amount)
KV_SERIALIZE(block_height)
KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(locked)
KV_SERIALIZE(subaddr_index)
KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
@@ -1364,6 +1372,7 @@ namespace wallet_rpc
std::list<transfer_destination> destinations;
std::string type;
uint64_t unlock_time;
+ bool locked;
cryptonote::subaddress_index subaddr_index;
std::vector<cryptonote::subaddress_index> subaddr_indices;
std::string address;
@@ -1382,6 +1391,7 @@ namespace wallet_rpc
KV_SERIALIZE(destinations);
KV_SERIALIZE(type);
KV_SERIALIZE(unlock_time)
+ KV_SERIALIZE(locked)
KV_SERIALIZE(subaddr_index);
KV_SERIALIZE(subaddr_indices);
KV_SERIALIZE(address);
@@ -1841,6 +1851,38 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY
+ {
+ struct request_t
+ {
+ uint64_t index;
+ bool set_address;
+ std::string address;
+ bool set_payment_id;
+ std::string payment_id;
+ bool set_description;
+ std::string description;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(index)
+ KV_SERIALIZE(set_address)
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(set_payment_id)
+ KV_SERIALIZE(payment_id)
+ KV_SERIALIZE(set_description)
+ KV_SERIALIZE(description)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY
{
struct request_t
@@ -2544,5 +2586,36 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT
+ {
+ struct request_t
+ {
+ uint32_t n_inputs;
+ uint32_t n_outputs;
+ uint32_t ring_size;
+ bool rct;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(n_inputs)
+ KV_SERIALIZE(n_outputs)
+ KV_SERIALIZE_OPT(ring_size, 0u)
+ KV_SERIALIZE_OPT(rct, true)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ uint64_t size;
+ uint64_t weight;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(size)
+ KV_SERIALIZE(weight)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
}
}