aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/blockchain_db.h19
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp103
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h1
-rw-r--r--src/blockchain_db/locked_txn.h (renamed from src/cryptonote_basic/cryptonote_stat_info.h)51
-rw-r--r--src/blockchain_db/testdb.h1
-rw-r--r--src/common/threadpool.cpp1
-rw-r--r--src/crypto/rx-slow-hash.c4
-rw-r--r--src/cryptonote_basic/CMakeLists.txt1
-rw-r--r--src/cryptonote_basic/account.cpp6
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp2
-rw-r--r--src/cryptonote_basic/verification_context.h1
-rw-r--r--src/cryptonote_config.h14
-rw-r--r--src/cryptonote_core/blockchain.cpp64
-rw-r--r--src/cryptonote_core/blockchain.h6
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp19
-rw-r--r--src/cryptonote_core/cryptonote_core.h19
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp2
-rw-r--r--src/cryptonote_core/tx_pool.cpp81
-rw-r--r--src/cryptonote_core/tx_pool.h5
-rw-r--r--src/cryptonote_core/tx_sanity_check.cpp16
-rw-r--r--src/cryptonote_core/tx_sanity_check.h6
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h18
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h7
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl76
-rw-r--r--src/cryptonote_protocol/levin_notify.cpp24
-rw-r--r--src/daemon/main.cpp2
-rw-r--r--src/daemon/rpc_command_executor.cpp10
-rw-r--r--src/device/device_default.cpp19
-rw-r--r--src/device/device_io_hid.cpp16
-rw-r--r--src/device/device_ledger.cpp70
-rw-r--r--src/device/device_ledger.hpp44
-rw-r--r--src/multisig/multisig.cpp7
-rw-r--r--src/net/i2p_address.cpp3
-rw-r--r--src/net/tor_address.cpp3
-rw-r--r--src/p2p/net_node.h19
-rw-r--r--src/p2p/net_node.inl149
-rw-r--r--src/p2p/net_peerlist.h9
-rw-r--r--src/p2p/p2p_protocol_defs.h126
-rw-r--r--src/ringct/bulletproofs.cc6
-rw-r--r--src/rpc/CMakeLists.txt1
-rw-r--r--src/rpc/bootstrap_daemon.cpp40
-rw-r--r--src/rpc/bootstrap_daemon.h34
-rw-r--r--src/rpc/bootstrap_node_selector.cpp117
-rw-r--r--src/rpc/bootstrap_node_selector.h103
-rw-r--r--src/rpc/core_rpc_server.cpp70
-rw-r--r--src/rpc/core_rpc_server.h4
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h2
-rw-r--r--src/rpc/daemon_handler.cpp5
-rw-r--r--src/rpc/message.cpp15
-rw-r--r--src/rpc/rpc_args.cpp4
-rw-r--r--src/rpc/rpc_payment.cpp4
-rw-r--r--src/rpc/rpc_payment_signature.cpp5
-rw-r--r--src/serialization/json_object.cpp26
-rw-r--r--src/serialization/json_object.h21
-rw-r--r--src/simplewallet/simplewallet.cpp48
-rw-r--r--src/simplewallet/simplewallet.h2
-rw-r--r--src/wallet/api/wallet.cpp26
-rw-r--r--src/wallet/api/wallet.h4
-rw-r--r--src/wallet/api/wallet2_api.h12
-rw-r--r--src/wallet/api/wallet_manager.cpp5
-rw-r--r--src/wallet/api/wallet_manager.h3
-rw-r--r--src/wallet/ringdb.cpp60
-rw-r--r--src/wallet/wallet2.cpp128
-rw-r--r--src/wallet/wallet2.h7
64 files changed, 1169 insertions, 607 deletions
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index e9fc85803..d3a218365 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -1291,6 +1291,25 @@ public:
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const = 0;
/**
+ * @brief fetches a variable number of blocks and transactions from the given height, in canonical blockchain order
+ *
+ * The subclass should return the blocks and transactions stored from the one with the given
+ * height. The number of blocks returned is variable, based on the max_size passed.
+ *
+ * @param start_height the height of the first block
+ * @param min_count the minimum number of blocks to return, if they exist
+ * @param max_count the maximum number of blocks to return
+ * @param max_size the maximum size of block/transaction data to return (will be exceeded by one blocks's worth at most, if min_count is met)
+ * @param blocks the returned block/transaction data
+ * @param pruned whether to return full or pruned tx data
+ * @param skip_coinbase whether to return or skip coinbase transactions (they're in blocks regardless)
+ * @param get_miner_tx_hash whether to calculate and return the miner (coinbase) tx hash
+ *
+ * @return true iff the blocks and transactions were found
+ */
+ virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const = 0;
+
+ /**
* @brief fetches the prunable transaction blob with the given hash
*
* The subclass should return the prunable transaction stored which has the given
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index 5093015f2..5cec8879d 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -1915,6 +1915,7 @@ bool BlockchainLMDB::get_txpool_tx_blob(const crypto::hash& txid, cryptonote::bl
// if filtering, make sure those requirements are met before copying blob
if (tx_category != relay_category::all)
{
+ RCURSOR(txpool_meta)
auto result = mdb_cursor_get(m_cur_txpool_meta, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND)
return false;
@@ -1986,7 +1987,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
const uint32_t log_stripes = tools::get_pruning_log_stripes(pruning_seed);
if (log_stripes && log_stripes != CRYPTONOTE_PRUNING_LOG_STRIPES)
throw0(DB_ERROR("Pruning seed not in range"));
- pruning_seed = tools::get_pruning_stripe(pruning_seed);;
+ pruning_seed = tools::get_pruning_stripe(pruning_seed);
if (pruning_seed > (1ul << CRYPTONOTE_PRUNING_LOG_STRIPES))
throw0(DB_ERROR("Pruning seed not in range"));
check_open();
@@ -3107,6 +3108,104 @@ bool BlockchainLMDB::get_pruned_tx_blobs_from(const crypto::hash& h, size_t coun
return true;
}
+bool BlockchainLMDB::get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(blocks);
+ RCURSOR(tx_indices);
+ RCURSOR(txs_pruned);
+ if (!pruned)
+ {
+ RCURSOR(txs_prunable);
+ }
+
+ blocks.reserve(std::min<size_t>(max_count, 10000)); // guard against very large max count if only checking bytes
+ const uint64_t blockchain_height = height();
+ uint64_t size = 0;
+ MDB_val_copy<uint64_t> key(start_height);
+ MDB_val k, v, val_tx_id;
+ uint64_t tx_id = ~0;
+ MDB_cursor_op op = MDB_SET;
+ for (uint64_t h = start_height; h < blockchain_height && blocks.size() < max_count && (size < max_size || blocks.size() < min_count); ++h)
+ {
+ MDB_cursor_op op = h == start_height ? MDB_SET : MDB_NEXT;
+ int result = mdb_cursor_get(m_cur_blocks, &key, &v, op);
+ if (result == MDB_NOTFOUND)
+ throw0(BLOCK_DNE(std::string("Attempt to get block from height ").append(boost::lexical_cast<std::string>(h)).append(" failed -- block not in db").c_str()));
+ else if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve a block from the db", result).c_str()));
+
+ blocks.resize(blocks.size() + 1);
+ auto &current_block = blocks.back();
+
+ current_block.first.first.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size);
+ size += v.mv_size;
+
+ cryptonote::block b;
+ if (!parse_and_validate_block_from_blob(current_block.first.first, b))
+ throw0(DB_ERROR("Invalid block"));
+ current_block.first.second = get_miner_tx_hash ? cryptonote::get_transaction_hash(b.miner_tx) : crypto::null_hash;
+
+ // get the tx_id for the first tx (the first block's coinbase tx)
+ if (h == start_height)
+ {
+ crypto::hash hash = cryptonote::get_transaction_hash(b.miner_tx);
+ MDB_val_set(v, hash);
+ result = mdb_cursor_get(m_cur_tx_indices, (MDB_val *)&zerokval, &v, MDB_GET_BOTH);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve block coinbase transaction from the db: ", result).c_str()));
+
+ const txindex *tip = (const txindex *)v.mv_data;
+ tx_id = tip->data.tx_id;
+ val_tx_id.mv_data = &tx_id;
+ val_tx_id.mv_size = sizeof(tx_id);
+ }
+
+ if (skip_coinbase)
+ {
+ result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ if (!pruned)
+ {
+ result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ }
+ }
+
+ op = MDB_NEXT;
+
+ current_block.second.reserve(b.tx_hashes.size());
+ for (const auto &tx_hash: b.tx_hashes)
+ {
+ // get pruned data
+ cryptonote::blobdata tx_blob;
+ result = mdb_cursor_get(m_cur_txs_pruned, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ tx_blob.assign((const char*)v.mv_data, v.mv_size);
+
+ if (!pruned)
+ {
+ result = mdb_cursor_get(m_cur_txs_prunable, &val_tx_id, &v, op);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve transaction data from the db: ", result).c_str()));
+ tx_blob.append(reinterpret_cast<const char*>(v.mv_data), v.mv_size);
+ }
+ current_block.second.push_back(std::make_pair(tx_hash, std::move(tx_blob)));
+ size += current_block.second.back().second.size();
+ }
+ }
+
+ TXN_POSTFIX_RDONLY();
+
+ return true;
+}
+
bool BlockchainLMDB::get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &bd) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -3271,7 +3370,7 @@ output_data_t BlockchainLMDB::get_output_key(const uint64_t& amount, const uint6
else
{
const pre_rct_outkey *okp = (const pre_rct_outkey *)v.mv_data;
- memcpy(&ret, &okp->data, sizeof(pre_rct_output_data_t));;
+ memcpy(&ret, &okp->data, sizeof(pre_rct_output_data_t));
if (include_commitmemt)
ret.commitment = rct::zeroCommit(amount);
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 7c0b4c72c..6ddeed671 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -255,6 +255,7 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const;
+ virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const;
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const;
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const;
diff --git a/src/cryptonote_basic/cryptonote_stat_info.h b/src/blockchain_db/locked_txn.h
index 4cc1bc764..d14631ddb 100644
--- a/src/cryptonote_basic/cryptonote_stat_info.h
+++ b/src/blockchain_db/locked_txn.h
@@ -1,21 +1,21 @@
// 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
@@ -25,30 +25,29 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
+//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
-#include "serialization/keyvalue_serialization.h"
-
namespace cryptonote
{
- struct core_stat_info_t
- {
- uint64_t tx_pool_size;
- uint64_t blockchain_height;
- uint64_t mining_speed;
- uint64_t alternative_blocks;
- std::string top_block_id_str;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(tx_pool_size)
- KV_SERIALIZE(blockchain_height)
- KV_SERIALIZE(mining_speed)
- KV_SERIALIZE(alternative_blocks)
- KV_SERIALIZE(top_block_id_str)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<core_stat_info_t> core_stat_info;
+ // This class is meant to create a batch when none currently exists.
+ // If a batch exists, it can't be from another thread, since we can
+ // only be called with the txpool lock taken, and it is held during
+ // the whole prepare/handle/cleanup incoming block sequence.
+ class LockedTXN {
+ public:
+ LockedTXN(BlockchainDB &db): m_db(db), m_batch(false), m_active(false) {
+ m_batch = m_db.batch_start();
+ m_active = true;
+ }
+ void commit() { try { if (m_batch && m_active) { m_db.batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } }
+ void abort() { try { if (m_batch && m_active) { m_db.batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } }
+ ~LockedTXN() { abort(); }
+ private:
+ BlockchainDB &m_db;
+ bool m_batch;
+ bool m_active;
+ };
}
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index 46de38c7e..638dd3b37 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -70,6 +70,7 @@ public:
virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_pruned_tx_blobs_from(const crypto::hash& h, size_t count, std::vector<cryptonote::blobdata> &bd) const { return false; }
+ virtual bool get_blocks_from(uint64_t start_height, size_t min_count, size_t max_count, size_t max_size, std::vector<std::pair<std::pair<cryptonote::blobdata, crypto::hash>, std::vector<std::pair<crypto::hash, cryptonote::blobdata>>>>& blocks, bool pruned, bool skip_coinbase, bool get_miner_tx_hash) const { return false; }
virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; }
virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }
diff --git a/src/common/threadpool.cpp b/src/common/threadpool.cpp
index 18204eeee..753bf238c 100644
--- a/src/common/threadpool.cpp
+++ b/src/common/threadpool.cpp
@@ -71,6 +71,7 @@ void threadpool::recycle() {
}
void threadpool::create(unsigned int max_threads) {
+ const boost::unique_lock<boost::mutex> lock(mutex);
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
max = max_threads ? max_threads : tools::get_max_concurrency();
diff --git a/src/crypto/rx-slow-hash.c b/src/crypto/rx-slow-hash.c
index 969bf8a0f..606c67336 100644
--- a/src/crypto/rx-slow-hash.c
+++ b/src/crypto/rx-slow-hash.c
@@ -284,6 +284,10 @@ void rx_slow_hash(const uint64_t mainheight, const uint64_t seedheight, const ch
CTHR_MUTEX_LOCK(rx_dataset_mutex);
if (rx_dataset != NULL && rx_dataset_height != seedheight)
rx_initdata(cache, miners, seedheight);
+ else if (rx_dataset == NULL) {
+ /* this is a no-op if the cache hasn't changed */
+ randomx_vm_set_cache(rx_vm, rx_sp->rs_cache);
+ }
CTHR_MUTEX_UNLOCK(rx_dataset_mutex);
} else {
/* this is a no-op if the cache hasn't changed */
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
index 5bb56e083..59040d8a2 100644
--- a/src/cryptonote_basic/CMakeLists.txt
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -54,7 +54,6 @@ set(cryptonote_basic_private_headers
cryptonote_basic_impl.h
cryptonote_boost_serialization.h
cryptonote_format_utils.h
- cryptonote_stat_info.h
difficulty.h
hardfork.h
miner.h
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index a9aec163b..02eca289e 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -40,13 +40,11 @@ extern "C"
}
#include "cryptonote_basic_impl.h"
#include "cryptonote_format_utils.h"
+#include "cryptonote_config.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "account"
-#define KEYS_ENCRYPTION_SALT 'k'
-
-
using namespace std;
DISABLE_VS_WARNINGS(4244 4345)
@@ -69,7 +67,7 @@ DISABLE_VS_WARNINGS(4244 4345)
static_assert(sizeof(base_key) == sizeof(crypto::hash), "chacha key and hash should be the same size");
epee::mlocked<tools::scrubbed_arr<char, sizeof(base_key)+1>> data;
memcpy(data.data(), &base_key, sizeof(base_key));
- data[sizeof(base_key)] = KEYS_ENCRYPTION_SALT;
+ data[sizeof(base_key)] = config::HASH_KEY_MEMORY;
crypto::generate_chacha_key(data.data(), sizeof(data), key, 1);
}
//-----------------------------------------------------------------
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 651d61b06..80747dd89 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -44,8 +44,6 @@ using namespace epee;
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "cn"
-#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
-
// #define ENABLE_HASH_CASH_INTEGRITY_CHECK
using namespace crypto;
diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h
index f5f663464..4d49b692c 100644
--- a/src/cryptonote_basic/verification_context.h
+++ b/src/cryptonote_basic/verification_context.h
@@ -47,7 +47,6 @@ namespace cryptonote
bool m_too_big;
bool m_overspend;
bool m_fee_too_low;
- bool m_not_rct;
bool m_too_few_outputs;
};
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 134b630f7..66af46a5f 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -144,8 +144,6 @@
#define RPC_IP_FAILS_BEFORE_BLOCK 3
-#define ALLOW_DEBUG_COMMANDS
-
#define CRYPTONOTE_NAME "bitmonero"
#define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin"
#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb"
@@ -193,7 +191,6 @@ namespace config
uint8_t const FEE_CALCULATION_MAX_RETRIES = 10;
uint64_t const DEFAULT_DUST_THRESHOLD = ((uint64_t)2000000000); // 2 * pow(10, 9)
uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8)
- std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000";
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18;
uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19;
@@ -207,6 +204,17 @@ namespace config
std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1";
uint32_t const GENESIS_NONCE = 10000;
+ // Hash domain separators
+ const char HASH_KEY_BULLETPROOF_EXPONENT[] = "bulletproof";
+ const char HASH_KEY_RINGDB[] = "ringdsb";
+ const char HASH_KEY_SUBADDRESS[] = "SubAddr";
+ const unsigned char HASH_KEY_ENCRYPTED_PAYMENT_ID = 0x8d;
+ const unsigned char HASH_KEY_WALLET = 0x8c;
+ const unsigned char HASH_KEY_WALLET_CACHE = 0x8d;
+ const unsigned char HASH_KEY_RPC_PAYMENT_NONCE = 0x58;
+ const unsigned char HASH_KEY_MEMORY = 'k';
+ const unsigned char HASH_KEY_MULTISIG[] = {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
namespace testnet
{
uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 53;
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index a8683a531..2571e4203 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -928,7 +928,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
for (auto& bl : original_chain)
{
block_verification_context bvc = {};
- bool r = handle_block_to_main_chain(bl, bvc);
+ bool r = handle_block_to_main_chain(bl, bvc, false);
CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC! failed to add (again) block while chain switching during the rollback!");
}
@@ -979,7 +979,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
block_verification_context bvc = {};
// add block to main chain
- bool r = handle_block_to_main_chain(bei.bl, bvc);
+ bool r = handle_block_to_main_chain(bei.bl, bvc, false);
// if adding block to main chain failed, rollback to previous state and
// return false
@@ -1044,6 +1044,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>
reorg_notify->notify("%s", std::to_string(split_height).c_str(), "%h", std::to_string(m_db->height()).c_str(),
"%n", std::to_string(m_db->height() - split_height).c_str(), "%d", std::to_string(discarded_blocks).c_str(), NULL);
+ std::shared_ptr<tools::Notify> block_notify = m_block_notify;
+ if (block_notify)
+ for (const auto &bei: alt_chain)
+ block_notify->notify("%s", epee::string_tools::pod_to_hex(get_block_hash(bei.bl)).c_str(), NULL);
+
MGINFO_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_db->height());
return true;
}
@@ -2488,38 +2493,10 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
}
db_rtxn_guard rtxn_guard(m_db);
- total_height = get_current_blockchain_height();
- size_t count = 0, size = 0;
blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height)));
- for(uint64_t i = start_height; i < total_height && count < max_count && (size < FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE || count < 3); i++, count++)
- {
- blocks.resize(blocks.size()+1);
- blocks.back().first.first = m_db->get_block_blob_from_height(i);
- block b;
- CHECK_AND_ASSERT_MES(parse_and_validate_block_from_blob(blocks.back().first.first, b), false, "internal error, invalid block");
- blocks.back().first.second = get_miner_tx_hash ? cryptonote::get_transaction_hash(b.miner_tx) : crypto::null_hash;
- std::vector<cryptonote::blobdata> txs;
- if (pruned)
- {
- CHECK_AND_ASSERT_MES(m_db->get_pruned_tx_blobs_from(b.tx_hashes.front(), b.tx_hashes.size(), txs), false, "Failed to retrieve all transactions needed");
- }
- else
- {
- std::vector<crypto::hash> mis;
- get_transactions_blobs(b.tx_hashes, txs, mis, pruned);
- CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found");
- }
- size += blocks.back().first.first.size();
- for (const auto &t: txs)
- size += t.size();
+ CHECK_AND_ASSERT_MES(m_db->get_blocks_from(start_height, 3, max_count, FIND_BLOCKCHAIN_SUPPLEMENT_MAX_SIZE, blocks, pruned, true, get_miner_tx_hash),
+ false, "Error getting blocks");
- CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size(), false, "mismatched sizes of b.tx_hashes and txs");
- blocks.back().second.reserve(txs.size());
- for (size_t i = 0; i < txs.size(); ++i)
- {
- blocks.back().second.push_back(std::make_pair(b.tx_hashes[i], std::move(txs[i])));
- }
- }
return true;
}
//------------------------------------------------------------------
@@ -2574,11 +2551,11 @@ bool Blockchain::have_block(const crypto::hash& id) const
return false;
}
//------------------------------------------------------------------
-bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_context& bvc)
+bool Blockchain::handle_block_to_main_chain(const block& bl, block_verification_context& bvc, bool notify/* = true*/)
{
LOG_PRINT_L3("Blockchain::" << __func__);
crypto::hash id = get_block_hash(bl);
- return handle_block_to_main_chain(bl, id, bvc);
+ return handle_block_to_main_chain(bl, id, bvc, notify);
}
//------------------------------------------------------------------
size_t Blockchain::get_total_transactions() const
@@ -3423,7 +3400,7 @@ bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
if (version >= HF_VERSION_PER_BYTE_FEE)
{
const bool use_long_term_median_in_fee = version >= HF_VERSION_LONG_TERM_BLOCK_WEIGHT;
- uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? m_long_term_effective_median_block_weight : median, version);
+ uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, use_long_term_median_in_fee ? std::min<uint64_t>(median, m_long_term_effective_median_block_weight) : median, version);
MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee");
needed_fee = tx_weight * fee_per_byte;
// quantize fee up to 8 decimals
@@ -3481,7 +3458,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
uint64_t already_generated_coins = db_height ? m_db->get_block_already_generated_coins(db_height - 1) : 0;
uint64_t base_reward;
- if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
+ if (!get_block_reward(m_current_block_cumul_weight_limit / 2, 1, already_generated_coins, base_reward, version))
{
MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound");
base_reward = BLOCK_REWARD_OVERESTIMATE;
@@ -3687,7 +3664,7 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
// Needs to validate the block and acquire each transaction from the
// transaction mem_pool, then pass the block and transactions to
// m_db->add_block()
-bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc)
+bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc, bool notify/* = true*/)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -4067,9 +4044,12 @@ leave:
get_difficulty_for_next_block(); // just to cache it
invalidate_block_template_cache();
- std::shared_ptr<tools::Notify> block_notify = m_block_notify;
- if (block_notify)
- block_notify->notify("%s", epee::string_tools::pod_to_hex(id).c_str(), NULL);
+ if (notify)
+ {
+ std::shared_ptr<tools::Notify> block_notify = m_block_notify;
+ if (block_notify)
+ block_notify->notify("%s", epee::string_tools::pod_to_hex(id).c_str(), NULL);
+ }
return true;
}
@@ -5176,12 +5156,12 @@ bool Blockchain::for_all_transactions(std::function<bool(const crypto::hash&, co
bool Blockchain::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const
{
- return m_db->for_all_outputs(f);;
+ return m_db->for_all_outputs(f);
}
bool Blockchain::for_all_outputs(uint64_t amount, std::function<bool(uint64_t height)> f) const
{
- return m_db->for_all_outputs(amount, f);;
+ return m_db->for_all_outputs(amount, f);
}
void Blockchain::invalidate_block_template_cache()
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 4ed7a2f02..3a89cc5df 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -1212,10 +1212,11 @@ namespace cryptonote
*
* @param bl the block to be added
* @param bvc metadata concerning the block's validity
+ * @param notify if set to true, sends new block notification on success
*
* @return true if the block was added successfully, otherwise false
*/
- bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc);
+ bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc, bool notify = true);
/**
* @brief validate and add a new block to the end of the blockchain
@@ -1227,10 +1228,11 @@ namespace cryptonote
* @param bl the block to be added
* @param id the hash of the block
* @param bvc metadata concerning the block's validity
+ * @param notify if set to true, sends new block notification on success
*
* @return true if the block was added successfully, otherwise false
*/
- bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
+ bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc, bool notify = true);
/**
* @brief validate and add a new block to an alternate blockchain
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 8b1613b4b..7fb232ad2 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -1056,17 +1056,6 @@ namespace cryptonote
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
- {
- st_inf.mining_speed = m_miner.get_speed();
- st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count();
- st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height();
- st_inf.tx_pool_size = m_mempool.get_transactions_count();
- st_inf.top_block_id_str = epee::string_tools::pod_to_hex(m_blockchain_storage.get_tail_id());
- return true;
- }
-
- //-----------------------------------------------------------------------------------------------
bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) const
{
if(!tx.vin.size())
@@ -1918,7 +1907,7 @@ namespace cryptonote
MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")");
if (p < threshold)
{
- MWARNING("There were " << b << (b == max_blocks_checked ? " or more" : "") << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack. Or it could be just sheer bad luck.");
+ MWARNING("There were " << b << (b == max_blocks_checked ? " or more" : "") << " blocks in the last " << seconds[n] / 60 << " minutes, there might be large hash rate changes, or we might be partitioned, cut off from the Monero network or under attack, or your computer's time is off. Or it could be just sheer bad luck.");
std::shared_ptr<tools::Notify> block_rate_notify = m_block_rate_notify;
if (block_rate_notify)
@@ -1946,6 +1935,12 @@ namespace cryptonote
{
m_blockchain_storage.flush_invalid_blocks();
}
+ //-----------------------------------------------------------------------------------------------
+ bool core::get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes)
+ {
+ return m_mempool.get_complement(hashes, txes);
+ }
+ //-----------------------------------------------------------------------------------------------
bool core::update_blockchain_pruning()
{
return m_blockchain_storage.update_blockchain_pruning();
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 5dec890e8..79a846de1 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -45,7 +45,6 @@
#include "blockchain.h"
#include "cryptonote_basic/miner.h"
#include "cryptonote_basic/connection_context.h"
-#include "cryptonote_basic/cryptonote_stat_info.h"
#include "warnings.h"
#include "crypto/hash.h"
#include "span.h"
@@ -556,15 +555,6 @@ namespace cryptonote
bool 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;
/**
- * @brief gets some stats about the daemon
- *
- * @param st_inf return-by-reference container for the stats requested
- *
- * @return true
- */
- bool get_stat_info(core_stat_info& st_inf) const;
-
- /**
* @copydoc Blockchain::get_tx_outputs_gindexs
*
* @note see Blockchain::get_tx_outputs_gindexs
@@ -864,6 +854,15 @@ namespace cryptonote
*/
void flush_invalid_blocks();
+ /**
+ * @brief returns the set of transactions in the txpool which are not in the argument
+ *
+ * @param hashes hashes of transactions to exclude from the result
+ *
+ * @return true iff success, false otherwise
+ */
+ bool get_txpool_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes);
+
private:
/**
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index f50fc61a5..b84a59698 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -142,7 +142,7 @@ namespace cryptonote
uint64_t summary_amounts = 0;
for (size_t no = 0; no < out_amounts.size(); no++)
{
- crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);;
+ crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation);
CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")");
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index 1bc475879..53a6ce579 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -38,6 +38,7 @@
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_config.h"
#include "blockchain.h"
+#include "blockchain_db/locked_txn.h"
#include "blockchain_db/blockchain_db.h"
#include "common/boost_serialization_helper.h"
#include "int-util.h"
@@ -88,25 +89,6 @@ namespace cryptonote
else
return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
}
-
- // This class is meant to create a batch when none currently exists.
- // If a batch exists, it can't be from another thread, since we can
- // only be called with the txpool lock taken, and it is held during
- // the whole prepare/handle/cleanup incoming block sequence.
- class LockedTXN {
- public:
- LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false), m_active(false) {
- m_batch = m_blockchain.get_db().batch_start();
- m_active = true;
- }
- void commit() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } }
- void abort() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } }
- ~LockedTXN() { abort(); }
- private:
- Blockchain &m_blockchain;
- bool m_batch;
- bool m_active;
- };
}
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
@@ -256,7 +238,7 @@ namespace cryptonote
if (kept_by_block)
m_parsed_tx_cache.insert(std::make_pair(id, tx));
CRITICAL_REGION_LOCAL1(m_blockchain);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
if (!insert_key_images(tx, id, tx_relay))
return false;
@@ -301,7 +283,7 @@ namespace cryptonote
if (kept_by_block)
m_parsed_tx_cache.insert(std::make_pair(id, tx));
CRITICAL_REGION_LOCAL1(m_blockchain);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
m_blockchain.remove_txpool_tx(id);
if (!insert_key_images(tx, id, tx_relay))
return false;
@@ -362,7 +344,7 @@ namespace cryptonote
if (bytes == 0)
bytes = m_txpool_max_weight;
CRITICAL_REGION_LOCAL1(m_blockchain);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
bool changed = false;
// this will never remove the first one, but we don't care
@@ -494,7 +476,7 @@ namespace cryptonote
try
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(id, meta))
{
@@ -549,7 +531,7 @@ namespace cryptonote
try
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(txid, meta))
{
@@ -594,6 +576,39 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------------------------
+ bool tx_memory_pool::get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const
+ {
+ CRITICAL_REGION_LOCAL(m_transactions_lock);
+ CRITICAL_REGION_LOCAL1(m_blockchain);
+
+ m_blockchain.for_all_txpool_txes([this, &hashes, &txes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) {
+ const auto relay_method = meta.get_relay_method();
+ if (relay_method != relay_method::block && relay_method != relay_method::fluff)
+ return true;
+ const auto i = std::find(hashes.begin(), hashes.end(), txid);
+ if (i == hashes.end())
+ {
+ cryptonote::blobdata bd;
+ try
+ {
+ if (!m_blockchain.get_txpool_tx_blob(txid, bd, cryptonote::relay_category::broadcasted))
+ {
+ MERROR("Failed to get blob for txpool transaction " << txid);
+ return true;
+ }
+ txes.emplace_back(std::move(bd));
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Failed to get blob for txpool transaction " << txid << ": " << e.what());
+ return true;
+ }
+ }
+ return true;
+ }, false);
+ return true;
+ }
+ //---------------------------------------------------------------------------------
void tx_memory_pool::on_idle()
{
m_remove_stuck_tx_interval.do_call([this](){return remove_stuck_transactions();});
@@ -638,7 +653,7 @@ namespace cryptonote
if (!remove.empty())
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const std::pair<crypto::hash, uint64_t> &entry: remove)
{
const crypto::hash &txid = entry.first;
@@ -709,7 +724,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
const time_t now = time(NULL);
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const auto& hash : hashes)
{
try
@@ -1186,7 +1201,7 @@ namespace cryptonote
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
bool changed = false;
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for(size_t i = 0; i!= tx.vin.size(); i++)
{
CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, void());
@@ -1268,7 +1283,11 @@ namespace cryptonote
fee = 0;
//baseline empty block
- get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version);
+ if (!get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version))
+ {
+ MERROR("Failed to get block reward for empty block");
+ return false;
+ }
size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
@@ -1278,7 +1297,7 @@ namespace cryptonote
LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it)
@@ -1415,7 +1434,7 @@ namespace cryptonote
size_t n_removed = 0;
if (!remove.empty())
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const crypto::hash &txid: remove)
{
try
@@ -1495,7 +1514,7 @@ namespace cryptonote
}
if (!remove.empty())
{
- LockedTXN lock(m_blockchain);
+ LockedTXN lock(m_blockchain.get_db());
for (const auto &txid: remove)
{
try
diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h
index f716440ad..ca0e50415 100644
--- a/src/cryptonote_core/tx_pool.h
+++ b/src/cryptonote_core/tx_pool.h
@@ -441,6 +441,11 @@ namespace cryptonote
*/
bool get_transaction_info(const crypto::hash &txid, tx_details &td) const;
+ /**
+ * @brief get transactions not in the passed set
+ */
+ bool get_complement(const std::vector<crypto::hash> &hashes, std::vector<cryptonote::blobdata> &txes) const;
+
private:
/**
diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp
index 03cbb5c26..e99982def 100644
--- a/src/cryptonote_core/tx_sanity_check.cpp
+++ b/src/cryptonote_core/tx_sanity_check.cpp
@@ -28,7 +28,7 @@
#include <stdint.h>
#include <vector>
-#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "blockchain.h"
#include "tx_sanity_check.h"
@@ -39,7 +39,7 @@
namespace cryptonote
{
-bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob)
+bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available)
{
cryptonote::transaction tx;
@@ -70,14 +70,18 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
n_indices += in_to_key.key_offsets.size();
}
+ return tx_sanity_check(rct_indices, n_indices, rct_outs_available);
+}
+
+bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available)
+{
if (n_indices <= 10)
{
MDEBUG("n_indices is only " << n_indices << ", not checking");
return true;
}
- uint64_t n_available = blockchain.get_num_mature_outputs(0);
- if (n_available < 10000)
+ if (rct_outs_available < 10000)
return true;
if (rct_indices.size() < n_indices * 8 / 10)
@@ -88,9 +92,9 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
uint64_t median = epee::misc_utils::median(offsets);
- if (median < n_available * 6 / 10)
+ if (median < rct_outs_available * 6 / 10)
{
- MERROR("median offset index is too low (median is " << median << " out of total " << n_available << "offsets). Transactions should contain a higher fraction of recent outputs.");
+ MERROR("median offset index is too low (median is " << median << " out of total " << rct_outs_available << "offsets). Transactions should contain a higher fraction of recent outputs.");
return false;
}
diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h
index c12d1b0b1..4a469462f 100644
--- a/src/cryptonote_core/tx_sanity_check.h
+++ b/src/cryptonote_core/tx_sanity_check.h
@@ -26,11 +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.
+#include <set>
#include "cryptonote_basic/blobdatatype.h"
namespace cryptonote
{
- class Blockchain;
-
- bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob);
+ bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_available);
+ bool tx_sanity_check(const std::set<uint64_t> &rct_indices, size_t n_indices, uint64_t rct_outs_available);
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index ee7e69eb7..f809bff74 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -353,5 +353,23 @@ namespace cryptonote
};
typedef epee::misc_utils::struct_init<request_t> request;
};
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ struct NOTIFY_GET_TXPOOL_COMPLEMENT
+ {
+ const static int ID = BC_COMMANDS_POOL_BASE + 10;
+
+ struct request_t
+ {
+ std::vector<crypto::hash> hashes;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_CONTAINER_POD_AS_BLOB(hashes)
+ 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 ddbd45a61..2664716a8 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -45,7 +45,6 @@
#include "block_queue.h"
#include "common/perf_timer.h"
#include "cryptonote_basic/connection_context.h"
-#include "cryptonote_basic/cryptonote_stat_info.h"
#include <boost/circular_buffer.hpp>
PUSH_WARNINGS
@@ -77,7 +76,6 @@ namespace cryptonote
{
public:
typedef cryptonote_connection_context connection_context;
- typedef core_stat_info stat_info;
typedef t_cryptonote_protocol_handler<t_core> cryptonote_protocol_handler;
typedef CORE_SYNC_DATA payload_type;
@@ -92,6 +90,7 @@ namespace cryptonote
HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry)
HANDLE_NOTIFY_T2(NOTIFY_NEW_FLUFFY_BLOCK, &cryptonote_protocol_handler::handle_notify_new_fluffy_block)
HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, &cryptonote_protocol_handler::handle_request_fluffy_missing_tx)
+ HANDLE_NOTIFY_T2(NOTIFY_GET_TXPOOL_COMPLEMENT, &cryptonote_protocol_handler::handle_notify_get_txpool_complement)
END_INVOKE_MAP2()
bool on_idle();
@@ -102,7 +101,6 @@ namespace cryptonote
bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital);
bool get_payload_sync_data(blobdata& data);
bool get_payload_sync_data(CORE_SYNC_DATA& hshd);
- bool get_stat_info(core_stat_info& stat_inf);
bool on_callback(cryptonote_connection_context& context);
t_core& get_core(){return m_core;}
bool is_synchronized(){return m_synchronized;}
@@ -127,6 +125,7 @@ namespace cryptonote
int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context);
int handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context);
int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context);
+ int handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context);
//----------------- i_bc_protocol_layout ---------------------------------------
virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context);
@@ -147,6 +146,7 @@ namespace cryptonote
int try_add_next_blocks(cryptonote_connection_context &context);
void notify_new_stripe(cryptonote_connection_context &context, uint32_t stripe);
void skip_unneeded_hashes(cryptonote_connection_context& context, bool check_block_queue) const;
+ bool request_txpool_complement(cryptonote_connection_context &context);
t_core& m_core;
@@ -156,6 +156,7 @@ namespace cryptonote
std::atomic<bool> m_synchronized;
std::atomic<bool> m_stopping;
std::atomic<bool> m_no_sync;
+ std::atomic<bool> m_ask_for_txpool_complement;
boost::mutex m_sync_lock;
block_queue m_block_queue;
epee::math_helper::once_a_time_seconds<30> m_idle_peer_kicker;
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index d11f198aa..3aacce421 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -83,6 +83,7 @@ namespace cryptonote
m_p2p(p_net_layout),
m_syncronized_connections_count(0),
m_synchronized(offline),
+ m_ask_for_txpool_complement(true),
m_stopping(false),
m_no_sync(false)
@@ -154,12 +155,6 @@ namespace cryptonote
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
- bool t_cryptonote_protocol_handler<t_core>::get_stat_info(core_stat_info& stat_inf)
- {
- return m_core.get_stat_info(stat_inf);
- }
- //------------------------------------------------------------------------------------------------------------------------
- template<class t_core>
void t_cryptonote_protocol_handler<t_core>::log_connections()
{
std::stringstream ss;
@@ -344,7 +339,7 @@ namespace cryptonote
if(m_core.have_block(hshd.top_id))
{
context.m_state = cryptonote_connection_context::state_normal;
- if(is_inital && target == m_core.get_current_blockchain_height())
+ if(is_inital && hshd.current_height >= target && target == m_core.get_current_blockchain_height())
on_connection_synchronized();
return true;
}
@@ -885,6 +880,34 @@ namespace cryptonote
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
+ int t_cryptonote_protocol_handler<t_core>::handle_notify_get_txpool_complement(int command, NOTIFY_GET_TXPOOL_COMPLEMENT::request& arg, cryptonote_connection_context& context)
+ {
+ MLOG_P2P_MESSAGE("Received NOTIFY_GET_TXPOOL_COMPLEMENT (" << arg.hashes.size() << " txes)");
+
+ std::vector<std::pair<cryptonote::blobdata, block>> local_blocks;
+ std::vector<cryptonote::blobdata> local_txs;
+
+ std::vector<cryptonote::blobdata> txes;
+ if (!m_core.get_txpool_complement(arg.hashes, txes))
+ {
+ LOG_ERROR_CCONTEXT("failed to get txpool complement");
+ return 1;
+ }
+
+ NOTIFY_NEW_TRANSACTIONS::request new_txes;
+ new_txes.txs = std::move(txes);
+
+ MLOG_P2P_MESSAGE
+ (
+ "-->>NOTIFY_NEW_TRANSACTIONS: "
+ << ", txs.size()=" << new_txes.txs.size()
+ );
+
+ post_notify<NOTIFY_NEW_TRANSACTIONS>(new_txes, context);
+ return 1;
+ }
+ //------------------------------------------------------------------------------------------------------------------------
+ template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context)
{
MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)");
@@ -2201,6 +2224,27 @@ skip:
}
m_core.safesyncmode(true);
m_p2p->clear_used_stripe_peers();
+
+ // ask for txpool complement from any suitable node if we did not yet
+ val_expected = true;
+ if (m_ask_for_txpool_complement.compare_exchange_strong(val_expected, false))
+ {
+ m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool
+ {
+ if(context.m_state < cryptonote_connection_context::state_synchronizing)
+ {
+ MDEBUG(context << "not ready, ignoring");
+ return true;
+ }
+ if (!request_txpool_complement(context))
+ {
+ MERROR(context << "Failed to request txpool complement");
+ return true;
+ }
+ return false;
+ });
+ }
+
return true;
}
//------------------------------------------------------------------------------------------------------------------------
@@ -2354,6 +2398,21 @@ skip:
}
//------------------------------------------------------------------------------------------------------------------------
template<class t_core>
+ bool t_cryptonote_protocol_handler<t_core>::request_txpool_complement(cryptonote_connection_context &context)
+ {
+ NOTIFY_GET_TXPOOL_COMPLEMENT::request r = {};
+ if (!m_core.get_pool_transaction_hashes(r.hashes, false))
+ {
+ MERROR("Failed to get txpool hashes");
+ return false;
+ }
+ MLOG_P2P_MESSAGE("-->>NOTIFY_GET_TXPOOL_COMPLEMENT: hashes.size()=" << r.hashes.size() );
+ post_notify<NOTIFY_GET_TXPOOL_COMPLEMENT>(r, context);
+ MLOG_PEER_STATE("requesting txpool complement");
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------
+ template<class t_core>
std::string t_cryptonote_protocol_handler<t_core>::get_peers_overview() const
{
std::stringstream ss;
@@ -2463,7 +2522,10 @@ skip:
MINFO("Target height decreasing from " << previous_target << " to " << target);
m_core.set_target_blockchain_height(target);
if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping)
+ {
MCWARNING("global", "monerod is now disconnected from the network");
+ m_ask_for_txpool_complement = true;
+ }
}
m_block_queue.flush_spans(context.m_connection_id, false);
diff --git a/src/cryptonote_protocol/levin_notify.cpp b/src/cryptonote_protocol/levin_notify.cpp
index e45c34e02..428b739bc 100644
--- a/src/cryptonote_protocol/levin_notify.cpp
+++ b/src/cryptonote_protocol/levin_notify.cpp
@@ -44,6 +44,9 @@
#include "net/dandelionpp.h"
#include "p2p/net_node.h"
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "net.p2p.tx"
+
namespace
{
int get_command_from_message(const cryptonote::blobdata &msg)
@@ -298,6 +301,8 @@ namespace levin
if (!channel.connection.is_nil())
channel.queue.push_back(std::move(message_));
+ else if (destination_ == 0 && zone_->connection_count == 0)
+ MWARNING("Unable to send transaction(s) over anonymity network - no available outbound connections");
}
};
@@ -358,7 +363,10 @@ namespace levin
});
for (auto& connection : connections)
+ {
+ std::sort(connection.first.begin(), connection.first.end()); // don't leak receive order
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);
@@ -390,10 +398,12 @@ namespace levin
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)
+ bool available = false;
+ zone_->p2p->foreach_connection([this, now, &in_duration, &out_duration, &next_flush, &available] (detail::p2p_context& context)
{
if (this->source_ != context.m_connection_id && (this->zone_->is_public || !context.m_is_income))
{
+ available = true;
if (context.fluff_txs.empty())
context.flush_time = now + (context.m_is_income ? in_duration() : out_duration());
@@ -405,6 +415,9 @@ namespace levin
return true;
});
+ if (!available)
+ MWARNING("Unable to send transaction(s), no available connections");
+
if (next_flush < zone_->flush_time)
fluff_flush::queue(std::move(zone_), next_flush);
}
@@ -564,9 +577,12 @@ namespace levin
{
channel.active = nullptr;
channel.connection = boost::uuids::nil_uuid();
- zone_->strand.post(
- update_channels{zone_, get_out_connections(*zone_->p2p)}
- );
+
+ auto connections = get_out_connections(*zone_->p2p);
+ if (connections.empty())
+ MWARNING("Lost all outbound connections to anonymity network - currently unable to send transaction(s)");
+
+ zone_->strand.post(update_channels{zone_, std::move(connections)});
}
}
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 8fa983fe5..3e25636d8 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -69,7 +69,7 @@ uint16_t parse_public_rpc_port(const po::variables_map &vm)
const auto &restricted_rpc_port = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
if (!command_line::is_arg_defaulted(vm, restricted_rpc_port))
{
- rpc_port_str = command_line::get_arg(vm, restricted_rpc_port);;
+ rpc_port_str = command_line::get_arg(vm, restricted_rpc_port);
}
else if (command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc))
{
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 086808f8e..80b14d534 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -1699,15 +1699,19 @@ bool t_rpc_command_executor::print_bans()
}
}
- for (auto i = res.bans.begin(); i != res.bans.end(); ++i)
+ if (!res.bans.empty())
{
- tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds";
+ for (auto i = res.bans.begin(); i != res.bans.end(); ++i)
+ {
+ tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds";
+ }
}
+ else
+ tools::msg_writer() << "No IPs are banned";
return true;
}
-
bool t_rpc_command_executor::ban(const std::string &address, time_t seconds)
{
cryptonote::COMMAND_RPC_SETBANS::request req;
diff --git a/src/device/device_default.cpp b/src/device/device_default.cpp
index dc06ce237..57ac7c1b2 100644
--- a/src/device/device_default.cpp
+++ b/src/device/device_default.cpp
@@ -36,9 +36,7 @@
#include "cryptonote_basic/subaddress_index.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "ringct/rctOps.h"
-
-#define ENCRYPTED_PAYMENT_ID_TAIL 0x8d
-#define CHACHA8_KEY_TAIL 0x8c
+#include "cryptonote_config.h"
namespace hw {
@@ -107,7 +105,7 @@ namespace hw {
epee::mlocked<tools::scrubbed_arr<char, sizeof(view_key) + sizeof(spend_key) + 1>> data;
memcpy(data.data(), &view_key, sizeof(view_key));
memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key));
- data[sizeof(data) - 1] = CHACHA8_KEY_TAIL;
+ data[sizeof(data) - 1] = config::HASH_KEY_WALLET;
crypto::generate_chacha_key(data.data(), sizeof(data), key, kdf_rounds);
return true;
}
@@ -196,14 +194,13 @@ namespace hw {
}
crypto::secret_key device_default::get_subaddress_secret_key(const crypto::secret_key &a, const cryptonote::subaddress_index &index) {
- const char prefix[] = "SubAddr";
- char data[sizeof(prefix) + sizeof(crypto::secret_key) + 2 * sizeof(uint32_t)];
- memcpy(data, prefix, sizeof(prefix));
- memcpy(data + sizeof(prefix), &a, sizeof(crypto::secret_key));
+ char data[sizeof(config::HASH_KEY_SUBADDRESS) + sizeof(crypto::secret_key) + 2 * sizeof(uint32_t)];
+ memcpy(data, config::HASH_KEY_SUBADDRESS, sizeof(config::HASH_KEY_SUBADDRESS));
+ memcpy(data + sizeof(config::HASH_KEY_SUBADDRESS), &a, sizeof(crypto::secret_key));
uint32_t idx = SWAP32LE(index.major);
- memcpy(data + sizeof(prefix) + sizeof(crypto::secret_key), &idx, sizeof(uint32_t));
+ memcpy(data + sizeof(config::HASH_KEY_SUBADDRESS) + sizeof(crypto::secret_key), &idx, sizeof(uint32_t));
idx = SWAP32LE(index.minor);
- memcpy(data + sizeof(prefix) + sizeof(crypto::secret_key) + sizeof(uint32_t), &idx, sizeof(uint32_t));
+ memcpy(data + sizeof(config::HASH_KEY_SUBADDRESS) + sizeof(crypto::secret_key) + sizeof(uint32_t), &idx, sizeof(uint32_t));
crypto::secret_key m;
crypto::hash_to_scalar(data, sizeof(data), m);
return m;
@@ -344,7 +341,7 @@ namespace hw {
return false;
memcpy(data, &derivation, 32);
- data[32] = ENCRYPTED_PAYMENT_ID_TAIL;
+ data[32] = config::HASH_KEY_ENCRYPTED_PAYMENT_ID;
cn_fast_hash(data, 33, hash);
for (size_t b = 0; b < 8; ++b)
diff --git a/src/device/device_io_hid.cpp b/src/device/device_io_hid.cpp
index 72f4c3bdb..840529c38 100644
--- a/src/device/device_io_hid.cpp
+++ b/src/device/device_io_hid.cpp
@@ -44,8 +44,20 @@ namespace hw {
static std::string safe_hid_error(hid_device *hwdev) {
if (hwdev) {
- const char* error_str = (const char*)hid_error(hwdev);
- return std::string(error_str == nullptr ? "Unknown error" : error_str);
+ const wchar_t* error_wstr = hid_error(hwdev);
+ if (error_wstr == nullptr)
+ {
+ return "Unknown error";
+ }
+ std::mbstate_t state{};
+ const size_t len_symbols = std::wcsrtombs(nullptr, &error_wstr, 0, &state);
+ if (len_symbols == static_cast<std::size_t>(-1))
+ {
+ return "Failed to convert wide char error";
+ }
+ std::string error_str(len_symbols + 1, 0);
+ std::wcsrtombs(&error_str[0], &error_wstr, error_str.size(), &state);
+ return error_str;
}
return std::string("NULL device");
}
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index 49f54e5a5..eaa9f910d 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -55,7 +55,10 @@ namespace hw {
}
#define TRACKD MTRACE("hw")
- #define ASSERT_SW(sw,ok,msk) CHECK_AND_ASSERT_THROW_MES(((sw)&(mask))==(ok), "Wrong Device Status : SW=" << std::hex << (sw) << " (EXPECT=" << std::hex << (ok) << ", MASK=" << std::hex << (mask) << ")") ;
+ #define ASSERT_SW(sw,ok,msk) CHECK_AND_ASSERT_THROW_MES(((sw)&(mask))==(ok), \
+ "Wrong Device Status: " << "0x" << std::hex << (sw) << " (" << Status::to_string(sw) << "), " << \
+ "EXPECTED 0x" << std::hex << (ok) << " (" << Status::to_string(ok) << "), " << \
+ "MASK 0x" << std::hex << (mask));
#define ASSERT_T0(exp) CHECK_AND_ASSERT_THROW_MES(exp, "Protocol assert failure: "#exp ) ;
#define ASSERT_X(exp,msg) CHECK_AND_ASSERT_THROW_MES(exp, msg);
@@ -64,6 +67,71 @@ namespace hw {
crypto::secret_key dbg_spendkey;
#endif
+ struct Status
+ {
+ unsigned int code;
+ const char *string;
+
+ constexpr operator unsigned int() const
+ {
+ return this->code;
+ }
+
+ static const char *to_string(unsigned int code);
+ };
+
+ // Must be sorted in ascending order by the code
+ #define LEDGER_STATUS(status) {status, #status}
+ constexpr Status status_codes[] = {
+ LEDGER_STATUS(SW_BYTES_REMAINING_00),
+ LEDGER_STATUS(SW_WARNING_STATE_UNCHANGED),
+ LEDGER_STATUS(SW_STATE_TERMINATED),
+ LEDGER_STATUS(SW_MORE_DATA_AVAILABLE),
+ LEDGER_STATUS(SW_WRONG_LENGTH),
+ LEDGER_STATUS(SW_LOGICAL_CHANNEL_NOT_SUPPORTED),
+ LEDGER_STATUS(SW_SECURE_MESSAGING_NOT_SUPPORTED),
+ LEDGER_STATUS(SW_LAST_COMMAND_EXPECTED),
+ LEDGER_STATUS(SW_COMMAND_CHAINING_NOT_SUPPORTED),
+ LEDGER_STATUS(SW_SECURITY_LOAD_KEY),
+ LEDGER_STATUS(SW_SECURITY_COMMITMENT_CONTROL),
+ LEDGER_STATUS(SW_SECURITY_AMOUNT_CHAIN_CONTROL),
+ LEDGER_STATUS(SW_SECURITY_COMMITMENT_CHAIN_CONTROL),
+ LEDGER_STATUS(SW_SECURITY_OUTKEYS_CHAIN_CONTROL),
+ LEDGER_STATUS(SW_SECURITY_MAXOUTPUT_REACHED),
+ LEDGER_STATUS(SW_SECURITY_TRUSTED_INPUT),
+ LEDGER_STATUS(SW_CLIENT_NOT_SUPPORTED),
+ LEDGER_STATUS(SW_SECURITY_STATUS_NOT_SATISFIED),
+ LEDGER_STATUS(SW_FILE_INVALID),
+ LEDGER_STATUS(SW_PIN_BLOCKED),
+ LEDGER_STATUS(SW_DATA_INVALID),
+ LEDGER_STATUS(SW_CONDITIONS_NOT_SATISFIED),
+ LEDGER_STATUS(SW_COMMAND_NOT_ALLOWED),
+ LEDGER_STATUS(SW_APPLET_SELECT_FAILED),
+ LEDGER_STATUS(SW_WRONG_DATA),
+ LEDGER_STATUS(SW_FUNC_NOT_SUPPORTED),
+ LEDGER_STATUS(SW_FILE_NOT_FOUND),
+ LEDGER_STATUS(SW_RECORD_NOT_FOUND),
+ LEDGER_STATUS(SW_FILE_FULL),
+ LEDGER_STATUS(SW_INCORRECT_P1P2),
+ LEDGER_STATUS(SW_REFERENCED_DATA_NOT_FOUND),
+ LEDGER_STATUS(SW_WRONG_P1P2),
+ LEDGER_STATUS(SW_CORRECT_LENGTH_00),
+ LEDGER_STATUS(SW_INS_NOT_SUPPORTED),
+ LEDGER_STATUS(SW_CLA_NOT_SUPPORTED),
+ LEDGER_STATUS(SW_UNKNOWN),
+ LEDGER_STATUS(SW_OK),
+ LEDGER_STATUS(SW_ALGORITHM_UNSUPPORTED)
+ };
+
+ const char *Status::to_string(unsigned int code)
+ {
+ constexpr size_t status_codes_size = sizeof(status_codes) / sizeof(status_codes[0]);
+ constexpr const Status *status_codes_end = &status_codes[status_codes_size];
+
+ const Status *item = std::lower_bound(&status_codes[0], status_codes_end, code);
+ return (item == status_codes_end || code < *item) ? "UNKNOWN" : item->string;
+ }
+
/* ===================================================================== */
/* === hmacmap ==== */
/* ===================================================================== */
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index 05a26143a..e3e30fba8 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -58,6 +58,46 @@ namespace hw {
#ifdef WITH_DEVICE_LEDGER
+ // Origin: https://github.com/LedgerHQ/ledger-app-monero/blob/master/src/monero_types.h
+ #define SW_BYTES_REMAINING_00 0x6100
+ #define SW_WARNING_STATE_UNCHANGED 0x6200
+ #define SW_STATE_TERMINATED 0x6285
+ #define SW_MORE_DATA_AVAILABLE 0x6310
+ #define SW_WRONG_LENGTH 0x6700
+ #define SW_LOGICAL_CHANNEL_NOT_SUPPORTED 0x6881
+ #define SW_SECURE_MESSAGING_NOT_SUPPORTED 0x6882
+ #define SW_LAST_COMMAND_EXPECTED 0x6883
+ #define SW_COMMAND_CHAINING_NOT_SUPPORTED 0x6884
+ #define SW_SECURITY_LOAD_KEY 0x6900
+ #define SW_SECURITY_COMMITMENT_CONTROL 0x6911
+ #define SW_SECURITY_AMOUNT_CHAIN_CONTROL 0x6912
+ #define SW_SECURITY_COMMITMENT_CHAIN_CONTROL 0x6913
+ #define SW_SECURITY_OUTKEYS_CHAIN_CONTROL 0x6914
+ #define SW_SECURITY_MAXOUTPUT_REACHED 0x6915
+ #define SW_SECURITY_TRUSTED_INPUT 0x6916
+ #define SW_CLIENT_NOT_SUPPORTED 0x6930
+ #define SW_SECURITY_STATUS_NOT_SATISFIED 0x6982
+ #define SW_FILE_INVALID 0x6983
+ #define SW_PIN_BLOCKED 0x6983
+ #define SW_DATA_INVALID 0x6984
+ #define SW_CONDITIONS_NOT_SATISFIED 0x6985
+ #define SW_COMMAND_NOT_ALLOWED 0x6986
+ #define SW_APPLET_SELECT_FAILED 0x6999
+ #define SW_WRONG_DATA 0x6a80
+ #define SW_FUNC_NOT_SUPPORTED 0x6a81
+ #define SW_FILE_NOT_FOUND 0x6a82
+ #define SW_RECORD_NOT_FOUND 0x6a83
+ #define SW_FILE_FULL 0x6a84
+ #define SW_INCORRECT_P1P2 0x6a86
+ #define SW_REFERENCED_DATA_NOT_FOUND 0x6a88
+ #define SW_WRONG_P1P2 0x6b00
+ #define SW_CORRECT_LENGTH_00 0x6c00
+ #define SW_INS_NOT_SUPPORTED 0x6d00
+ #define SW_CLA_NOT_SUPPORTED 0x6e00
+ #define SW_UNKNOWN 0x6f00
+ #define SW_OK 0x9000
+ #define SW_ALGORITHM_UNSUPPORTED 0x9484
+
namespace {
bool apdu_verbose =true;
}
@@ -128,8 +168,8 @@ namespace hw {
unsigned int id;
void logCMD(void);
void logRESP(void);
- unsigned int exchange(unsigned int ok=0x9000, unsigned int mask=0xFFFF);
- unsigned int exchange_wait_on_input(unsigned int ok=0x9000, unsigned int mask=0xFFFF);
+ unsigned int exchange(unsigned int ok=SW_OK, unsigned int mask=0xFFFF);
+ unsigned int exchange_wait_on_input(unsigned int ok=SW_OK, unsigned int mask=0xFFFF);
void reset_buffer(void);
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);
diff --git a/src/multisig/multisig.cpp b/src/multisig/multisig.cpp
index 14df4d554..999894db0 100644
--- a/src/multisig/multisig.cpp
+++ b/src/multisig/multisig.cpp
@@ -33,19 +33,22 @@
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "multisig.h"
+#include "cryptonote_config.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "multisig"
using namespace std;
-static const rct::key multisig_salt = { {'M', 'u', 'l', 't' , 'i', 's', 'i', 'g', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
-
namespace cryptonote
{
//-----------------------------------------------------------------
crypto::secret_key get_multisig_blinded_secret_key(const crypto::secret_key &key)
{
+ rct::key multisig_salt;
+ static_assert(sizeof(rct::key) == sizeof(config::HASH_KEY_MULTISIG), "Hash domain separator is an unexpected size");
+ memcpy(multisig_salt.bytes, config::HASH_KEY_MULTISIG, sizeof(rct::key));
+
rct::keyV data;
data.reserve(2);
data.push_back(rct::sk2rct(key));
diff --git a/src/net/i2p_address.cpp b/src/net/i2p_address.cpp
index cba829d3f..f4cc75fee 100644
--- a/src/net/i2p_address.cpp
+++ b/src/net/i2p_address.cpp
@@ -171,7 +171,8 @@ namespace net
bool i2p_address::less(const i2p_address& rhs) const noexcept
{
- return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port();
+ int res = std::strcmp(host_str(), rhs.host_str());
+ return res < 0 || (res == 0 && port() < rhs.port());
}
bool i2p_address::is_same_host(const i2p_address& rhs) const noexcept
diff --git a/src/net/tor_address.cpp b/src/net/tor_address.cpp
index 904a9a0fc..4414861e7 100644
--- a/src/net/tor_address.cpp
+++ b/src/net/tor_address.cpp
@@ -173,7 +173,8 @@ namespace net
bool tor_address::less(const tor_address& rhs) const noexcept
{
- return std::strcmp(host_str(), rhs.host_str()) < 0 || port() < rhs.port();
+ int res = std::strcmp(host_str(), rhs.host_str());
+ return res < 0 || (res == 0 && port() < rhs.port());
}
bool tor_address::is_same_host(const tor_address& rhs) const noexcept
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 9b9ffbe2a..31d8aad3f 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -301,8 +301,6 @@ namespace nodetool
bool islimitup=false;
bool islimitdown=false;
- typedef COMMAND_REQUEST_STAT_INFO_T<typename t_payload_net_handler::stat_info> COMMAND_REQUEST_STAT_INFO;
-
CHAIN_LEVIN_INVOKE_MAP2(p2p_connection_context); //move levin_commands_handler interface invoke(...) callbacks into invoke map
CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing
@@ -313,11 +311,6 @@ namespace nodetool
HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake)
HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync)
HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping)
-#ifdef ALLOW_DEBUG_COMMANDS
- HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info)
- HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state)
- HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id)
-#endif
HANDLE_INVOKE_T2(COMMAND_REQUEST_SUPPORT_FLAGS, &node_server::handle_get_support_flags)
CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&)
END_INVOKE_MAP2()
@@ -328,17 +321,11 @@ namespace nodetool
int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context);
int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context);
int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context);
-#ifdef ALLOW_DEBUG_COMMANDS
- int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context);
- int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context);
- int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context);
-#endif
int handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context);
bool init_config();
bool make_default_peer_id();
bool make_default_config();
bool store_config();
- bool check_trust(const proof_of_trust& tr, epee::net_utils::zone zone_type);
//----------------- levin_commands_handler -------------------------------------------------------------
@@ -393,7 +380,7 @@ namespace nodetool
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb);
bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f);
bool make_expected_connections_count(network_zone& zone, PeerType peer_type, size_t expected_connections);
- void cache_connect_fail_info(const epee::net_utils::network_address& addr);
+ void record_addr_failed(const epee::net_utils::network_address& addr);
bool is_addr_recently_failed(const epee::net_utils::network_address& addr);
bool is_priority_node(const epee::net_utils::network_address& na);
std::set<std::string> get_seed_nodes(cryptonote::network_type nettype) const;
@@ -414,7 +401,6 @@ namespace nodetool
bool set_rate_limit(const boost::program_options::variables_map& vm, int64_t limit);
bool has_too_many_connections(const epee::net_utils::network_address &address);
- uint64_t get_connections_count();
size_t get_incoming_connections_count();
size_t get_incoming_connections_count(network_zone&);
size_t get_outgoing_connections_count();
@@ -478,9 +464,6 @@ namespace nodetool
epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval;
epee::math_helper::once_a_time_seconds<3600, false> m_incoming_connections_interval;
-#ifdef ALLOW_DEBUG_COMMANDS
- uint64_t m_last_stat_request_time;
-#endif
std::list<epee::net_utils::network_address> m_priority_peers;
std::vector<epee::net_utils::network_address> m_exclusive_peers;
std::vector<epee::net_utils::network_address> m_seed_nodes;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 6a02d31b5..d9b46f459 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -813,7 +813,6 @@ namespace nodetool
//only in case if we really sure that we have external visible ip
m_have_address = true;
- m_last_stat_request_time = 0;
//configure self
@@ -940,15 +939,6 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- uint64_t node_server<t_payload_net_handler>::get_connections_count()
- {
- std::uint64_t count = 0;
- for (auto& zone : m_network_zones)
- count += zone.second.m_net_server.get_config_object().get_connections_count();
- return count;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::deinit()
{
kill();
@@ -1023,15 +1013,18 @@ namespace nodetool
epee::simple_event ev;
std::atomic<bool> hsh_result(false);
+ bool timeout = false;
bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_HANDSHAKE::response>(context_, COMMAND_HANDSHAKE::ID, arg, zone.m_net_server.get_config_object(),
- [this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context)
+ [this, &pi, &ev, &hsh_result, &just_take_peerlist, &context_, &timeout](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context)
{
epee::misc_utils::auto_scope_leave_caller scope_exit_handler = epee::misc_utils::create_scope_leave_handler([&](){ev.raise();});
if(code < 0)
{
LOG_WARNING_CC(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")");
+ if (code == LEVIN_ERROR_CONNECTION_TIMEDOUT || code == LEVIN_ERROR_CONNECTION_DESTROYED)
+ timeout = true;
return;
}
@@ -1060,17 +1053,15 @@ namespace nodetool
pi = context.peer_id = rsp.node_data.peer_id;
context.m_rpc_port = rsp.node_data.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);
+ network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone());
+ 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)
+ if(rsp.node_data.peer_id == zone.m_config.m_peer_id)
{
- if(rsp.node_data.peer_id == zone.second.m_config.m_peer_id)
- {
- LOG_DEBUG_CC(context, "Connection to self detected, dropping connection");
- hsh_result = false;
- return;
- }
+ LOG_DEBUG_CC(context, "Connection to self detected, dropping connection");
+ hsh_result = false;
+ return;
}
LOG_INFO_CC(context, "New connection handshaked, pruning seed " << epee::string_tools::to_string_hex(context.m_pruning_seed));
LOG_DEBUG_CC(context, " COMMAND_HANDSHAKE INVOKED OK");
@@ -1089,7 +1080,8 @@ namespace nodetool
if(!hsh_result)
{
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);
+ if (!timeout)
+ zone.m_net_server.get_config_object().close(context_.m_connection_id);
}
else if (!just_take_peerlist)
{
@@ -1262,7 +1254,7 @@ namespace nodetool
bool is_priority = is_priority_node(na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, bool(con), "Connect failed to " << na.str()
/*<< ", try " << try_count*/);
- //m_peerlist.set_peer_unreachable(pe);
+ record_addr_failed(na);
return false;
}
@@ -1276,7 +1268,7 @@ namespace nodetool
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer "
<< na.str()
/*<< ", try " << try_count*/);
- zone.m_net_server.get_config_object().close(con->m_connection_id);
+ record_addr_failed(na);
return false;
}
@@ -1327,6 +1319,7 @@ namespace nodetool
bool is_priority = is_priority_node(na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, p2p_connection_context{}, "Connect failed to " << na.str());
+ record_addr_failed(na);
return false;
}
@@ -1338,7 +1331,7 @@ namespace nodetool
bool is_priority = is_priority_node(na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str());
- zone.m_net_server.get_config_object().close(con->m_connection_id);
+ record_addr_failed(na);
return false;
}
@@ -1353,6 +1346,13 @@ namespace nodetool
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::record_addr_failed(const epee::net_utils::network_address& addr)
+ {
+ CRITICAL_REGION_LOCAL(m_conn_fails_cache_lock);
+ m_conn_fails_cache[addr.host_str()] = time(NULL);
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
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);
@@ -1434,10 +1434,10 @@ namespace nodetool
std::deque<size_t> filtered;
const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max();
- size_t idx = 0, skipped = 0;
for (int step = 0; step < 2; ++step)
{
bool skip_duplicate_class_B = step == 0;
+ size_t idx = 0, skipped = 0;
zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){
if (filtered.size() >= limit)
return false;
@@ -1543,6 +1543,7 @@ namespace nodetool
return true;
size_t try_count = 0;
+ bool is_connected_to_at_least_one_seed_node = false;
size_t current_index = crypto::rand_idx(m_seed_nodes.size());
const net_server& server = m_network_zones.at(epee::net_utils::zone::public_).m_net_server;
while(true)
@@ -1550,21 +1551,25 @@ namespace nodetool
if(server.is_stop_signal_sent())
return false;
- if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true))
+ peerlist_entry pe_seed{};
+ pe_seed.adr = m_seed_nodes[current_index];
+ if (is_peer_used(pe_seed))
+ is_connected_to_at_least_one_seed_node = true;
+ else if (try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true))
break;
if(++try_count > m_seed_nodes.size())
{
if (!m_fallback_seed_nodes_added)
{
MWARNING("Failed to connect to any of seed peers, trying fallback seeds");
- current_index = m_seed_nodes.size();
+ current_index = m_seed_nodes.size() - 1;
for (const auto &peer: get_seed_nodes(m_nettype))
{
MDEBUG("Fallback seed node: " << peer);
append_net_address(m_seed_nodes, peer, cryptonote::get_config(m_nettype).P2P_DEFAULT_PORT);
}
m_fallback_seed_nodes_added = true;
- if (current_index == m_seed_nodes.size())
+ if (current_index == m_seed_nodes.size() - 1)
{
MWARNING("No fallback seeds, continuing without seeds");
break;
@@ -1573,7 +1578,8 @@ namespace nodetool
}
else
{
- MWARNING("Failed to connect to any of seed peers, continuing without seeds");
+ if (!is_connected_to_at_least_one_seed_node)
+ MWARNING("Failed to connect to any of seed peers, continuing without seeds");
break;
}
}
@@ -1912,7 +1918,7 @@ namespace nodetool
LOG_DEBUG_CC(context, "REMOTE PEERLIST: remote peerlist size=" << peerlist_.size());
LOG_TRACE_CC(context, "REMOTE PEERLIST: " << ENDL << print_peerlist_to_string(peerlist_));
- return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_);
+ return m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.merge_peerlist(peerlist_, [this](const peerlist_entry &pe) { return !is_addr_recently_failed(pe.adr); });
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@@ -1929,91 +1935,6 @@ namespace nodetool
return true;
}
//-----------------------------------------------------------------------------------
-#ifdef ALLOW_DEBUG_COMMANDS
- template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::check_trust(const proof_of_trust& tr, const epee::net_utils::zone zone_type)
- {
- uint64_t local_time = time(NULL);
- uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time;
- if(time_delata > 24*60*60 )
- {
- MWARNING("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time);
- return false;
- }
- if(m_last_stat_request_time >= tr.time )
- {
- MWARNING("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time);
- return false;
- }
-
- const network_zone& zone = m_network_zones.at(zone_type);
- if(zone.m_config.m_peer_id != tr.peer_id)
- {
- MWARNING("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << peerid_to_string(zone.m_config.m_peer_id) << ")");
- return false;
- }
- crypto::public_key pk = AUTO_VAL_INIT(pk);
- epee::string_tools::hex_to_pod(::config::P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY, pk);
- crypto::hash h = get_proof_of_trust_hash(tr);
- if(!crypto::check_signature(h, pk, tr.sign))
- {
- MWARNING("check_trust failed: sign check failed");
- return false;
- }
- //update last request time
- m_last_stat_request_time = tr.time;
- return true;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
- int node_server<t_payload_net_handler>::handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context)
- {
- if(!check_trust(arg.tr, context.m_remote_address.get_zone()))
- {
- drop_connection(context);
- return 1;
- }
- rsp.connections_count = get_connections_count();
- rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count();
- rsp.version = MONERO_VERSION_FULL;
- rsp.os_version = tools::get_os_version_string();
- m_payload_handler.get_stat_info(rsp.payload_info);
- return 1;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
- int node_server<t_payload_net_handler>::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context)
- {
- if(!check_trust(arg.tr, context.m_remote_address.get_zone()))
- {
- drop_connection(context);
- return 1;
- }
- m_network_zones.at(epee::net_utils::zone::public_).m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
- {
- connection_entry ce;
- ce.adr = cntxt.m_remote_address;
- ce.id = cntxt.peer_id;
- ce.is_income = cntxt.m_is_income;
- rsp.connections_list.push_back(ce);
- return true;
- });
-
- network_zone& zone = m_network_zones.at(context.m_remote_address.get_zone());
- zone.m_peerlist.get_peerlist(rsp.local_peerlist_gray, rsp.local_peerlist_white);
- rsp.my_id = zone.m_config.m_peer_id;
- rsp.local_time = time(NULL);
- return 1;
- }
- //-----------------------------------------------------------------------------------
- template<class t_payload_net_handler>
- int node_server<t_payload_net_handler>::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context)
- {
- rsp.my_id = m_network_zones.at(context.m_remote_address.get_zone()).m_config.m_peer_id;
- return 1;
- }
-#endif
- //-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
int node_server<t_payload_net_handler>::handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context)
{
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index 8225ad2fa..300181bbb 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -43,6 +43,7 @@
#include <boost/range/adaptor/reversed.hpp>
+#include "crypto/crypto.h"
#include "cryptonote_config.h"
#include "net/enums.h"
#include "net/local_ip.h"
@@ -101,7 +102,7 @@ namespace nodetool
bool init(peerlist_types&& peers, bool allow_local_ip);
size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();}
size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();}
- bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs);
+ bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f = NULL);
bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
void get_peerlist(peerlist_types& peers);
@@ -112,7 +113,6 @@ namespace nodetool
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, 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);
bool remove_from_peer_gray(const peerlist_entry& pe);
@@ -213,12 +213,13 @@ namespace nodetool
}
//--------------------------------------------------------------------------------------------------
inline
- bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs)
+ bool peerlist_manager::merge_peerlist(const std::vector<peerlist_entry>& outer_bs, const std::function<bool(const peerlist_entry&)> &f)
{
CRITICAL_REGION_LOCAL(m_peerlist_lock);
for(const peerlist_entry& be: outer_bs)
{
- append_with_peer_gray(be);
+ if (!f || f(be))
+ append_with_peer_gray(be);
}
// delete extra elements
trim_gray_peerlist();
diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h
index efc8e52fd..609661871 100644
--- a/src/p2p/p2p_protocol_defs.h
+++ b/src/p2p/p2p_protocol_defs.h
@@ -40,9 +40,6 @@
#include "string_tools.h"
#include "time_helper.h"
#include "cryptonote_config.h"
-#ifdef ALLOW_DEBUG_COMMANDS
-#include "crypto/crypto.h"
-#endif
namespace nodetool
{
@@ -286,117 +283,6 @@ namespace nodetool
};
-#ifdef ALLOW_DEBUG_COMMANDS
- //These commands are considered as insecure, and made in debug purposes for a limited lifetime.
- //Anyone who feel unsafe with this commands can disable the ALLOW_GET_STAT_COMMAND macro.
-
- struct proof_of_trust
- {
- peerid_type peer_id;
- uint64_t time;
- crypto::signature sign;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(peer_id)
- KV_SERIALIZE(time)
- KV_SERIALIZE_VAL_POD_AS_BLOB(sign)
- END_KV_SERIALIZE_MAP()
- };
-
-
- template<class payload_stat_info>
- struct COMMAND_REQUEST_STAT_INFO_T
- {
- const static int ID = P2P_COMMANDS_POOL_BASE + 4;
-
- struct request_t
- {
- proof_of_trust tr;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(tr)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string version;
- std::string os_version;
- uint64_t connections_count;
- uint64_t incoming_connections_count;
- payload_stat_info payload_info;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(version)
- KV_SERIALIZE(os_version)
- KV_SERIALIZE(connections_count)
- KV_SERIALIZE(incoming_connections_count)
- KV_SERIALIZE(payload_info)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
-
- /************************************************************************/
- /* */
- /************************************************************************/
- struct COMMAND_REQUEST_NETWORK_STATE
- {
- const static int ID = P2P_COMMANDS_POOL_BASE + 5;
-
- struct request_t
- {
- proof_of_trust tr;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(tr)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::vector<peerlist_entry> local_peerlist_white;
- std::vector<peerlist_entry> local_peerlist_gray;
- std::vector<connection_entry> connections_list;
- peerid_type my_id;
- uint64_t local_time;
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_white)
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_gray)
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(connections_list)
- KV_SERIALIZE(my_id)
- KV_SERIALIZE(local_time)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
- /************************************************************************/
- /* */
- /************************************************************************/
- struct COMMAND_REQUEST_PEER_ID
- {
- const static int ID = P2P_COMMANDS_POOL_BASE + 6;
-
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- peerid_type my_id;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(my_id)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
/************************************************************************/
/* */
/************************************************************************/
@@ -421,16 +307,4 @@ namespace nodetool
};
typedef epee::misc_utils::struct_init<response_t> response;
};
-
-#endif
-
-
- inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot)
- {
- std::string s;
- s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id));
- s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time));
- return crypto::cn_fast_hash(s.data(), s.size());
- }
-
}
diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc
index 80ecc5593..2ff88c6e7 100644
--- a/src/ringct/bulletproofs.cc
+++ b/src/ringct/bulletproofs.cc
@@ -100,8 +100,8 @@ static inline bool is_reduced(const rct::key &scalar)
static rct::key get_exponent(const rct::key &base, size_t idx)
{
- static const std::string salt("bulletproof");
- std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + salt + tools::get_varint_data(idx);
+ static const std::string domain_separator(config::HASH_KEY_BULLETPROOF_EXPONENT);
+ std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + domain_separator + tools::get_varint_data(idx);
rct::key e;
ge_p3 e_p3;
rct::hash_to_p3(e_p3, rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size())));
@@ -173,7 +173,7 @@ static rct::key cross_vector_exponent8(size_t size, const std::vector<ge_p3> &A,
multiexp_data.resize(size*2 + (!!extra_point));
for (size_t i = 0; i < size; ++i)
{
- sc_mul(multiexp_data[i*2].scalar.bytes, a[ao+i].bytes, INV_EIGHT.bytes);;
+ sc_mul(multiexp_data[i*2].scalar.bytes, a[ao+i].bytes, INV_EIGHT.bytes);
multiexp_data[i*2].point = A[Ao+i];
sc_mul(multiexp_data[i*2+1].scalar.bytes, b[bo+i].bytes, INV_EIGHT.bytes);
if (scale)
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index 65d88b57e..fe5e5a85b 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -35,6 +35,7 @@ set(rpc_base_sources
set(rpc_sources
bootstrap_daemon.cpp
+ bootstrap_node_selector.cpp
core_rpc_server.cpp
rpc_payment.cpp
rpc_version_str.cpp
diff --git a/src/rpc/bootstrap_daemon.cpp b/src/rpc/bootstrap_daemon.cpp
index c97b2c95a..6a0833f19 100644
--- a/src/rpc/bootstrap_daemon.cpp
+++ b/src/rpc/bootstrap_daemon.cpp
@@ -2,6 +2,8 @@
#include <stdexcept>
+#include <boost/thread/locks.hpp>
+
#include "crypto/crypto.h"
#include "cryptonote_core/cryptonote_core.h"
#include "misc_log_ex.h"
@@ -12,15 +14,22 @@
namespace cryptonote
{
- bootstrap_daemon::bootstrap_daemon(std::function<boost::optional<std::string>()> get_next_public_node)
- : m_get_next_public_node(get_next_public_node)
+ bootstrap_daemon::bootstrap_daemon(
+ std::function<std::map<std::string, bool>()> get_public_nodes,
+ bool rpc_payment_enabled)
+ : m_selector(new bootstrap_node::selector_auto(std::move(get_public_nodes)))
+ , m_rpc_payment_enabled(rpc_payment_enabled)
{
}
- bootstrap_daemon::bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials)
- : bootstrap_daemon(nullptr)
+ bootstrap_daemon::bootstrap_daemon(
+ const std::string &address,
+ boost::optional<epee::net_utils::http::login> credentials,
+ bool rpc_payment_enabled)
+ : m_selector(nullptr)
+ , m_rpc_payment_enabled(rpc_payment_enabled)
{
- if (!set_server(address, credentials))
+ if (!set_server(address, std::move(credentials)))
{
throw std::runtime_error("invalid bootstrap daemon address or credentials");
}
@@ -54,11 +63,16 @@ namespace cryptonote
return res.height;
}
- bool bootstrap_daemon::handle_result(bool success)
+ bool bootstrap_daemon::handle_result(bool success, const std::string &status)
{
- if (!success && m_get_next_public_node)
+ const bool failed = !success || (!m_rpc_payment_enabled && status == CORE_RPC_STATUS_PAYMENT_REQUIRED);
+ if (failed && m_selector)
{
+ const std::string current_address = address();
m_http_client.disconnect();
+
+ const boost::unique_lock<boost::mutex> lock(m_selector_mutex);
+ m_selector->handle_result(current_address, !failed);
}
return success;
@@ -79,14 +93,18 @@ namespace cryptonote
bool bootstrap_daemon::switch_server_if_needed()
{
- if (!m_get_next_public_node || m_http_client.is_connected())
+ if (m_http_client.is_connected() || !m_selector)
{
return true;
}
- const boost::optional<std::string> address = m_get_next_public_node();
- if (address) {
- return set_server(*address);
+ boost::optional<bootstrap_node::node_info> node;
+ {
+ const boost::unique_lock<boost::mutex> lock(m_selector_mutex);
+ node = m_selector->next_node();
+ }
+ if (node) {
+ return set_server(node->address, node->credentials);
}
return false;
diff --git a/src/rpc/bootstrap_daemon.h b/src/rpc/bootstrap_daemon.h
index 6276b1b21..bedc255b5 100644
--- a/src/rpc/bootstrap_daemon.h
+++ b/src/rpc/bootstrap_daemon.h
@@ -1,26 +1,34 @@
#pragma once
#include <functional>
-#include <vector>
+#include <map>
#include <boost/optional/optional.hpp>
+#include <boost/thread/mutex.hpp>
#include <boost/utility/string_ref.hpp>
#include "net/http_client.h"
#include "storages/http_abstract_invoke.h"
+#include "bootstrap_node_selector.h"
+
namespace cryptonote
{
class bootstrap_daemon
{
public:
- 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);
+ bootstrap_daemon(
+ std::function<std::map<std::string, bool>()> get_public_nodes,
+ bool rpc_payment_enabled);
+ bootstrap_daemon(
+ const std::string &address,
+ boost::optional<epee::net_utils::http::login> credentials,
+ bool rpc_payment_enabled);
std::string address() const noexcept;
boost::optional<uint64_t> get_height();
- bool handle_result(bool success);
+ bool handle_result(bool success, const std::string &status);
template <class t_request, class t_response>
bool invoke_http_json(const boost::string_ref uri, const t_request &out_struct, t_response &result_struct)
@@ -30,7 +38,8 @@ namespace cryptonote
return false;
}
- return handle_result(epee::net_utils::invoke_http_json(uri, out_struct, result_struct, m_http_client));
+ const bool result = epee::net_utils::invoke_http_json(uri, out_struct, result_struct, m_http_client);
+ return handle_result(result, result_struct.status);
}
template <class t_request, class t_response>
@@ -41,7 +50,8 @@ namespace cryptonote
return false;
}
- return handle_result(epee::net_utils::invoke_http_bin(uri, out_struct, result_struct, m_http_client));
+ const bool result = epee::net_utils::invoke_http_bin(uri, out_struct, result_struct, m_http_client);
+ return handle_result(result, result_struct.status);
}
template <class t_request, class t_response>
@@ -52,7 +62,13 @@ namespace cryptonote
return false;
}
- return handle_result(epee::net_utils::invoke_http_json_rpc("/json_rpc", std::string(command_name.begin(), command_name.end()), out_struct, result_struct, m_http_client));
+ const bool result = epee::net_utils::invoke_http_json_rpc(
+ "/json_rpc",
+ std::string(command_name.begin(), command_name.end()),
+ out_struct,
+ result_struct,
+ m_http_client);
+ return handle_result(result, result_struct.status);
}
private:
@@ -61,7 +77,9 @@ namespace cryptonote
private:
epee::net_utils::http::http_simple_client m_http_client;
- std::function<boost::optional<std::string>()> m_get_next_public_node;
+ const bool m_rpc_payment_enabled;
+ const std::unique_ptr<bootstrap_node::selector> m_selector;
+ boost::mutex m_selector_mutex;
};
}
diff --git a/src/rpc/bootstrap_node_selector.cpp b/src/rpc/bootstrap_node_selector.cpp
new file mode 100644
index 000000000..34845060e
--- /dev/null
+++ b/src/rpc/bootstrap_node_selector.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2020, 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 "bootstrap_node_selector.h"
+
+#include "crypto/crypto.h"
+
+namespace cryptonote
+{
+namespace bootstrap_node
+{
+
+ void selector_auto::node::handle_result(bool success)
+ {
+ if (!success)
+ {
+ fails = std::min(std::numeric_limits<size_t>::max() - 2, fails) + 2;
+ }
+ else
+ {
+ fails = std::max(std::numeric_limits<size_t>::min() + 2, fails) - 2;
+ }
+ }
+
+ void selector_auto::handle_result(const std::string &address, bool success)
+ {
+ auto &nodes_by_address = m_nodes.get<by_address>();
+ const auto it = nodes_by_address.find(address);
+ if (it != nodes_by_address.end())
+ {
+ nodes_by_address.modify(it, [success](node &entry) {
+ entry.handle_result(success);
+ });
+ }
+ }
+
+ boost::optional<node_info> selector_auto::next_node()
+ {
+ if (!has_at_least_one_good_node())
+ {
+ append_new_nodes();
+ }
+
+ if (m_nodes.empty())
+ {
+ return {};
+ }
+
+ auto node = m_nodes.get<by_fails>().begin();
+ const size_t count = std::distance(node, m_nodes.get<by_fails>().upper_bound(node->fails));
+ std::advance(node, crypto::rand_idx(count));
+
+ return {{node->address, {}}};
+ }
+
+ bool selector_auto::has_at_least_one_good_node() const
+ {
+ return !m_nodes.empty() && m_nodes.get<by_fails>().begin()->fails == 0;
+ }
+
+ void selector_auto::append_new_nodes()
+ {
+ bool updated = false;
+
+ for (const auto &node : m_get_nodes())
+ {
+ const auto &address = node.first;
+ const auto &white = node.second;
+ const size_t initial_score = white ? 0 : 1;
+ updated |= m_nodes.get<by_address>().insert({address, initial_score}).second;
+ }
+
+ if (updated)
+ {
+ truncate();
+ }
+ }
+
+ void selector_auto::truncate()
+ {
+ const size_t total = m_nodes.size();
+ if (total > m_max_nodes)
+ {
+ auto &nodes_by_fails = m_nodes.get<by_fails>();
+ auto from = nodes_by_fails.rbegin();
+ std::advance(from, total - m_max_nodes);
+ nodes_by_fails.erase(from.base(), nodes_by_fails.end());
+ }
+ }
+
+}
+}
diff --git a/src/rpc/bootstrap_node_selector.h b/src/rpc/bootstrap_node_selector.h
new file mode 100644
index 000000000..fc993719b
--- /dev/null
+++ b/src/rpc/bootstrap_node_selector.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2020, 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 <functional>
+#include <limits>
+#include <map>
+#include <string>
+#include <utility>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/optional/optional.hpp>
+
+#include "net/http_client.h"
+
+namespace cryptonote
+{
+namespace bootstrap_node
+{
+
+ struct node_info
+ {
+ std::string address;
+ boost::optional<epee::net_utils::http::login> credentials;
+ };
+
+ struct selector
+ {
+ virtual void handle_result(const std::string &address, bool success) = 0;
+ virtual boost::optional<node_info> next_node() = 0;
+ };
+
+ class selector_auto : public selector
+ {
+ public:
+ selector_auto(std::function<std::map<std::string, bool>()> get_nodes, size_t max_nodes = 1000)
+ : m_get_nodes(std::move(get_nodes))
+ , m_max_nodes(max_nodes)
+ {}
+
+ void handle_result(const std::string &address, bool success) final;
+ boost::optional<node_info> next_node() final;
+
+ private:
+ bool has_at_least_one_good_node() const;
+ void append_new_nodes();
+ void truncate();
+
+ private:
+ struct node
+ {
+ std::string address;
+ size_t fails;
+
+ void handle_result(bool success);
+ };
+
+ struct by_address {};
+ struct by_fails {};
+
+ typedef boost::multi_index_container<
+ node,
+ boost::multi_index::indexed_by<
+ boost::multi_index::ordered_unique<boost::multi_index::tag<by_address>, boost::multi_index::member<node, std::string, &node::address>>,
+ boost::multi_index::ordered_non_unique<boost::multi_index::tag<by_fails>, boost::multi_index::member<node, size_t, &node::fails>>
+ >
+ > nodes_list;
+
+ const std::function<std::map<std::string, bool>()> m_get_nodes;
+ const size_t m_max_nodes;
+ nodes_list m_nodes;
+ };
+
+}
+}
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 5b79310c6..f097c93fa 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -153,6 +153,7 @@ namespace cryptonote
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);
+ command_line::add_arg(desc, arg_rpc_payment_allow_free_loopback);
}
//------------------------------------------------------------------------------------------------------------------------------
core_rpc_server::core_rpc_server(
@@ -163,6 +164,7 @@ namespace cryptonote
, m_p2p(p2p)
, m_was_bootstrap_ever_used(false)
, disable_rpc_ban(false)
+ , m_rpc_payment_allow_free_loopback(false)
{}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password)
@@ -176,7 +178,7 @@ namespace cryptonote
return set_bootstrap_daemon(address, credentials);
}
//------------------------------------------------------------------------------------------------------------------------------
- boost::optional<std::string> core_rpc_server::get_random_public_node()
+ std::map<std::string, bool> core_rpc_server::get_public_nodes(uint32_t credits_per_hash_threshold/* = 0*/)
{
COMMAND_RPC_GET_PUBLIC_NODES::request request;
COMMAND_RPC_GET_PUBLIC_NODES::response response;
@@ -185,47 +187,51 @@ namespace cryptonote
request.white = true;
if (!on_get_public_nodes(request, response) || response.status != CORE_RPC_STATUS_OK)
{
- return boost::none;
+ return {};
}
- const auto get_random_node_address = [](const std::vector<public_node>& public_nodes) -> std::string {
- const auto& random_node = public_nodes[crypto::rand_idx(public_nodes.size())];
- const auto address = random_node.host + ":" + std::to_string(random_node.rpc_port);
- return address;
- };
-
- if (!response.white.empty())
- {
- return get_random_node_address(response.white);
- }
-
- MDEBUG("No white public node found, checking gray peers");
+ std::map<std::string, bool> result;
- if (!response.gray.empty())
- {
- return get_random_node_address(response.gray);
- }
+ const auto append = [&result, &credits_per_hash_threshold](const std::vector<public_node> &nodes, bool white) {
+ for (const auto &node : nodes)
+ {
+ const bool rpc_payment_enabled = credits_per_hash_threshold > 0;
+ const bool node_rpc_payment_enabled = node.rpc_credits_per_hash > 0;
+ if (!node_rpc_payment_enabled ||
+ (rpc_payment_enabled && node.rpc_credits_per_hash >= credits_per_hash_threshold))
+ {
+ result.insert(std::make_pair(node.host + ":" + std::to_string(node.rpc_port), white));
+ }
+ }
+ };
- MERROR("Failed to find any suitable public node");
+ append(response.white, true);
+ append(response.gray, false);
- return boost::none;
+ return result;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials)
{
boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ constexpr const uint32_t credits_per_hash_threshold = 0;
+ constexpr const bool rpc_payment_enabled = credits_per_hash_threshold != 0;
+
if (address.empty())
{
m_bootstrap_daemon.reset(nullptr);
}
else if (address == "auto")
{
- m_bootstrap_daemon.reset(new bootstrap_daemon([this]{ return get_random_public_node(); }));
+ auto get_nodes = [this, credits_per_hash_threshold]() {
+ return get_public_nodes(credits_per_hash_threshold);
+ };
+ m_bootstrap_daemon.reset(new bootstrap_daemon(std::move(get_nodes), rpc_payment_enabled));
}
else
{
- m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials));
+ m_bootstrap_daemon.reset(new bootstrap_daemon(address, credentials, rpc_payment_enabled));
}
m_should_use_bootstrap_daemon = m_bootstrap_daemon.get() != nullptr;
@@ -280,6 +286,7 @@ namespace cryptonote
MERROR("Payments difficulty and/or payments credits are 0, but a payment address was given");
return false;
}
+ m_rpc_payment_allow_free_loopback = command_line::get_arg(vm, arg_rpc_payment_allow_free_loopback);
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));
@@ -353,7 +360,7 @@ namespace cryptonote
#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)
+#define CHECK_PAYMENT_MIN1(req, res, payment, same_ts) do { if (!ctx || (m_rpc_payment_allow_free_loopback && ctx->m_remote_address.is_loopback())) 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()
{
@@ -788,6 +795,9 @@ namespace cryptonote
CHECK_PAYMENT_MIN1(req, res, req.txs_hashes.size() * COST_PER_TX, false);
+ const bool restricted = m_restricted && ctx;
+ const bool request_has_rpc_origin = ctx != NULL;
+
std::vector<crypto::hash> vh;
for(const auto& tx_hex_str: req.txs_hashes)
{
@@ -822,7 +832,7 @@ namespace cryptonote
{
std::vector<tx_info> pool_tx_info;
std::vector<spent_key_image_info> pool_key_image_info;
- bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info);
+ bool r = m_core.get_pool_transactions_and_spent_keys_info(pool_tx_info, pool_key_image_info, !request_has_rpc_origin || !restricted);
if(r)
{
// sort to match original request
@@ -1100,7 +1110,7 @@ namespace cryptonote
return true;
}
- if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob))
+ if (req.do_sanity_checks && !cryptonote::tx_sanity_check(tx_blob, m_core.get_blockchain_storage().get_num_mature_outputs(0)))
{
res.status = "Failed";
res.reason = "Sanity check failed";
@@ -1128,8 +1138,6 @@ namespace cryptonote
add_reason(reason, "overspend");
if ((res.fee_too_low = tvc.m_fee_too_low))
add_reason(reason, "fee too low");
- if ((res.not_rct = tvc.m_not_rct))
- add_reason(reason, "tx is not ringct");
if ((res.too_few_outputs = tvc.m_too_few_outputs))
add_reason(reason, "too few outputs");
const std::string punctuation = reason.empty() ? "" : ": ";
@@ -1937,7 +1945,7 @@ namespace cryptonote
if (*bootstrap_daemon_height < target_height)
{
MINFO("Bootstrap daemon is out of sync");
- return m_bootstrap_daemon->handle_result(false);
+ return m_bootstrap_daemon->handle_result(false, {});
}
uint64_t top_height = m_core.get_current_blockchain_height();
@@ -3255,4 +3263,10 @@ namespace cryptonote
, "Restrict RPC to clients sending micropayment, yields that many credits per payment"
, DEFAULT_PAYMENT_CREDITS_PER_HASH
};
+
+ const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_payment_allow_free_loopback = {
+ "rpc-payment-allow-free-loopback"
+ , "Allow free access from the loopback address (ie, the local host)"
+ , false
+ };
} // namespace cryptonote
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 3b8e9c20a..3c404bbd8 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -75,6 +75,7 @@ namespace cryptonote
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;
+ static const command_line::arg_descriptor<bool> arg_rpc_payment_allow_free_loopback;
typedef epee::net_utils::connection_context_base connection_context;
@@ -266,7 +267,7 @@ private:
//utils
uint64_t get_block_reward(const block& blk);
bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response, bool fill_pow_hash);
- boost::optional<std::string> get_random_public_node();
+ std::map<std::string, bool> get_public_nodes(uint32_t credits_per_hash_threshold = 0);
bool set_bootstrap_daemon(const std::string &address, const std::string &username_password);
bool set_bootstrap_daemon(const std::string &address, const boost::optional<epee::net_utils::http::login> &credentials);
enum invoke_http_mode { JON, BIN, JON_RPC };
@@ -287,6 +288,7 @@ private:
std::map<std::string, uint64_t> m_host_fails_score;
std::unique_ptr<rpc_payment> m_rpc_payment;
bool disable_rpc_ban;
+ bool m_rpc_payment_allow_free_loopback;
};
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index dbb1d4472..a3c187c24 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -588,7 +588,6 @@ namespace cryptonote
bool too_big;
bool overspend;
bool fee_too_low;
- bool not_rct;
bool too_few_outputs;
bool sanity_check_failed;
@@ -603,7 +602,6 @@ namespace cryptonote
KV_SERIALIZE(too_big)
KV_SERIALIZE(overspend)
KV_SERIALIZE(fee_too_low)
- KV_SERIALIZE(not_rct)
KV_SERIALIZE(too_few_outputs)
KV_SERIALIZE(sanity_check_failed)
END_KV_SERIALIZE_MAP()
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 125688ba5..7292176b4 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -410,11 +410,6 @@ namespace rpc
if (!res.error_details.empty()) res.error_details += " and ";
res.error_details += "fee too low";
}
- if (tvc.m_not_rct)
- {
- if (!res.error_details.empty()) res.error_details += " and ";
- res.error_details += "tx is not ringct";
- }
if (tvc.m_too_few_outputs)
{
if (!res.error_details.empty()) res.error_details += " and ";
diff --git a/src/rpc/message.cpp b/src/rpc/message.cpp
index a3df7fb56..5b6a1c05b 100644
--- a/src/rpc/message.cpp
+++ b/src/rpc/message.cpp
@@ -50,6 +50,16 @@ constexpr const char id_field[] = "id";
constexpr const char method_field[] = "method";
constexpr const char params_field[] = "params";
constexpr const char result_field[] = "result";
+
+const rapidjson::Value& get_method_field(const rapidjson::Value& src)
+{
+ const auto member = src.FindMember(method_field);
+ if (member == src.MemberEnd())
+ throw cryptonote::json::MISSING_KEY{method_field};
+ if (!member->value.IsString())
+ throw cryptonote::json::WRONG_TYPE{"Expected string"};
+ return member->value;
+}
}
void Message::toJson(rapidjson::Writer<rapidjson::StringBuffer>& dest) const
@@ -81,7 +91,7 @@ FullMessage::FullMessage(const std::string& json_string, bool request)
if (request)
{
- OBJECT_HAS_MEMBER_OR_THROW(doc, method_field)
+ get_method_field(doc); // throws on errors
OBJECT_HAS_MEMBER_OR_THROW(doc, params_field)
}
else
@@ -95,8 +105,7 @@ FullMessage::FullMessage(const std::string& json_string, bool request)
std::string FullMessage::getRequestType() const
{
- OBJECT_HAS_MEMBER_OR_THROW(doc, method_field)
- return doc[method_field].GetString();
+ return get_method_field(doc).GetString();
}
const rapidjson::Value& FullMessage::getMessage() const
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index dcb804d3e..9153e76ea 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2019, The Monero Project
+// Copyright (c) 2014-2020, The Monero Project
//
// All rights reserved.
//
@@ -51,7 +51,7 @@ namespace cryptonote
const std::vector<std::string> ssl_allowed_fingerprints = command_line::get_arg(vm, arg.rpc_ssl_allowed_fingerprints);
std::vector<std::vector<uint8_t>> allowed_fingerprints{ ssl_allowed_fingerprints.size() };
- std::transform(ssl_allowed_fingerprints.begin(), ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector);
+ std::transform(ssl_allowed_fingerprints.begin(), ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex_locale::to_vector);
for (const auto &fpr: allowed_fingerprints)
{
if (fpr.size() != SSL_FINGERPRINT_SIZE)
diff --git a/src/rpc/rpc_payment.cpp b/src/rpc/rpc_payment.cpp
index b363c27b2..2b9c19f57 100644
--- a/src/rpc/rpc_payment.cpp
+++ b/src/rpc/rpc_payment.cpp
@@ -54,8 +54,6 @@
#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():
@@ -147,7 +145,7 @@ namespace cryptonote
return false;
char data[33];
memcpy(data, &client, 32);
- data[32] = RPC_PAYMENT_NONCE_TAIL;
+ data[32] = config::HASH_KEY_RPC_PAYMENT_NONCE;
crypto::hash hash;
cn_fast_hash(data, sizeof(data), hash);
extra_nonce = cryptonote::blobdata((const char*)&hash, 4);
diff --git a/src/rpc/rpc_payment_signature.cpp b/src/rpc/rpc_payment_signature.cpp
index 2e8b54b4f..559f3a1e9 100644
--- a/src/rpc/rpc_payment_signature.cpp
+++ b/src/rpc/rpc_payment_signature.cpp
@@ -102,6 +102,11 @@ namespace cryptonote
MDEBUG("Timestamp is in the future");
return false;
}
+ if (ts < now - TIMESTAMP_LEEWAY)
+ {
+ MDEBUG("Timestamp is too old");
+ return false;
+ }
return true;
}
}
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index e98ba0483..f20fd181a 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2019, The Monero Project
+// Copyright (c) 2016-2020, The Monero Project
//
// All rights reserved.
//
@@ -32,7 +32,11 @@
#include <boost/variant/apply_visitor.hpp>
#include <limits>
#include <type_traits>
-#include "string_tools.h"
+
+// drop macro from windows.h
+#ifdef GetObject
+ #undef GetObject
+#endif
namespace cryptonote
{
@@ -109,6 +113,19 @@ namespace
}
}
+void read_hex(const rapidjson::Value& val, epee::span<std::uint8_t> dest)
+{
+ if (!val.IsString())
+ {
+ throw WRONG_TYPE("string");
+ }
+
+ if (!epee::from_hex::to_buffer(dest, {val.GetString(), val.Size()}))
+ {
+ throw BAD_INPUT();
+ }
+}
+
void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src)
{
src.Accept(dest);
@@ -190,7 +207,7 @@ void fromJsonValue(const rapidjson::Value& val, int& i)
void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const unsigned long long i)
{
- static_assert(std::numeric_limits<unsigned long long>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad uint64 conversion");
+ static_assert(!precision_loss<unsigned long long, std::uint64_t>(), "bad uint64 conversion");
dest.Uint64(i);
}
@@ -201,8 +218,7 @@ void fromJsonValue(const rapidjson::Value& val, unsigned long long& i)
void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const long long i)
{
- static_assert(std::numeric_limits<std::uint64_t>::min() <= std::numeric_limits<long long>::min(), "bad int64 conversion");
- static_assert(std::numeric_limits<long long>::max() <= std::numeric_limits<std::uint64_t>::max(), "bad int64 conversion");
+ static_assert(!precision_loss<long long, std::int64_t>(), "bad int64 conversion");
dest.Int64(i);
}
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index a1a5105d5..664b539b5 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2016-2019, The Monero Project
+// Copyright (c) 2016-2020, The Monero Project
//
// All rights reserved.
//
@@ -30,7 +30,6 @@
#include <boost/utility/string_ref.hpp>
#include <cstring>
-#include "string_tools.h" // out of order because windows.h GetObject macro conflicts with GenericValue<..>::GetObject()
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
@@ -39,6 +38,8 @@
#include "rpc/message_data_structs.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "common/sfinae_helpers.h"
+#include "hex.h"
+#include "span.h"
#define OBJECT_HAS_MEMBER_OR_THROW(val, key) \
do \
@@ -118,6 +119,8 @@ inline constexpr bool is_to_hex()
return std::is_pod<Type>() && !std::is_integral<Type>();
}
+void read_hex(const rapidjson::Value& val, epee::span<std::uint8_t> dest);
+
// POD to json key
template <class Type>
inline typename std::enable_if<is_to_hex<Type>()>::type toJsonKey(rapidjson::Writer<rapidjson::StringBuffer>& dest, const Type& pod)
@@ -137,18 +140,8 @@ inline typename std::enable_if<is_to_hex<Type>()>::type toJsonValue(rapidjson::W
template <class Type>
inline typename std::enable_if<is_to_hex<Type>()>::type fromJsonValue(const rapidjson::Value& val, Type& t)
{
- if (!val.IsString())
- {
- throw WRONG_TYPE("string");
- }
-
- //TODO: handle failure to convert hex string to POD type
- bool success = epee::string_tools::hex_to_pod(val.GetString(), t);
-
- if (!success)
- {
- throw BAD_INPUT();
- }
+ static_assert(std::is_standard_layout<Type>(), "expected standard layout type");
+ json::read_hex(val, epee::as_mut_byte_span(t));
}
void toJsonValue(rapidjson::Writer<rapidjson::StringBuffer>& dest, const rapidjson::Value& src);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index bc6866c28..d45ef3d7c 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -108,7 +108,7 @@ typedef cryptonote::simple_wallet sw;
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_suspend_rpc_payment_mining.store(false, std::memory_order_relaxed); \
m_rpc_payment_checker.trigger(); \
m_idle_cond.notify_one(); \
})
@@ -1691,7 +1691,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args)
rings.push_back({key_image, ring});
else if (!m_wallet->get_rings(txid, rings))
{
- fail_msg_writer() << tr("Key image either not spent, or spent with mixin 0");
+ fail_msg_writer() << tr("Key image either not spent, or spent with ring size 1");
return true;
}
@@ -1963,7 +1963,7 @@ bool simple_wallet::rpc_payment_info(const std::vector<std::string> &args)
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");;
+ 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)
@@ -5199,8 +5199,11 @@ void simple_wallet::check_background_mining(const epee::wipeable_string &passwor
if (is_background_mining_enabled)
{
// already active, nice
- m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes);
- m_wallet->rewrite(m_wallet_file, password);
+ if (setup == tools::wallet2::BackgroundMiningMaybe)
+ {
+ m_wallet->setup_background_mining(tools::wallet2::BackgroundMiningYes);
+ m_wallet->rewrite(m_wallet_file, password);
+ }
start_background_mining();
return;
}
@@ -5223,6 +5226,11 @@ void simple_wallet::check_background_mining(const epee::wipeable_string &passwor
m_wallet->rewrite(m_wallet_file, password);
start_background_mining();
}
+ else
+ {
+ // the setting is already enabled, and the daemon is not mining yet, so start it
+ start_background_mining();
+ }
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::start_mining(const std::vector<std::string>& args)
@@ -6064,11 +6072,14 @@ std::pair<std::string, std::string> simple_wallet::show_outputs_line(const std::
return std::make_pair(ostr.str(), ring_str);
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr)
+bool simple_wallet::process_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr, bool verbose)
{
uint32_t version;
if (!try_connect_to_daemon(false, &version))
+ {
+ fail_msg_writer() << tr("failed to connect to daemon");
return false;
+ }
// available for RPC version 1.4 or higher
if (version < MAKE_CORE_RPC_VERSION(1, 4))
return true;
@@ -6084,7 +6095,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
{
const cryptonote::transaction& tx = ptx_vector[n].tx;
const tools::wallet2::tx_construction_data& construction_data = ptx_vector[n].construction_data;
- ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx);
+ if (verbose)
+ ostr << boost::format(tr("\nTransaction %llu/%llu: txid=%s")) % (n + 1) % ptx_vector.size() % cryptonote::get_transaction_hash(tx);
// for each input
std::vector<uint64_t> spent_key_height(tx.vin.size());
std::vector<crypto::hash> spent_key_txid (tx.vin.size());
@@ -6105,7 +6117,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
}
const cryptonote::tx_source_entry& source = *sptr;
- ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount);
+ if (verbose)
+ ostr << boost::format(tr("\nInput %llu/%llu (%s): amount=%s")) % (i + 1) % tx.vin.size() % epee::string_tools::pod_to_hex(in_key.k_image) % print_money(source.amount);
// convert relative offsets of ring member keys into absolute offsets (indices) associated with the amount
std::vector<uint64_t> absolute_offsets = cryptonote::relative_output_offsets_to_absolute(in_key.key_offsets);
// get block heights from which those ring member keys originated
@@ -6135,7 +6148,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
return false;
}
}
- ostr << tr("\nOriginating block heights: ");
+ if (verbose)
+ ostr << tr("\nOriginating block heights: ");
spent_key_height[i] = res.outs[source.real_output].height;
spent_key_txid [i] = res.outs[source.real_output].txid;
std::vector<uint64_t> heights(absolute_offsets.size(), 0);
@@ -6144,7 +6158,8 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
heights[j] = res.outs[j].height;
}
std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, source.real_output);
- ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
+ if (verbose)
+ ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
}
// warn if rings contain keys originating from the same tx or temporally very close block heights
bool are_keys_from_same_tx = false;
@@ -6163,7 +6178,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
ostr
<< tr("\nWarning: Some input keys being spent are from ")
<< (are_keys_from_same_tx ? tr("the same transaction") : tr("blocks that are temporally very close"))
- << tr(", which can break the anonymity of ring signature. Make sure this is intentional!");
+ << tr(", which can break the anonymity of ring signatures. Make sure this is intentional!");
}
ostr << ENDL;
}
@@ -6604,11 +6619,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
float days = locked_blocks / 720.0f;
prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days;
}
- if (m_wallet->print_ring_members())
- {
- if (!print_ring_members(ptx_vector, prompt))
- return false;
- }
+ if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
+ return false;
bool default_ring_size = true;
for (const auto &ptx: ptx_vector)
{
@@ -7064,7 +7076,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
if (subaddr_indices.size() > 1)
prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
}
- if (m_wallet->print_ring_members() && !print_ring_members(ptx_vector, prompt))
+ if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
return true;
if (ptx_vector.size() > 1) {
prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
@@ -7308,7 +7320,7 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
uint64_t total_fee = ptx_vector[0].fee;
uint64_t total_sent = m_wallet->get_transfer_details(ptx_vector[0].selected_transfers.front()).amount();
std::ostringstream prompt;
- if (!print_ring_members(ptx_vector, prompt))
+ if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
return true;
prompt << boost::format(tr("Sweeping %s for a total fee of %s. Is this okay?")) %
print_money(total_sent) %
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index c0416ecbb..4ba2793e0 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -270,7 +270,7 @@ namespace cryptonote
bool accept_loaded_tx(const std::function<size_t()> get_num_txes, const std::function<const tools::wallet2::tx_construction_data&(size_t)> &get_tx, const std::string &extra_message = std::string());
bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs);
bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs);
- bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr);
+ bool process_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr, bool verbose);
std::string get_prompt() const;
bool print_seed(bool encrypted);
void key_images_sync_intern();
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 6200c7a1f..4612b0397 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -725,7 +725,7 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed)
return recover(path, "", seed);
}
-bool WalletImpl::recover(const std::string &path, const std::string &password, const std::string &seed)
+bool WalletImpl::recover(const std::string &path, const std::string &password, const std::string &seed, const std::string &seed_offset/* = {}*/)
{
clearStatus();
m_errorString.clear();
@@ -743,6 +743,10 @@ bool WalletImpl::recover(const std::string &path, const std::string &password, c
setStatusError(tr("Electrum-style word list failed verification"));
return false;
}
+ if (!seed_offset.empty())
+ {
+ recovery_key = cryptonote::decrypt_key(recovery_key, seed_offset);
+ }
if (old_language == crypto::ElectrumWords::old_language_name)
old_language = Language::English().get_language_name();
@@ -1671,6 +1675,26 @@ void WalletImpl::disposeTransaction(PendingTransaction *t)
delete t;
}
+uint64_t WalletImpl::estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
+ PendingTransaction::Priority priority) const
+{
+ const size_t pubkey_size = 33;
+ const size_t encrypted_paymentid_size = 11;
+ const size_t extra_size = pubkey_size + encrypted_paymentid_size;
+
+ return m_wallet->estimate_fee(
+ m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0),
+ m_wallet->use_fork_rules(4, 0),
+ 1,
+ m_wallet->get_min_ring_size() - 1,
+ destinations.size() + 1,
+ extra_size,
+ m_wallet->use_fork_rules(8, 0),
+ m_wallet->get_base_fee(),
+ m_wallet->get_fee_multiplier(m_wallet->adjust_priority(static_cast<uint32_t>(priority))),
+ m_wallet->get_fee_quantization_mask());
+}
+
TransactionHistory *WalletImpl::history()
{
return m_history.get();
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 331bf4b38..66eeb0e73 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -60,7 +60,7 @@ public:
const std::string &language) const override;
bool open(const std::string &path, const std::string &password);
bool recover(const std::string &path,const std::string &password,
- const std::string &seed);
+ const std::string &seed, const std::string &seed_offset = {});
bool recoverFromKeysWithPassword(const std::string &path,
const std::string &password,
const std::string &language,
@@ -166,6 +166,8 @@ public:
bool importKeyImages(const std::string &filename) override;
virtual void disposeTransaction(PendingTransaction * t) override;
+ virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
+ PendingTransaction::Priority priority) const override;
virtual TransactionHistory * history() override;
virtual AddressBook * addressBook() override;
virtual Subaddress * subaddress() override;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index e543a115b..09c64106e 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -879,6 +879,14 @@ struct Wallet
*/
virtual void disposeTransaction(PendingTransaction * t) = 0;
+ /*!
+ * \brief Estimates transaction fee.
+ * \param destinations Vector consisting of <address, amount> pairs.
+ * \return Estimated fee.
+ */
+ virtual uint64_t estimateTransactionFee(const std::vector<std::pair<std::string, uint64_t>> &destinations,
+ PendingTransaction::Priority priority) const = 0;
+
/*!
* \brief exportKeyImages - exports key images to file
* \param filename
@@ -1085,10 +1093,12 @@ struct WalletManager
* \param nettype Network type
* \param restoreHeight restore from start height
* \param kdf_rounds Number of rounds for key derivation function
+ * \param seed_offset Seed offset passphrase (optional)
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/
virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
- NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1) = 0;
+ NetworkType nettype = MAINNET, uint64_t restoreHeight = 0, uint64_t kdf_rounds = 1,
+ const std::string &seed_offset = {}) = 0;
Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
bool testnet = false, uint64_t restoreHeight = 0) // deprecated
{
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index d589dcc75..44a184304 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -93,13 +93,14 @@ Wallet *WalletManagerImpl::recoveryWallet(const std::string &path,
const std::string &mnemonic,
NetworkType nettype,
uint64_t restoreHeight,
- uint64_t kdf_rounds)
+ uint64_t kdf_rounds,
+ const std::string &seed_offset/* = {}*/)
{
WalletImpl * wallet = new WalletImpl(nettype, kdf_rounds);
if(restoreHeight > 0){
wallet->setRefreshFromBlockHeight(restoreHeight);
}
- wallet->recover(path, password, mnemonic);
+ wallet->recover(path, password, mnemonic, seed_offset);
return wallet;
}
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 537fc5ba6..0595b8327 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -46,7 +46,8 @@ public:
const std::string &mnemonic,
NetworkType nettype,
uint64_t restoreHeight,
- uint64_t kdf_rounds = 1) override;
+ uint64_t kdf_rounds = 1,
+ const std::string &seed_offset = {}) override;
virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &password,
const std::string &language,
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index b7efdd75c..dfeb987ca 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -35,10 +35,13 @@
#include "misc_language.h"
#include "wallet_errors.h"
#include "ringdb.h"
+#include "cryptonote_config.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb"
+#define V1TAG ((uint64_t)798237759845202)
+
static const char zerokey[8] = {0};
static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey };
@@ -63,15 +66,16 @@ static int compare_uint64(const MDB_val *a, const MDB_val *b)
return va < vb ? -1 : va > vb;
}
-static std::string compress_ring(const std::vector<uint64_t> &ring)
+static std::string compress_ring(const std::vector<uint64_t> &ring, uint64_t tag)
{
std::string s;
+ s += tools::get_varint_data(tag);
for (uint64_t out: ring)
s += tools::get_varint_data(out);
return s;
}
-static std::vector<uint64_t> decompress_ring(const std::string &s)
+static std::vector<uint64_t> decompress_ring(const std::string &s, uint64_t tag)
{
std::vector<uint64_t> ring;
int read = 0;
@@ -81,6 +85,13 @@ static std::vector<uint64_t> decompress_ring(const std::string &s)
std::string tmp(i, s.cend());
read = tools::read_varint(tmp.begin(), tmp.end(), out);
THROW_WALLET_EXCEPTION_IF(read <= 0 || read > 256, tools::error::wallet_internal_error, "Internal error decompressing ring");
+ if (tag)
+ {
+ if (tag != out)
+ return {};
+ tag = 0;
+ continue;
+ }
ring.push_back(out);
}
return ring;
@@ -93,25 +104,25 @@ std::string get_rings_filename(boost::filesystem::path filename)
return filename.string();
}
-static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key)
+static crypto::chacha_iv make_iv(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- static const char salt[] = "ringdsb";
-
- uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(salt)];
+ uint8_t buffer[sizeof(key_image) + sizeof(key) + sizeof(config::HASH_KEY_RINGDB) + sizeof(field)];
memcpy(buffer, &key_image, sizeof(key_image));
memcpy(buffer + sizeof(key_image), &key, sizeof(key));
- memcpy(buffer + sizeof(key_image) + sizeof(key), salt, sizeof(salt));
+ memcpy(buffer + sizeof(key_image) + sizeof(key), config::HASH_KEY_RINGDB, sizeof(config::HASH_KEY_RINGDB));
+ memcpy(buffer + sizeof(key_image) + sizeof(key) + sizeof(config::HASH_KEY_RINGDB), &field, sizeof(field));
crypto::hash hash;
- crypto::cn_fast_hash(buffer, sizeof(buffer), hash.data);
+ // if field is 0, backward compat mode: hash without the field
+ crypto::cn_fast_hash(buffer, sizeof(buffer) - !field, hash.data);
static_assert(sizeof(hash) >= CHACHA_IV_SIZE, "Incompatible hash and chacha IV sizes");
crypto::chacha_iv iv;
memcpy(&iv, &hash, CHACHA_IV_SIZE);
return iv;
}
-static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string encrypt(const std::string &plaintext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- const crypto::chacha_iv iv = make_iv(key_image, key);
+ const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string ciphertext;
ciphertext.resize(plaintext.size() + sizeof(iv));
crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]);
@@ -119,14 +130,14 @@ static std::string encrypt(const std::string &plaintext, const crypto::key_image
return ciphertext;
}
-static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string encrypt(const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key);
+ return encrypt(std::string((const char*)&key_image, sizeof(key_image)), key_image, key, field);
}
-static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key)
+static std::string decrypt(const std::string &ciphertext, const crypto::key_image &key_image, const crypto::chacha_key &key, uint8_t field)
{
- const crypto::chacha_iv iv = make_iv(key_image, key);
+ const crypto::chacha_iv iv = make_iv(key_image, key, field);
std::string plaintext;
THROW_WALLET_EXCEPTION_IF(ciphertext.size() < sizeof(iv), tools::error::wallet_internal_error, "Bad ciphertext text");
plaintext.resize(ciphertext.size() - sizeof(iv));
@@ -137,11 +148,11 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag
static void store_relative_ring(MDB_txn *txn, MDB_dbi &dbi, const crypto::key_image &key_image, const std::vector<uint64_t> &relative_ring, const crypto::chacha_key &chacha_key)
{
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
- std::string compressed_ring = compress_ring(relative_ring);
- std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key);
+ std::string compressed_ring = compress_ring(relative_ring, V1TAG);
+ std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key, 1);
data.mv_size = data_ciphertext.size();
data.mv_data = (void*)data_ciphertext.c_str();
int dbr = mdb_put(txn, dbi, &key, &data, 0);
@@ -297,7 +308,7 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const std::vecto
for (const crypto::key_image &key_image: key_images)
{
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
@@ -349,7 +360,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
tx_active = true;
MDB_val key, data;
- std::string key_ciphertext = encrypt(key_image, chacha_key);
+ std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size();
dbr = mdb_get(txn, dbi_rings, &key, &data);
@@ -358,8 +369,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return false;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size");
- std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key);
- outs = decompress_ring(data_plaintext);
+ bool try_v0 = false;
+ std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1);
+ try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; }
+ catch(...) { try_v0 = true; }
+ if (try_v0)
+ {
+ data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 0);
+ outs = decompress_ring(data_plaintext, 0);
+ }
MDEBUG("Found ring for key image " << key_image << ":");
MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
outs = cryptonote::relative_output_offsets_to_absolute(outs);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index e16c4ba62..bc8219c69 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -44,6 +44,7 @@
using namespace epee;
#include "cryptonote_config.h"
+#include "cryptonote_core/tx_sanity_check.h"
#include "wallet_rpc_helpers.h"
#include "wallet2.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
@@ -101,10 +102,6 @@ using namespace cryptonote;
// used to target a given block weight (additional outputs may be added on top to build fee)
#define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
-// arbitrary, used to generate different hashes from the same input
-#define CHACHA8_KEY_TAIL 0x8c
-#define CACHE_KEY_TAIL 0x8d
-
#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004"
#define SIGNED_TX_PREFIX "Monero signed tx set\004"
#define MULTISIG_UNSIGNED_TX_PREFIX "Monero multisig unsigned tx set\001"
@@ -149,6 +146,9 @@ static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
static const std::string ASCII_OUTPUT_MAGIC = "MoneroAsciiDataV1";
+boost::mutex tools::wallet2::default_daemon_address_lock;
+std::string tools::wallet2::default_daemon_address = "";
+
namespace
{
std::string get_default_ringdb_path()
@@ -236,8 +236,6 @@ namespace
add_reason(reason, "overspend");
if (res.fee_too_low)
add_reason(reason, "fee too low");
- if (res.not_rct)
- add_reason(reason, "tx is not ringct");
if (res.sanity_check_failed)
add_reason(reason, "tx sanity check failed");
if (res.not_relayed)
@@ -357,7 +355,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
else if (!daemon_ssl_ca_file.empty() || !daemon_ssl_allowed_fingerprints.empty())
{
std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
- std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
+ std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex_locale::to_vector);
for (const auto &fpr: ssl_allowed_fingerprints)
{
THROW_WALLET_EXCEPTION_IF(fpr.size() != SSL_FINGERPRINT_SIZE, tools::error::wallet_internal_error,
@@ -412,6 +410,15 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
daemon_port = get_config(nettype).RPC_DEFAULT_PORT;
}
+ // if no daemon settings are given and we have a previous one, reuse that one
+ if (command_line::is_arg_defaulted(vm, opts.daemon_host) && command_line::is_arg_defaulted(vm, opts.daemon_port) && command_line::is_arg_defaulted(vm, opts.daemon_address))
+ {
+ // not a bug: taking a const ref to a temporary in this way is actually ok in a recent C++ standard
+ const std::string &def = tools::wallet2::get_default_daemon_address();
+ if (!def.empty())
+ daemon_address = def;
+ }
+
if (daemon_address.empty())
daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port);
@@ -428,6 +435,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
verification_required && !ssl_options.has_strong_verification(real_daemon),
tools::error::wallet_internal_error,
tools::wallet2::tr("Enabling --") + std::string{use_proxy ? opts.proxy.name : opts.daemon_ssl.name} + tools::wallet2::tr(" requires --") +
+ opts.daemon_ssl_allow_any_cert.name + tools::wallet2::tr(" or --") +
opts.daemon_ssl_ca_certificates.name + tools::wallet2::tr(" or --") + opts.daemon_ssl_allowed_fingerprints.name + tools::wallet2::tr(" or use of a .onion/.i2p domain")
);
}
@@ -591,6 +599,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
}
viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
crypto::public_key pkey;
+ if (viewkey == crypto::null_skey)
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("view secret key may not be all zeroes"));
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify view key secret key"));
}
@@ -607,6 +617,8 @@ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> generate_f
}
spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
crypto::public_key pkey;
+ if (spendkey == crypto::null_skey)
+ THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("spend secret key may not be all zeroes"));
if (!crypto::secret_key_to_public_key(spendkey, pkey)) {
THROW_WALLET_EXCEPTION(tools::error::wallet_internal_error, tools::wallet2::tr("failed to verify spend key secret key"));
}
@@ -879,20 +891,6 @@ uint8_t get_bulletproof_fork()
return 8;
}
-uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
-{
- if (use_per_byte_fee)
- {
- const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
- return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
- }
- else
- {
- const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
- return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
- }
-}
-
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
if (use_per_byte_fee)
@@ -1313,8 +1311,15 @@ bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_u
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));
+ const std::string address = get_daemon_address();
+ MINFO("setting daemon to " << address);
+ bool ret = m_http_client.set_server(address, get_daemon_login(), std::move(ssl_options));
+ if (ret)
+ {
+ CRITICAL_REGION_LOCAL(default_daemon_address_lock);
+ default_daemon_address = address;
+ }
+ return ret;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, boost::asio::ip::tcp::endpoint proxy, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
@@ -2942,7 +2947,6 @@ void wallet2::update_pool_state(std::vector<std::tuple<cryptonote::transaction,
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
// the inputs aren't spent anymore, since the tx failed
- remove_rings(pit->second.m_tx);
for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
{
if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
@@ -3931,7 +3935,7 @@ void wallet2::setup_keys(const epee::wipeable_string &password)
static_assert(HASH_SIZE == sizeof(crypto::chacha_key), "Mismatched sizes of hash and chacha key");
epee::mlocked<tools::scrubbed_arr<char, HASH_SIZE+1>> cache_key_data;
memcpy(cache_key_data.data(), &key, HASH_SIZE);
- cache_key_data[HASH_SIZE] = CACHE_KEY_TAIL;
+ cache_key_data[HASH_SIZE] = config::HASH_KEY_WALLET_CACHE;
cn_fast_hash(cache_key_data.data(), HASH_SIZE+1, (crypto::hash&)m_cache_key);
get_ringdb_key();
}
@@ -4096,9 +4100,18 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_always_confirm_transfers = field_always_confirm_transfers;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, print_ring_members, int, Int, false, true);
m_print_ring_members = field_print_ring_members;
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true);
- GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true);
- m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0));
+ if (json.HasMember("store_tx_info"))
+ {
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, true, true);
+ m_store_tx_info = field_store_tx_info;
+ }
+ else if (json.HasMember("store_tx_keys")) // backward compatibility
+ {
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, true, true);
+ m_store_tx_info = field_store_tx_keys;
+ }
+ else
+ m_store_tx_info = true;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0);
m_default_mixin = field_default_mixin;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0);
@@ -7142,6 +7155,20 @@ 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::estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const
+{
+ if (use_per_byte_fee)
+ {
+ const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
+ }
+ else
+ {
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
+ return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
+ }
+}
+
uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm)
{
static const struct
@@ -7783,8 +7810,50 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
}
}
+std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs)
+{
+ std::set<uint64_t> unique;
+ size_t total = 0;
+
+ for (const auto &it : outs)
+ {
+ for (const auto &out : it)
+ {
+ const uint64_t global_index = std::get<0>(out);
+ unique.insert(global_index);
+ }
+ total += it.size();
+ }
+
+ return std::make_pair(std::move(unique), total);
+}
+
void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count)
{
+ std::vector<uint64_t> rct_offsets;
+ for (size_t attempts = 3; attempts > 0; --attempts)
+ {
+ get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets);
+
+ const auto unique = outs_unique(outs);
+ if (tx_sanity_check(unique.first, unique.second, rct_offsets.empty() ? 0 : rct_offsets.back()))
+ {
+ return;
+ }
+
+ std::vector<crypto::key_image> key_images;
+ key_images.reserve(selected_transfers.size());
+ std::for_each(selected_transfers.begin(), selected_transfers.end(), [this, &key_images](size_t index) {
+ key_images.push_back(m_transfers[index].m_key_image);
+ });
+ unset_ring(key_images);
+ }
+
+ THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed"));
+}
+
+void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets)
+{
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
outs.clear();
@@ -7805,7 +7874,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// if we have at least one rct out, get the distribution, or fall back to the previous system
uint64_t rct_start_height;
- std::vector<uint64_t> rct_offsets;
bool has_rct = false;
uint64_t max_rct_index = 0;
for (size_t idx: selected_transfers)
@@ -7814,7 +7882,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
has_rct = true;
max_rct_index = std::max(max_rct_index, m_transfers[idx].m_global_output_index);
}
- const bool has_rct_distribution = has_rct && get_rct_distribution(rct_start_height, rct_offsets);
+ const bool has_rct_distribution = has_rct && (!rct_offsets.empty() || get_rct_distribution(rct_start_height, rct_offsets));
if (has_rct_distribution)
{
// check we're clear enough of rct start, to avoid corner cases below
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 707b35e13..7620d09d8 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1247,6 +1247,7 @@ private:
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 estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) const;
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1);
uint64_t get_base_fee();
uint64_t get_fee_quantization_mask();
@@ -1390,6 +1391,8 @@ private:
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; }
+ static std::string get_default_daemon_address() { CRITICAL_REGION_LOCAL(default_daemon_address_lock); return default_daemon_address; }
+
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1441,6 +1444,7 @@ private:
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count);
+ void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets);
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
@@ -1629,6 +1633,9 @@ private:
std::unique_ptr<wallet_device_callback> m_device_callback;
ExportFormat m_export_format;
+
+ static boost::mutex default_daemon_address_lock;
+ static std::string default_daemon_address;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 29)