aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/blockchain_db/blockchain_db.h65
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp157
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h13
-rw-r--r--src/blockchain_db/testdb.h7
-rw-r--r--src/blockchain_utilities/blockchain_blackball.cpp1
-rw-r--r--src/blockchain_utilities/blockchain_export.cpp2
-rw-r--r--src/common/util.cpp17
-rw-r--r--src/common/util.h2
-rw-r--r--src/crypto/CryptonightR_JIT.c6
-rw-r--r--src/crypto/slow-hash.c2
-rw-r--r--src/cryptonote_basic/miner.cpp2
-rw-r--r--src/cryptonote_basic/verification_context.h1
-rw-r--r--src/cryptonote_config.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp261
-rw-r--r--src/cryptonote_core/blockchain.h48
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp15
-rw-r--r--src/cryptonote_core/cryptonote_core.h1
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp2
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h15
-rw-r--r--src/cryptonote_core/tx_sanity_check.cpp6
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h10
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl19
-rw-r--r--src/daemon/command_line_args.h5
-rw-r--r--src/daemon/command_parser_executor.cpp76
-rw-r--r--src/daemon/command_parser_executor.h6
-rw-r--r--src/daemon/command_server.cpp18
-rw-r--r--src/daemon/daemon.cpp35
-rw-r--r--src/daemon/daemon.h1
-rw-r--r--src/daemon/main.cpp1
-rw-r--r--src/daemon/rpc_command_executor.cpp268
-rw-r--r--src/daemon/rpc_command_executor.h16
-rw-r--r--src/debug_utilities/CMakeLists.txt22
-rw-r--r--src/debug_utilities/dns_checks.cpp149
-rw-r--r--src/device/device.hpp1
-rw-r--r--src/device/device_cold.hpp1
-rw-r--r--src/device_trezor/device_trezor.cpp16
-rw-r--r--src/device_trezor/device_trezor.hpp4
-rw-r--r--src/device_trezor/trezor/protocol.cpp3
-rw-r--r--src/net/error.h3
-rw-r--r--src/net/parse.cpp82
-rw-r--r--src/net/parse.h15
-rw-r--r--src/p2p/net_node.cpp24
-rw-r--r--src/p2p/net_node.h59
-rw-r--r--src/p2p/net_node.inl388
-rw-r--r--src/p2p/net_node_common.h11
-rw-r--r--src/p2p/net_peerlist.h5
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h31
-rw-r--r--src/ringct/multiexp.cc1
-rw-r--r--src/ringct/rctTypes.cpp1
-rw-r--r--src/rpc/CMakeLists.txt2
-rw-r--r--src/rpc/core_rpc_server.cpp382
-rw-r--r--src/rpc/core_rpc_server.h15
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h177
-rw-r--r--src/rpc/daemon_handler.cpp5
-rw-r--r--src/rpc/rpc_args.cpp37
-rw-r--r--src/rpc/rpc_args.h6
-rw-r--r--src/rpc/rpc_handler.cpp4
-rw-r--r--src/rpc/zmq_server.cpp2
-rw-r--r--src/serialization/json_object.cpp2
-rw-r--r--src/simplewallet/simplewallet.cpp195
-rw-r--r--src/simplewallet/simplewallet.h2
-rw-r--r--src/wallet/api/transaction_history.cpp2
-rw-r--r--src/wallet/api/wallet.cpp157
-rw-r--r--src/wallet/api/wallet.h10
-rw-r--r--src/wallet/api/wallet2_api.h36
-rw-r--r--src/wallet/api/wallet_manager.cpp3
-rw-r--r--src/wallet/api/wallet_manager.h1
-rw-r--r--src/wallet/wallet2.cpp93
-rw-r--r--src/wallet/wallet2.h7
-rw-r--r--src/wallet/wallet_args.cpp5
-rw-r--r--src/wallet/wallet_rpc_server.cpp22
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h1
73 files changed, 2370 insertions, 695 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index da6d76d97..8a21763c8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -133,7 +133,7 @@ if(NOT IOS)
add_subdirectory(blockchain_utilities)
endif()
-if(CMAKE_BUILD_TYPE STREQUAL Debug)
+if(BUILD_DEBUG_UTILITIES)
add_subdirectory(debug_utilities)
endif()
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index b6b8c6c3e..bb4de3ce6 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -129,6 +129,15 @@ struct tx_data_t
};
#pragma pack(pop)
+struct alt_block_data_t
+{
+ uint64_t height;
+ uint64_t cumulative_weight;
+ uint64_t cumulative_difficulty_low;
+ uint64_t cumulative_difficulty_high;
+ uint64_t already_generated_coins;
+};
+
/**
* @brief a struct containing txpool per transaction metadata
*/
@@ -1543,8 +1552,45 @@ public:
*
* @param: sz the block size
*/
-
virtual void add_max_block_size(uint64_t sz) = 0;
+
+ /**
+ * @brief add a new alternative block
+ *
+ * @param: blkid the block hash
+ * @param: data: the metadata for the block
+ * @param: blob: the block's blob
+ */
+ virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) = 0;
+
+ /**
+ * @brief get an alternative block by hash
+ *
+ * @param: blkid the block hash
+ * @param: data: the metadata for the block
+ * @param: blob: the block's blob
+ *
+ * @return true if the block was found in the alternative blocks list, false otherwise
+ */
+ virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) = 0;
+
+ /**
+ * @brief remove an alternative block
+ *
+ * @param: blkid the block hash
+ */
+ virtual void remove_alt_block(const crypto::hash &blkid) = 0;
+
+ /**
+ * @brief get the number of alternative blocks stored
+ */
+ virtual uint64_t get_alt_block_count() = 0;
+
+ /**
+ * @brief drop all alternative blocks
+ */
+ virtual void drop_alt_blocks() = 0;
+
/**
* @brief runs a function over all txpool transactions
*
@@ -1634,6 +1680,23 @@ public:
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const = 0;
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const = 0;
+ /**
+ * @brief runs a function over all alternative blocks stored
+ *
+ * The subclass should run the passed function for each alt block it has
+ * stored, passing (blkid, data, blob) as its parameters.
+ *
+ * If any call to the function returns false, the subclass should return
+ * false. Otherwise, the subclass returns true.
+ *
+ * The subclass should throw DB_ERROR if any of the expected values are
+ * not found. Current implementations simply return false.
+ *
+ * @param std::function f the function to run
+ *
+ * @return false if the function returns false for any output, otherwise true
+ */
+ virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const = 0;
//
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index d0fd787db..78db37b5a 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -194,6 +194,8 @@ namespace
* txpool_meta txn hash txn metadata
* txpool_blob txn hash txn blob
*
+ * alt_blocks block hash {block data, block blob}
+ *
* Note: where the data items are of uniform size, DUPFIXED tables have
* been used to save space. In most of these cases, a dummy "zerokval"
* key is used when accessing the table; the Key listed above will be
@@ -221,6 +223,8 @@ const char* const LMDB_SPENT_KEYS = "spent_keys";
const char* const LMDB_TXPOOL_META = "txpool_meta";
const char* const LMDB_TXPOOL_BLOB = "txpool_blob";
+const char* const LMDB_ALT_BLOCKS = "alt_blocks";
+
const char* const LMDB_HF_STARTING_HEIGHTS = "hf_starting_heights";
const char* const LMDB_HF_VERSIONS = "hf_versions";
@@ -707,7 +711,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
++num_blocks_used;
}
if (my_rtxn) block_rtxn_stop();
- avg_block_size = total_block_size / num_blocks_used;
+ avg_block_size = total_block_size / (num_blocks_used ? num_blocks_used : 1);
MDEBUG("average block size across recent " << num_blocks_used << " blocks: " << avg_block_size);
}
estim:
@@ -1400,6 +1404,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
lmdb_db_open(txn, LMDB_TXPOOL_META, MDB_CREATE, m_txpool_meta, "Failed to open db handle for m_txpool_meta");
lmdb_db_open(txn, LMDB_TXPOOL_BLOB, MDB_CREATE, m_txpool_blob, "Failed to open db handle for m_txpool_blob");
+ lmdb_db_open(txn, LMDB_ALT_BLOCKS, MDB_CREATE, m_alt_blocks, "Failed to open db handle for m_alt_blocks");
+
// this subdb is dropped on sight, so it may not be present when we open the DB.
// Since we use MDB_CREATE, we'll get an exception if we open read-only and it does not exist.
// So we don't open for read-only, and also not drop below. It is not used elsewhere.
@@ -1423,6 +1429,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
mdb_set_compare(txn, m_txpool_meta, compare_hash32);
mdb_set_compare(txn, m_txpool_blob, compare_hash32);
+ mdb_set_compare(txn, m_alt_blocks, compare_hash32);
mdb_set_compare(txn, m_properties, compare_string);
if (!(mdb_flags & MDB_RDONLY))
@@ -2290,6 +2297,50 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&,
return ret;
}
+bool BlockchainLMDB::for_all_alt_blocks(std::function<bool(const crypto::hash&, const alt_block_data_t&, const cryptonote::blobdata*)> f, bool include_blob) const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(alt_blocks);
+
+ MDB_val k;
+ MDB_val v;
+ bool ret = true;
+
+ MDB_cursor_op op = MDB_FIRST;
+ while (1)
+ {
+ int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, op);
+ op = MDB_NEXT;
+ if (result == MDB_NOTFOUND)
+ break;
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to enumerate alt blocks: ", result).c_str()));
+ const crypto::hash &blkid = *(const crypto::hash*)k.mv_data;
+ if (v.mv_size < sizeof(alt_block_data_t))
+ throw0(DB_ERROR("alt_blocks record is too small"));
+ const alt_block_data_t *data = (const alt_block_data_t*)v.mv_data;
+ const cryptonote::blobdata *passed_bd = NULL;
+ cryptonote::blobdata bd;
+ if (include_blob)
+ {
+ bd.assign(reinterpret_cast<const char*>(v.mv_data) + sizeof(alt_block_data_t), v.mv_size - sizeof(alt_block_data_t));
+ passed_bd = &bd;
+ }
+
+ if (!f(blkid, *data, passed_bd)) {
+ ret = false;
+ break;
+ }
+ }
+
+ TXN_POSTFIX_RDONLY();
+
+ return ret;
+}
+
bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -4111,6 +4162,110 @@ uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const
return ret;
}
+void BlockchainLMDB::add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ mdb_txn_cursors *m_cursors = &m_wcursors;
+
+ CURSOR(alt_blocks)
+
+ MDB_val k = {sizeof(blkid), (void *)&blkid};
+ const size_t val_size = sizeof(alt_block_data_t) + blob.size();
+ std::unique_ptr<char[]> val(new char[val_size]);
+ memcpy(val.get(), &data, sizeof(alt_block_data_t));
+ memcpy(val.get() + sizeof(alt_block_data_t), blob.data(), blob.size());
+ MDB_val v = {val_size, (void *)val.get()};
+ if (auto result = mdb_cursor_put(m_cur_alt_blocks, &k, &v, MDB_NODUPDATA)) {
+ if (result == MDB_KEYEXIST)
+ throw1(DB_ERROR("Attempting to add alternate block that's already in the db"));
+ else
+ throw1(DB_ERROR(lmdb_error("Error adding alternate block to db transaction: ", result).c_str()));
+ }
+}
+
+bool BlockchainLMDB::get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob)
+{
+ LOG_PRINT_L3("BlockchainLMDB:: " << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(alt_blocks);
+
+ MDB_val_set(k, blkid);
+ MDB_val v;
+ int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET);
+ if (result == MDB_NOTFOUND)
+ return false;
+
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error attempting to retrieve alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str()));
+ if (v.mv_size < sizeof(alt_block_data_t))
+ throw0(DB_ERROR("Record size is less than expected"));
+
+ const alt_block_data_t *ptr = (const alt_block_data_t*)v.mv_data;
+ if (data)
+ *data = *ptr;
+ if (blob)
+ blob->assign((const char*)(ptr + 1), v.mv_size - sizeof(alt_block_data_t));
+
+ TXN_POSTFIX_RDONLY();
+ return true;
+}
+
+void BlockchainLMDB::remove_alt_block(const crypto::hash &blkid)
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+ mdb_txn_cursors *m_cursors = &m_wcursors;
+
+ CURSOR(alt_blocks)
+
+ MDB_val k = {sizeof(blkid), (void *)&blkid};
+ MDB_val v;
+ int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error locating alternate block " + epee::string_tools::pod_to_hex(blkid) + " in the db: ", result).c_str()));
+ result = mdb_cursor_del(m_cur_alt_blocks, 0);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Error deleting alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str()));
+}
+
+uint64_t BlockchainLMDB::get_alt_block_count()
+{
+ LOG_PRINT_L3("BlockchainLMDB:: " << __func__);
+ check_open();
+
+ TXN_PREFIX_RDONLY();
+ RCURSOR(alt_blocks);
+
+ MDB_stat db_stats;
+ int result = mdb_stat(m_txn, m_alt_blocks, &db_stats);
+ uint64_t count = 0;
+ if (result != MDB_NOTFOUND)
+ {
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to query m_alt_blocks: ", result).c_str()));
+ count = db_stats.ms_entries;
+ }
+ TXN_POSTFIX_RDONLY();
+ return count;
+}
+
+void BlockchainLMDB::drop_alt_blocks()
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ check_open();
+
+ TXN_PREFIX(0);
+
+ auto result = mdb_drop(*txn_ptr, m_alt_blocks, 0);
+ if (result)
+ throw1(DB_ERROR(lmdb_error("Error dropping alternative blocks: ", result).c_str()));
+
+ TXN_POSTFIX_SUCCESS();
+}
+
bool BlockchainLMDB::is_read_only() const
{
unsigned int flags;
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index 4b46f081e..61a551476 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -67,6 +67,8 @@ typedef struct mdb_txn_cursors
MDB_cursor *m_txc_txpool_meta;
MDB_cursor *m_txc_txpool_blob;
+ MDB_cursor *m_txc_alt_blocks;
+
MDB_cursor *m_txc_hf_versions;
MDB_cursor *m_txc_properties;
@@ -87,6 +89,7 @@ typedef struct mdb_txn_cursors
#define m_cur_spent_keys m_cursors->m_txc_spent_keys
#define m_cur_txpool_meta m_cursors->m_txc_txpool_meta
#define m_cur_txpool_blob m_cursors->m_txc_txpool_blob
+#define m_cur_alt_blocks m_cursors->m_txc_alt_blocks
#define m_cur_hf_versions m_cursors->m_txc_hf_versions
#define m_cur_properties m_cursors->m_txc_properties
@@ -108,6 +111,7 @@ typedef struct mdb_rflags
bool m_rf_spent_keys;
bool m_rf_txpool_meta;
bool m_rf_txpool_blob;
+ bool m_rf_alt_blocks;
bool m_rf_hf_versions;
bool m_rf_properties;
} mdb_rflags;
@@ -288,6 +292,12 @@ public:
virtual bool update_pruning();
virtual bool check_pruning();
+ virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob);
+ virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob);
+ virtual void remove_alt_block(const crypto::hash &blkid);
+ virtual uint64_t get_alt_block_count();
+ virtual void drop_alt_blocks();
+
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const;
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
@@ -295,6 +305,7 @@ public:
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const;
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const;
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const;
+ virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const;
virtual uint64_t add_block( const std::pair<block, blobdata>& blk
, size_t block_weight
@@ -452,6 +463,8 @@ private:
MDB_dbi m_txpool_meta;
MDB_dbi m_txpool_blob;
+ MDB_dbi m_alt_blocks;
+
MDB_dbi m_hf_starting_heights;
MDB_dbi m_hf_versions;
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index 34e635899..ac19fae25 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -156,6 +156,13 @@ public:
virtual uint64_t get_max_block_size() override { return 100000000; }
virtual void add_max_block_size(uint64_t sz) override { }
+
+ virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) override {}
+ virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) override { return false; }
+ virtual void remove_alt_block(const crypto::hash &blkid) override {}
+ virtual uint64_t get_alt_block_count() override { return 0; }
+ virtual void drop_alt_blocks() override {}
+ virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const override { return true; }
};
}
diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp
index 6ff184041..f824d93a6 100644
--- a/src/blockchain_utilities/blockchain_blackball.cpp
+++ b/src/blockchain_utilities/blockchain_blackball.cpp
@@ -637,6 +637,7 @@ static void inc_per_amount_outputs(MDB_txn *txn, uint64_t amount, uint64_t total
v.mv_size = 2 * sizeof(uint64_t);
v.mv_data = (void*)data;
dbr = mdb_cursor_put(cur, &k, &v, 0);
+ CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to write record for per amount outputs: " + std::string(mdb_strerror(dbr)));
mdb_cursor_close(cur);
}
diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp
index fa1243c1f..85566efca 100644
--- a/src/blockchain_utilities/blockchain_export.cpp
+++ b/src/blockchain_utilities/blockchain_export.cpp
@@ -177,7 +177,7 @@ int main(int argc, char* argv[])
}
r = core_storage->init(db, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET);
- if (core_storage->get_blockchain_pruning_seed())
+ if (core_storage->get_blockchain_pruning_seed() && !opt_blocks_dat)
{
LOG_PRINT_L0("Blockchain is pruned, cannot export");
return 1;
diff --git a/src/common/util.cpp b/src/common/util.cpp
index db5aa3052..0fa9e8dc1 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -1068,6 +1068,23 @@ std::string get_nix_version_display_string()
return std::string(buffer);
}
+ std::string get_human_readable_timespan(uint64_t seconds)
+ {
+ if (seconds < 60)
+ return std::to_string(seconds) + " seconds";
+ if (seconds < 3600)
+ return std::to_string((uint64_t)(seconds / 60)) + " minutes";
+ if (seconds < 3600 * 24)
+ return std::to_string((uint64_t)(seconds / 3600)) + " hours";
+ if (seconds < 3600 * 24 * 30.5)
+ return std::to_string((uint64_t)(seconds / (3600 * 24))) + " days";
+ if (seconds < 3600 * 24 * 365.25)
+ return std::to_string((uint64_t)(seconds / (3600 * 24 * 30.5))) + " months";
+ if (seconds < 3600 * 24 * 365.25 * 100)
+ return std::to_string((uint64_t)(seconds / (3600 * 24 * 30.5 * 365.25))) + " years";
+ return "a long time";
+ }
+
std::string get_human_readable_bytes(uint64_t bytes)
{
// Use 1024 for "kilo", 1024*1024 for "mega" and so on instead of the more modern and standard-conforming
diff --git a/src/common/util.h b/src/common/util.h
index f6d5c9b1f..b0f734eff 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -245,5 +245,7 @@ namespace tools
std::string get_human_readable_timestamp(uint64_t ts);
+ std::string get_human_readable_timespan(uint64_t seconds);
+
std::string get_human_readable_bytes(uint64_t bytes);
}
diff --git a/src/crypto/CryptonightR_JIT.c b/src/crypto/CryptonightR_JIT.c
index ee8f3f36f..ffe7820a2 100644
--- a/src/crypto/CryptonightR_JIT.c
+++ b/src/crypto/CryptonightR_JIT.c
@@ -63,7 +63,7 @@ int v4_generate_JIT_code(const struct V4_Instruction* code, v4_random_math_JIT_f
#if !(defined(_MSC_VER) || defined(__MINGW32__))
if (mprotect((void*)buf, buf_size, PROT_READ | PROT_WRITE))
- return 1;
+ return -1;
#endif
APPEND_CODE(prologue, sizeof(prologue));
@@ -111,13 +111,13 @@ int v4_generate_JIT_code(const struct V4_Instruction* code, v4_random_math_JIT_f
#if !(defined(_MSC_VER) || defined(__MINGW32__))
if (mprotect((void*)buf, buf_size, PROT_READ | PROT_EXEC))
- return 1;
+ return -1;
#endif
__builtin___clear_cache((char*)buf, (char*)JIT_code);
return 0;
#else
- return 1;
+ return -1;
#endif
}
diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c
index 7f36c9dc3..1fa819b57 100644
--- a/src/crypto/slow-hash.c
+++ b/src/crypto/slow-hash.c
@@ -43,7 +43,6 @@
#include "CryptonightR_JIT.h"
#include <errno.h>
-#include <string.h>
#define MEMORY (1 << 21) // 2MB scratchpad
#define ITER (1 << 20)
@@ -897,7 +896,6 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
// locals to avoid constant TLS dereferencing
uint8_t *local_hp_state = hp_state;
- v4_random_math_JIT_func local_hp_jitfunc = hp_jitfunc;
/* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */
if (prehashed) {
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index e594eb049..2dad2795e 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -91,7 +91,7 @@ namespace cryptonote
const command_line::arg_descriptor<std::string> arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true};
const command_line::arg_descriptor<std::string> arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true};
const command_line::arg_descriptor<uint32_t> arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true};
- const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable/disable background mining", true, true};
+ const command_line::arg_descriptor<bool> arg_bg_mining_enable = {"bg-mining-enable", "enable background mining", true, true};
const command_line::arg_descriptor<bool> arg_bg_mining_ignore_battery = {"bg-mining-ignore-battery", "if true, assumes plugged in when unable to query system power status", false, true};
const command_line::arg_descriptor<uint64_t> arg_bg_mining_min_idle_interval_seconds = {"bg-mining-min-idle-interval", "Specify min lookback interval in seconds for determining idle state", miner::BACKGROUND_MINING_DEFAULT_MIN_IDLE_INTERVAL_IN_SECONDS, true};
const command_line::arg_descriptor<uint16_t> arg_bg_mining_idle_threshold_percentage = {"bg-mining-idle-threshold", "Specify minimum avg idle percentage over lookback interval", miner::BACKGROUND_MINING_DEFAULT_IDLE_THRESHOLD_PERCENTAGE, true};
diff --git a/src/cryptonote_basic/verification_context.h b/src/cryptonote_basic/verification_context.h
index 36b63f254..3d7200fae 100644
--- a/src/cryptonote_basic/verification_context.h
+++ b/src/cryptonote_basic/verification_context.h
@@ -48,6 +48,7 @@ namespace cryptonote
bool m_overspend;
bool m_fee_too_low;
bool m_not_rct;
+ bool m_too_few_outputs;
};
struct block_verification_context
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h
index 56b6a63b7..b68bb41e1 100644
--- a/src/cryptonote_config.h
+++ b/src/cryptonote_config.h
@@ -128,6 +128,8 @@
#define P2P_SUPPORT_FLAG_FLUFFY_BLOCKS 0x01
#define P2P_SUPPORT_FLAGS P2P_SUPPORT_FLAG_FLUFFY_BLOCKS
+#define RPC_IP_FAILS_BEFORE_BLOCK 3
+
#define ALLOW_DEBUG_COMMANDS
#define CRYPTONOTE_NAME "bitmonero"
@@ -147,6 +149,7 @@
#define HF_VERSION_PER_BYTE_FEE 8
#define HF_VERSION_SMALLER_BP 10
#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10
+#define HF_VERSION_MIN_2_OUTPUTS 12
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index ac2c07913..dab1a0a96 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -715,7 +715,6 @@ block Blockchain::pop_block_from_blockchain()
m_blocks_longhash_table.clear();
m_scan_table.clear();
m_blocks_txs_check.clear();
- m_check_txin_table.clear();
CHECK_AND_ASSERT_THROW_MES(update_next_cumulative_weight_limit(), "Error updating next cumulative weight limit");
uint64_t top_block_height;
@@ -731,9 +730,9 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
m_timestamps_and_difficulties_height = 0;
- m_alternative_chains.clear();
invalidate_block_template_cache();
m_db->reset();
+ m_db->drop_alt_blocks();
m_hardfork->init();
db_wtxn_guard wtxn_guard(m_db);
@@ -858,10 +857,15 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph
// try to find block in alternative chain
catch (const BLOCK_DNE& e)
{
- blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h);
- if (m_alternative_chains.end() != it_alt)
+ alt_block_data_t data;
+ cryptonote::blobdata blob;
+ if (m_db->get_alt_block(h, &data, &blob))
{
- blk = it_alt->second.bl;
+ if (!cryptonote::parse_and_validate_block_from_blob(blob, blk))
+ {
+ MERROR("Found block " << h << " in alt chain, but failed to parse it");
+ throw std::runtime_error("Found block in alt chain, but failed to parse it");
+ }
if (orphan)
*orphan = true;
return true;
@@ -1019,7 +1023,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
//------------------------------------------------------------------
// This function attempts to switch to an alternate chain, returning
// boolean based on success therein.
-bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain)
+bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain)
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -1030,7 +1034,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
// verify that main chain has front of alt chain's parent block
- if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
+ if (!m_db->block_exists(alt_chain.front().bl.prev_id))
{
LOG_ERROR("Attempting to move to an alternate chain, but it doesn't appear to connect to the main chain!");
return false;
@@ -1039,7 +1043,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
// pop blocks from the blockchain until the top block is the parent
// of the front block of the alt chain.
std::list<block> disconnected_chain;
- while (m_db->top_block_hash() != alt_chain.front()->second.bl.prev_id)
+ while (m_db->top_block_hash() != alt_chain.front().bl.prev_id)
{
block b = pop_block_from_blockchain();
disconnected_chain.push_front(b);
@@ -1050,11 +1054,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
//connecting new alternative chain
for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
{
- auto ch_ent = *alt_ch_iter;
+ const auto &bei = *alt_ch_iter;
block_verification_context bvc = boost::value_initialized<block_verification_context>();
// add block to main chain
- bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc);
+ bool r = handle_block_to_main_chain(bei.bl, bvc);
// if adding block to main chain failed, rollback to previous state and
// return false
@@ -1070,14 +1074,18 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
// FIXME: Why do we keep invalid blocks around? Possibly in case we hear
// about them again so we can immediately dismiss them, but needs some
// looking into.
- add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl));
- MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl));
- m_alternative_chains.erase(*alt_ch_iter++);
+ const crypto::hash blkid = cryptonote::get_block_hash(bei.bl);
+ add_block_as_invalid(bei, blkid);
+ MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << blkid);
+ m_db->remove_alt_block(blkid);
+ alt_ch_iter++;
for(auto alt_ch_to_orph_iter = alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); )
{
- add_block_as_invalid((*alt_ch_to_orph_iter)->second, (*alt_ch_to_orph_iter)->first);
- m_alternative_chains.erase(*alt_ch_to_orph_iter++);
+ const auto &bei = *alt_ch_to_orph_iter++;
+ const crypto::hash blkid = cryptonote::get_block_hash(bei.bl);
+ add_block_as_invalid(bei, blkid);
+ m_db->remove_alt_block(blkid);
}
return false;
}
@@ -1102,9 +1110,9 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
}
//removing alt_chain entries from alternative chains container
- for (auto ch_ent: alt_chain)
+ for (const auto &bei: alt_chain)
{
- m_alternative_chains.erase(ch_ent);
+ m_db->remove_alt_block(cryptonote::get_block_hash(bei.bl));
}
m_hardfork->reorganize_from_chain_height(split_height);
@@ -1120,7 +1128,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
//------------------------------------------------------------------
// This function calculates the difficulty target for the block being added to
// an alternate chain.
-difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const
+difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const
{
if (m_fixed_difficulty)
{
@@ -1138,7 +1146,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
CRITICAL_REGION_LOCAL(m_blockchain_lock);
// Figure out start and stop offsets for main chain blocks
- size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
+ size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front().height : bei.height;
size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
@@ -1156,10 +1164,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
// make sure we haven't accidentally grabbed too many blocks...maybe don't need this check?
CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT);
- for (auto it : alt_chain)
+ for (const auto &bei : alt_chain)
{
- timestamps.push_back(it->second.bl.timestamp);
- cumulative_difficulties.push_back(it->second.cumulative_difficulty);
+ timestamps.push_back(bei.bl.timestamp);
+ cumulative_difficulties.push_back(bei.cumulative_difficulty);
}
}
// if the alt chain is long enough for the difficulty calc, grab difficulties
@@ -1171,10 +1179,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
size_t count = 0;
size_t max_i = timestamps.size()-1;
// get difficulties and timestamps from most recent blocks in alt chain
- for(auto it: boost::adaptors::reverse(alt_chain))
+ for (const auto bei: boost::adaptors::reverse(alt_chain))
{
- timestamps[max_i - count] = it->second.bl.timestamp;
- cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
+ timestamps[max_i - count] = bei.bl.timestamp;
+ cumulative_difficulties[max_i - count] = bei.cumulative_difficulty;
count++;
if(count >= DIFFICULTY_BLOCKS_COUNT)
break;
@@ -1393,16 +1401,17 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
//build alternative subchain, front -> mainchain, back -> alternative head
//block is not related with head of main chain
//first of all - look in alternative chains container
- auto it_prev = m_alternative_chains.find(*from_block);
+ alt_block_data_t prev_data;
+ bool parent_in_alt = m_db->get_alt_block(*from_block, &prev_data, NULL);
bool parent_in_main = m_db->block_exists(*from_block);
- if(it_prev == m_alternative_chains.end() && !parent_in_main)
+ if (!parent_in_alt && !parent_in_main)
{
MERROR("Unknown from block");
return false;
}
//we have new block in alternative chain
- std::list<blocks_ext_by_hash::const_iterator> alt_chain;
+ std::list<block_extended_info> alt_chain;
block_verification_context bvc = boost::value_initialized<block_verification_context>();
std::vector<uint64_t> timestamps;
if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
@@ -1417,7 +1426,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
}
else
{
- height = alt_chain.back()->second.height + 1;
+ height = alt_chain.back().height + 1;
}
b.major_version = m_hardfork->get_ideal_version(height);
b.minor_version = m_hardfork->get_ideal_version();
@@ -1432,14 +1441,14 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
}
else
{
- median_weight = it_prev->second.block_cumulative_weight - it_prev->second.block_cumulative_weight / 20;
- already_generated_coins = alt_chain.back()->second.already_generated_coins;
+ median_weight = prev_data.cumulative_weight - prev_data.cumulative_weight / 20;
+ already_generated_coins = alt_chain.back().already_generated_coins;
}
// FIXME: consider moving away from block_extended_info at some point
block_extended_info bei = boost::value_initialized<block_extended_info>();
bei.bl = b;
- bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(*from_block) + 1;
+ bei.height = alt_chain.size() ? prev_data.height + 1 : m_db->get_block_height(*from_block) + 1;
diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei);
}
@@ -1618,16 +1627,25 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
return true;
}
//------------------------------------------------------------------
-bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const
+bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const
{
//build alternative subchain, front -> mainchain, back -> alternative head
- blocks_ext_by_hash::const_iterator alt_it = m_alternative_chains.find(prev_id);
+ cryptonote::alt_block_data_t data;
+ cryptonote::blobdata blob;
+ bool found = m_db->get_alt_block(prev_id, &data, &blob);
timestamps.clear();
- while(alt_it != m_alternative_chains.end())
+ while(found)
{
- alt_chain.push_front(alt_it);
- timestamps.push_back(alt_it->second.bl.timestamp);
- alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
+ block_extended_info bei;
+ CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_block_from_blob(blob, bei.bl), false, "Failed to parse alt block");
+ bei.height = data.height;
+ bei.block_cumulative_weight = data.cumulative_weight;
+ bei.cumulative_difficulty = data.cumulative_difficulty_high;
+ bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low;
+ bei.already_generated_coins = data.already_generated_coins;
+ timestamps.push_back(bei.bl.timestamp);
+ alt_chain.push_front(std::move(bei));
+ found = m_db->get_alt_block(bei.bl.prev_id, &data, &blob);
}
// if block to be added connects to known blocks that aren't part of the
@@ -1635,20 +1653,20 @@ bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_e
if(!alt_chain.empty())
{
// make sure alt chain doesn't somehow start past the end of the main chain
- CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
+ CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front().height, false, "main blockchain wrong height");
// make sure that the blockchain contains the block that should connect
// this alternate chain with it.
- if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
+ if (!m_db->block_exists(alt_chain.front().bl.prev_id))
{
MERROR("alternate chain does not appear to connect to main chain...");
return false;
}
// make sure block connects correctly to the main chain
- auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
- CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
- complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
+ auto h = m_db->get_block_hash_from_height(alt_chain.front().height - 1);
+ CHECK_AND_ASSERT_MES(h == alt_chain.front().bl.prev_id, false, "alternative chain has wrong connection to main chain");
+ complete_timestamps_vector(m_db->get_block_height(alt_chain.front().bl.prev_id), timestamps);
}
// if block not associated with known alternate chain
else
@@ -1703,12 +1721,13 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
//block is not related with head of main chain
//first of all - look in alternative chains container
- auto it_prev = m_alternative_chains.find(b.prev_id);
+ alt_block_data_t prev_data;
+ bool parent_in_alt = m_db->get_alt_block(b.prev_id, &prev_data, NULL);
bool parent_in_main = m_db->block_exists(b.prev_id);
- if(it_prev != m_alternative_chains.end() || parent_in_main)
+ if (parent_in_alt || parent_in_main)
{
//we have new block in alternative chain
- std::list<blocks_ext_by_hash::const_iterator> alt_chain;
+ std::list<block_extended_info> alt_chain;
std::vector<uint64_t> timestamps;
if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc))
return false;
@@ -1716,10 +1735,10 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
// FIXME: consider moving away from block_extended_info at some point
block_extended_info bei = boost::value_initialized<block_extended_info>();
bei.bl = b;
- const uint64_t prev_height = alt_chain.size() ? it_prev->second.height : m_db->get_block_height(b.prev_id);
+ const uint64_t prev_height = alt_chain.size() ? prev_data.height : m_db->get_block_height(b.prev_id);
bei.height = prev_height + 1;
uint64_t block_reward = get_outs_money_amount(b.miner_tx);
- bei.already_generated_coins = block_reward + (alt_chain.size() ? it_prev->second.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
+ bei.already_generated_coins = block_reward + (alt_chain.size() ? prev_data.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
// verify that the block's timestamp is within the acceptable range
// (not earlier than the median of the last X blocks)
@@ -1763,7 +1782,8 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
difficulty_type main_chain_cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
if (alt_chain.size())
{
- bei.cumulative_difficulty = it_prev->second.cumulative_difficulty;
+ bei.cumulative_difficulty = prev_data.cumulative_difficulty_high;
+ bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + prev_data.cumulative_difficulty_low;
}
else
{
@@ -1774,15 +1794,21 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
// add block to alternate blocks storage,
// as well as the current "alt chain" container
- auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei));
- CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist");
- alt_chain.push_back(i_res.first);
+ CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists");
+ cryptonote::alt_block_data_t data;
+ data.height = bei.height;
+ data.cumulative_weight = bei.block_cumulative_weight;
+ data.cumulative_difficulty_low = (bei.cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
+ data.cumulative_difficulty_high = ((bei.cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
+ data.already_generated_coins = bei.already_generated_coins;
+ m_db->add_alt_block(id, data, cryptonote::block_to_blob(bei.bl));
+ alt_chain.push_back(bei);
// FIXME: is it even possible for a checkpoint to show up not on the main chain?
if(is_a_checkpoint)
{
//do reorganize!
- MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height);
+ MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height);
bool r = switch_to_alternative_blockchain(alt_chain, true);
@@ -1794,7 +1820,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
else if(main_chain_cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain
{
//do reorganize!
- MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1) << std::endl << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty);
+ MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1) << std::endl << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty);
bool r = switch_to_alternative_blockchain(alt_chain, false);
if (r)
@@ -1814,7 +1840,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
//block orphaned
bvc.m_marked_as_orphaned = true;
MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height
- << ", parent in alt " << (it_prev != m_alternative_chains.end()) << ", parent in main " << parent_in_main
+ << ", parent in alt " << parent_in_alt << ", parent in main " << parent_in_main
<< " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")");
}
@@ -1912,8 +1938,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
//pack block
e.block = std::move(bl.first);
}
- //get and pack other transactions, if needed
- get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids);
return true;
}
@@ -1923,11 +1947,20 @@ bool Blockchain::get_alternative_blocks(std::vector<block>& blocks) const
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- blocks.reserve(m_alternative_chains.size());
- for (const auto& alt_bl: m_alternative_chains)
- {
- blocks.push_back(alt_bl.second.bl);
- }
+ blocks.reserve(m_db->get_alt_block_count());
+ m_db->for_all_alt_blocks([&blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) {
+ if (!blob)
+ {
+ MERROR("No blob, but blobs were requested");
+ return false;
+ }
+ cryptonote::block bl;
+ if (cryptonote::parse_and_validate_block_from_blob(*blob, bl))
+ blocks.push_back(std::move(bl));
+ else
+ MERROR("Failed to parse block from blob");
+ return true;
+ }, true);
return true;
}
//------------------------------------------------------------------
@@ -1935,7 +1968,7 @@ size_t Blockchain::get_alternative_blocks_count() const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- return m_alternative_chains.size();
+ return m_db->get_alt_block_count();
}
//------------------------------------------------------------------
// This function adds the output specified by <amount, i> to the result_outs container
@@ -2039,7 +2072,6 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height,
if (to_height > 0 && to_height < from_height)
return false;
- const uint64_t real_start_height = start_height;
if (from_height > start_height)
start_height = from_height;
@@ -2053,7 +2085,7 @@ bool Blockchain::get_output_distribution(uint64_t amount, uint64_t from_height,
{
std::vector<uint64_t> heights;
heights.reserve(to_height + 1 - start_height);
- uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height;
+ const uint64_t real_start_height = start_height > 0 ? start_height-1 : start_height;
for (uint64_t h = real_start_height; h <= to_height; ++h)
heights.push_back(h);
distribution = m_db->get_block_cumulative_rct_outputs(heights);
@@ -2423,9 +2455,9 @@ bool Blockchain::have_block(const crypto::hash& id) const
return true;
}
- if(m_alternative_chains.count(id))
+ if(m_db->get_alt_block(id, NULL, NULL))
{
- LOG_PRINT_L2("block " << id << " found in m_alternative_chains");
+ LOG_PRINT_L2("block " << id << " found in alternative chains");
return true;
}
@@ -2579,7 +2611,7 @@ void Blockchain::on_new_tx_from_block(const cryptonote::transaction &tx)
// This function overloads its sister function with
// an extra value (hash of highest block that holds an output used as input)
// as a return-by-reference.
-bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block)
+bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2610,7 +2642,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
return true;
}
//------------------------------------------------------------------
-bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context &tvc)
+bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context &tvc) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2719,7 +2751,7 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
}
return false;
}
-bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys)
+bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys) const
{
PERF_TIMER(expand_transaction_2);
CHECK_AND_ASSERT_MES(tx.version == 2, false, "Transaction version is not 2");
@@ -2795,7 +2827,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
// check_tx_input() rather than here, and use this function simply
// to iterate the inputs as necessary (splitting the task
// using threads, etc.)
-bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height)
+bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height) const
{
PERF_TIMER(check_tx_inputs);
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -2807,6 +2839,19 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const uint8_t hf_version = m_hardfork->get_current_version();
+ if (hf_version >= HF_VERSION_MIN_2_OUTPUTS)
+ {
+ if (tx.version >= 2)
+ {
+ if (tx.vout.size() < 2)
+ {
+ MERROR_VER("Tx " << get_transaction_hash(tx) << " has fewer than two outputs");
+ tvc.m_too_few_outputs = true;
+ return false;
+ }
+ }
+ }
+
// from hard fork 2, we require mixin at least 2 unless one output cannot mix with 2 others
// if one output cannot mix with 2 others, we accept at most 1 output that can mix
if (hf_version >= 2)
@@ -2901,13 +2946,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
}
}
- auto it = m_check_txin_table.find(tx_prefix_hash);
- if(it == m_check_txin_table.end())
- {
- m_check_txin_table.emplace(tx_prefix_hash, std::unordered_map<crypto::key_image, bool>());
- it = m_check_txin_table.find(tx_prefix_hash);
- assert(it != m_check_txin_table.end());
- }
std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size());
std::vector < uint64_t > results;
@@ -2939,29 +2977,12 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{
// basically, make sure number of inputs == number of signatures
CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index);
-
-#if defined(CACHE_VIN_RESULTS)
- auto itk = it->second.find(in_to_key.k_image);
- if(itk != it->second.end())
- {
- if(!itk->second)
- {
- MERROR_VER("Failed ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
- return false;
- }
-
- // txin has been verified already, skip
- sig_index++;
- continue;
- }
-#endif
}
// make sure that output being spent matches up correctly with the
// signature spending it.
if (!check_tx_input(tx.version, in_to_key, tx_prefix_hash, tx.version == 1 ? tx.signatures[sig_index] : std::vector<crypto::signature>(), tx.rct_signatures, pubkeys[sig_index], pmax_used_block_height))
{
- it->second[in_to_key.k_image] = false;
MERROR_VER("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
{
@@ -2984,7 +3005,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
check_ring_signature(tx_prefix_hash, in_to_key.k_image, pubkeys[sig_index], tx.signatures[sig_index], results[sig_index]);
if (!results[sig_index])
{
- it->second[in_to_key.k_image] = false;
MERROR_VER("Failed to check ring signature for tx " << get_transaction_hash(tx) << " vin key with k_image: " << in_to_key.k_image << " sig_index: " << sig_index);
if (pmax_used_block_height) // a default value of NULL is used when called from Blockchain::handle_block_to_main_chain()
@@ -2994,7 +3014,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false;
}
- it->second[in_to_key.k_image] = true;
}
}
@@ -3012,7 +3031,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
for (size_t i = 0; i < tx.vin.size(); i++)
{
const txin_to_key& in_to_key = boost::get<txin_to_key>(tx.vin[i]);
- it->second[in_to_key.k_image] = results[i];
if(!failed && !results[i])
failed = true;
}
@@ -3186,7 +3204,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
//------------------------------------------------------------------
-void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result)
+void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result) const
{
std::vector<const crypto::public_key *> p_output_keys;
p_output_keys.reserve(pubkeys.size());
@@ -3254,7 +3272,6 @@ uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_b
bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
{
const uint8_t version = get_current_hard_fork_version();
- const uint64_t blockchain_height = m_db->height();
uint64_t median = 0;
uint64_t already_generated_coins = 0;
@@ -3373,7 +3390,7 @@ bool Blockchain::is_tx_spendtime_unlocked(uint64_t unlock_time) const
// This function locates all outputs associated with a given input (mixins)
// and validates that they exist and are usable. It also checks the ring
// signature for each input.
-bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height)
+bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -3962,14 +3979,12 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
const uint64_t db_height = m_db->height();
const uint8_t hf_version = get_current_hard_fork_version();
uint64_t full_reward_zone = get_min_block_weight(hf_version);
- uint64_t long_term_block_weight;
if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
{
std::vector<uint64_t> weights;
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
m_current_block_cumul_weight_median = epee::misc_utils::median(weights);
- long_term_block_weight = weights.back();
}
else
{
@@ -3991,7 +4006,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
uint64_t short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5;
- long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
+ uint64_t long_term_block_weight = std::min<uint64_t>(block_weight, short_term_constraint);
if (db_height == 1)
{
@@ -4212,7 +4227,6 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
m_blocks_longhash_table.clear();
m_scan_table.clear();
m_blocks_txs_check.clear();
- m_check_txin_table.clear();
// when we're well clear of the precomputed hashes, free the memory
if (!m_blocks_hash_check.empty() && m_db->height() > m_blocks_hash_check.size() + 4096)
@@ -4501,7 +4515,6 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
m_fake_pow_calc_time = 0;
m_scan_table.clear();
- m_check_txin_table.clear();
TIME_MEASURE_FINISH(prepare);
m_fake_pow_calc_time = prepare / blocks_entry.size();
@@ -4810,15 +4823,39 @@ std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> Blockchain:: get_ou
return m_db->get_output_histogram(amounts, unlocked, recent_cutoff, min_count);
}
-std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> Blockchain::get_alternative_chains() const
+std::vector<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> Blockchain::get_alternative_chains() const
{
- std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
+ std::vector<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
+
+ blocks_ext_by_hash alt_blocks;
+ alt_blocks.reserve(m_db->get_alt_block_count());
+ m_db->for_all_alt_blocks([&alt_blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) {
+ if (!blob)
+ {
+ MERROR("No blob, but blobs were requested");
+ return false;
+ }
+ cryptonote::block bl;
+ block_extended_info bei;
+ if (cryptonote::parse_and_validate_block_from_blob(*blob, bei.bl))
+ {
+ bei.height = data.height;
+ bei.block_cumulative_weight = data.cumulative_weight;
+ bei.cumulative_difficulty = data.cumulative_difficulty_high;
+ bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low;
+ bei.already_generated_coins = data.already_generated_coins;
+ alt_blocks.insert(std::make_pair(cryptonote::get_block_hash(bei.bl), std::move(bei)));
+ }
+ else
+ MERROR("Failed to parse block from blob");
+ return true;
+ }, true);
- for (const auto &i: m_alternative_chains)
+ for (const auto &i: alt_blocks)
{
- const crypto::hash &top = i.first;
+ const crypto::hash top = cryptonote::get_block_hash(i.second.bl);
bool found = false;
- for (const auto &j: m_alternative_chains)
+ for (const auto &j: alt_blocks)
{
if (j.second.bl.prev_id == top)
{
@@ -4832,7 +4869,7 @@ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>>
auto h = i.second.bl.prev_id;
chain.push_back(top);
blocks_ext_by_hash::const_iterator prev;
- while ((prev = m_alternative_chains.find(h)) != m_alternative_chains.end())
+ while ((prev = alt_blocks.find(h)) != alt_blocks.end())
{
chain.push_back(h);
h = prev->second.bl.prev_id;
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 32ed96b5b..f58059a6d 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -92,24 +92,13 @@ namespace cryptonote
{
public:
/**
- * @brief Now-defunct (TODO: remove) struct from in-memory blockchain
- */
- struct transaction_chain_entry
- {
- transaction tx;
- uint64_t m_keeper_block_height;
- size_t m_blob_size;
- std::vector<uint64_t> m_global_output_indexes;
- };
-
- /**
* @brief container for passing a block and metadata about it on the blockchain
*/
struct block_extended_info
{
block bl; //!< the block
uint64_t height; //!< the height of the block in the blockchain
- size_t block_cumulative_weight; //!< the weight of the block
+ uint64_t block_cumulative_weight; //!< the weight of the block
difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block
uint64_t already_generated_coins; //!< the total coins minted after that block
};
@@ -559,7 +548,7 @@ namespace cryptonote
*
* @return false if any input is invalid, otherwise true
*/
- bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
+ bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false) const;
/**
* @brief get fee quantization mask
@@ -626,7 +615,7 @@ namespace cryptonote
*
* @return false if any outputs do not conform, otherwise true
*/
- bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc);
+ bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc) const;
/**
* @brief gets the block weight limit based on recent blocks
@@ -961,9 +950,9 @@ namespace cryptonote
/**
* @brief returns a set of known alternate chains
*
- * @return a list of chains
+ * @return a vector of chains
*/
- std::list<std::pair<block_extended_info,std::vector<crypto::hash>>> get_alternative_chains() const;
+ std::vector<std::pair<block_extended_info,std::vector<crypto::hash>>> get_alternative_chains() const;
void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const txpool_tx_meta_t &meta);
void update_txpool_tx(const crypto::hash &txid, const txpool_tx_meta_t &meta);
@@ -1011,20 +1000,12 @@ namespace cryptonote
#endif
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
- typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
-
- typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container;
-
typedef std::unordered_set<crypto::key_image> key_images_container;
typedef std::vector<block_extended_info> blocks_container;
typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash;
- typedef std::unordered_map<crypto::hash, block> blocks_by_hash;
-
- typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction
-
BlockchainDB* m_db;
@@ -1033,14 +1014,12 @@ namespace cryptonote
mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
// main chain
- transactions_container m_transactions;
size_t m_current_block_cumul_weight_limit;
size_t m_current_block_cumul_weight_median;
// metadata containers
std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table;
std::unordered_map<crypto::hash, crypto::hash> m_blocks_longhash_table;
- std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, bool>> m_check_txin_table;
// SHA-3 hashes for each block and for fast pow checking
std::vector<crypto::hash> m_blocks_hash_of_hashes;
@@ -1074,9 +1053,6 @@ namespace cryptonote
boost::thread_group m_async_pool;
std::unique_ptr<boost::asio::io_service::work> m_async_work_idle;
- // all alternative chains
- blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info
-
// some invalid blocks
blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info
@@ -1150,7 +1126,7 @@ namespace cryptonote
*
* @return false if any output is not yet unlocked, or is missing, otherwise true
*/
- bool check_tx_input(size_t tx_version,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height);
+ bool check_tx_input(size_t tx_version,const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const rct::rctSig &rct_signatures, std::vector<rct::ctkey> &output_keys, uint64_t* pmax_related_block_height) const;
/**
* @brief validate a transaction's inputs and their keys
@@ -1172,7 +1148,7 @@ namespace cryptonote
*
* @return false if any validation step fails, otherwise true
*/
- bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL);
+ bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL) const;
/**
* @brief performs a blockchain reorganization according to the longest chain rule
@@ -1186,7 +1162,7 @@ namespace cryptonote
*
* @return false if the reorganization fails, otherwise true
*/
- bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain);
+ bool switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain);
/**
* @brief removes the most recent block from the blockchain
@@ -1249,7 +1225,7 @@ namespace cryptonote
*
* @return true on success, false otherwise
*/
- bool build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const;
+ bool build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> &timestamps, block_verification_context& bvc) const;
/**
* @brief gets the difficulty requirement for a new block on an alternate chain
@@ -1259,7 +1235,7 @@ namespace cryptonote
*
* @return the difficulty requirement
*/
- difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const;
+ difficulty_type get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const;
/**
* @brief sanity checks a miner transaction before validating an entire block
@@ -1452,7 +1428,7 @@ namespace cryptonote
* @param result false if the ring signature is invalid, otherwise true
*/
void check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image,
- const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result);
+ const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature> &sig, uint64_t &result) const;
/**
* @brief loads block hashes from compiled-in data set
@@ -1472,7 +1448,7 @@ namespace cryptonote
* can be reconstituted by the receiver. This function expands
* that implicit data.
*/
- bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys);
+ bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys) const;
/**
* @brief invalidates any cached block template
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 13426230e..a3a92ab60 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -208,13 +208,17 @@ namespace cryptonote
"is acted upon."
, ""
};
+ static const command_line::arg_descriptor<bool> arg_keep_alt_blocks = {
+ "keep-alt-blocks"
+ , "Keep alternative blocks on restart"
+ , false
+ };
//-----------------------------------------------------------------------------------------------
core::core(i_cryptonote_protocol* pprotocol):
m_mempool(m_blockchain_storage),
m_blockchain_storage(m_mempool),
m_miner(this),
- m_miner_address(boost::value_initialized<account_public_address>()),
m_starter_message_showed(false),
m_target_blockchain_height(0),
m_checkpoints_path(""),
@@ -325,6 +329,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_prune_blockchain);
command_line::add_arg(desc, arg_reorg_notify);
command_line::add_arg(desc, arg_block_rate_notify);
+ command_line::add_arg(desc, arg_keep_alt_blocks);
miner::init_options(desc);
BlockchainDB::init_options(desc);
@@ -447,6 +452,7 @@ namespace cryptonote
m_nettype = FAKECHAIN;
}
bool r = handle_command_line(vm);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to handle command line");
std::string db_type = command_line::get_arg(vm, cryptonote::arg_db_type);
std::string db_sync_mode = command_line::get_arg(vm, cryptonote::arg_db_sync_mode);
@@ -456,6 +462,7 @@ namespace cryptonote
std::string check_updates_string = command_line::get_arg(vm, arg_check_updates);
size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight);
bool prune_blockchain = command_line::get_arg(vm, arg_prune_blockchain);
+ bool keep_alt_blocks = command_line::get_arg(vm, arg_keep_alt_blocks);
boost::filesystem::path folder(m_config_folder);
if (m_nettype == FAKECHAIN)
@@ -634,6 +641,7 @@ namespace cryptonote
};
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty, get_checkpoints);
+ CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage");
r = m_mempool.init(max_txpool_weight);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
@@ -671,6 +679,9 @@ namespace cryptonote
r = m_miner.init(vm, m_nettype);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance");
+ if (!keep_alt_blocks && !m_blockchain_storage.get_db().is_read_only())
+ m_blockchain_storage.get_db().drop_alt_blocks();
+
if (prune_blockchain)
{
// display a message if the blockchain is not pruned yet
@@ -1833,7 +1844,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_block_rate()
{
- if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height())
+ if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height() || m_target_blockchain_height == 0)
{
MDEBUG("Not checking block rate, offline or syncing");
return true;
diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h
index 2fcf26a17..badbaf936 100644
--- a/src/cryptonote_core/cryptonote_core.h
+++ b/src/cryptonote_core/cryptonote_core.h
@@ -1014,7 +1014,6 @@ namespace cryptonote
//m_miner and m_miner_addres are probably temporary here
miner m_miner; //!< miner instance
- account_public_address m_miner_address; //!< address to mine to (for miner instance)
std::string m_config_folder; //!< folder to look in for configs and other files
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index 854f3d1c5..4cf71e558 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -354,7 +354,7 @@ namespace cryptonote
if (shuffle_outs)
{
- std::shuffle(destinations.begin(), destinations.end(), std::default_random_engine(crypto::rand<unsigned int>()));
+ std::shuffle(destinations.begin(), destinations.end(), crypto::random_device{});
}
// sort ins by their key image
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index d38aa7474..b03eb6e70 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -83,6 +83,21 @@ namespace cryptonote
tx_destination_entry(uint64_t a, const account_public_address &ad, bool is_subaddress) : amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { }
tx_destination_entry(const std::string &o, uint64_t a, const account_public_address &ad, bool is_subaddress) : original(o), amount(a), addr(ad), is_subaddress(is_subaddress), is_integrated(false) { }
+ std::string address(network_type nettype, const crypto::hash &payment_id) const
+ {
+ if (!original.empty())
+ {
+ return original;
+ }
+
+ if (is_integrated)
+ {
+ return get_account_integrated_address_as_str(nettype, addr, reinterpret_cast<const crypto::hash8 &>(payment_id));
+ }
+
+ return get_account_address_as_str(nettype, is_subaddress, addr);
+ }
+
BEGIN_SERIALIZE_OBJECT()
FIELD(original)
VARINT_FIELD(amount)
diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp
index 36be91f2c..03cbb5c26 100644
--- a/src/cryptonote_core/tx_sanity_check.cpp
+++ b/src/cryptonote_core/tx_sanity_check.cpp
@@ -72,7 +72,7 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
if (n_indices <= 10)
{
- MERROR("n_indices is only " << n_indices);
+ MDEBUG("n_indices is only " << n_indices << ", not checking");
return true;
}
@@ -82,7 +82,7 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
if (rct_indices.size() < n_indices * 8 / 10)
{
- MERROR("unique indices is only " << rct_indices.size() << "/" << n_indices);
+ MERROR("amount of unique indices is too low (amount of rct indices is " << rct_indices.size() << ", out of total " << n_indices << "indices.");
return false;
}
@@ -90,7 +90,7 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
uint64_t median = epee::misc_utils::median(offsets);
if (median < n_available * 6 / 10)
{
- MERROR("median is " << median << "/" << n_available);
+ 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.");
return false;
}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index 3083a5b4c..b2f8da399 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -83,6 +83,8 @@ namespace cryptonote
uint32_t pruning_seed;
+ uint8_t address_type;
+
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(incoming)
KV_SERIALIZE(localhost)
@@ -107,6 +109,7 @@ namespace cryptonote
KV_SERIALIZE(connection_id)
KV_SERIALIZE(height)
KV_SERIALIZE(pruning_seed)
+ KV_SERIALIZE(address_type)
END_KV_SERIALIZE_MAP()
};
@@ -172,11 +175,8 @@ namespace cryptonote
struct request_t
{
- std::vector<crypto::hash> txs;
- std::vector<crypto::hash> blocks;
-
+ std::vector<crypto::hash> blocks;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks)
END_KV_SERIALIZE_MAP()
};
@@ -189,13 +189,11 @@ namespace cryptonote
struct request_t
{
- std::vector<blobdata> txs;
std::vector<block_complete_entry> blocks;
std::vector<crypto::hash> missed_ids;
uint64_t current_blockchain_height;
BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(txs)
KV_SERIALIZE(blocks)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids)
KV_SERIALIZE(current_blockchain_height)
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 03d04a074..a1fa9484c 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -285,6 +285,7 @@ namespace cryptonote
cnx.height = cntxt.m_remote_blockchain_height;
cnx.pruning_seed = cntxt.m_pruning_seed;
+ cnx.address_type = (uint8_t)cntxt.m_remote_address.get_type_id();
connections.push_back(cnx);
@@ -933,13 +934,12 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)
{
- MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks, " << arg.txs.size() << " txes)");
-
- if (arg.blocks.size() + arg.txs.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT)
+ MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks)");
+ if (arg.blocks.size() > CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT)
{
LOG_ERROR_CCONTEXT(
"Requested objects count is too big ("
- << arg.blocks.size() + arg.txs.size() << ") expected not more then "
+ << arg.blocks.size() << ") expected not more then "
<< CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT);
drop_connection(context, false, false);
return 1;
@@ -952,8 +952,9 @@ namespace cryptonote
drop_connection(context, false, false);
return 1;
}
- MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size()
- << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size());
+ MLOG_P2P_MESSAGE("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()="
+ << rsp.blocks.size() << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height
+ << ", missed_ids.size()=" << rsp.missed_ids.size());
post_notify<NOTIFY_RESPONSE_GET_OBJECTS>(rsp, context);
//handler_response_blocks_now(sizeof(rsp)); // XXX
//handler_response_blocks_now(200);
@@ -978,7 +979,7 @@ namespace cryptonote
template<class t_core>
int t_cryptonote_protocol_handler<t_core>::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context)
{
- MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_GET_OBJECTS (" << arg.blocks.size() << " blocks, " << arg.txs.size() << " txes)");
+ MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_GET_OBJECTS (" << arg.blocks.size() << " blocks)");
MLOG_PEER_STATE("received objects");
boost::posix_time::ptime request_time = context.m_last_request_time;
@@ -986,8 +987,6 @@ namespace cryptonote
// calculate size of request
size_t size = 0;
- for (const auto &element : arg.txs) size += element.size();
-
size_t blocks_size = 0;
for (const auto &element : arg.blocks) {
blocks_size += element.block.size();
@@ -1947,7 +1946,7 @@ skip:
}
context.m_last_request_time = boost::posix_time::microsec_clock::universal_time();
- MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size()
+ MLOG_P2P_MESSAGE("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size()
<< "requested blocks count=" << count << " / " << count_limit << " from " << span.first << ", first hash " << req.blocks.front());
//epee::net_utils::network_throttle_manager::get_global_throttle_inreq().logger_handle_net("log/dr-monero/net/req-all.data", sec, get_avg_block_size());
diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h
index 32fdca5ea..d089d4e47 100644
--- a/src/daemon/command_line_args.h
+++ b/src/daemon/command_line_args.h
@@ -122,6 +122,11 @@ namespace daemon_args
}
};
+ const command_line::arg_descriptor<bool> arg_zmq_rpc_disabled = {
+ "no-zmq"
+ , "Disable ZMQ RPC server"
+ };
+
} // namespace daemon_args
#endif // DAEMON_COMMAND_LINE_ARGS_H
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 0b452800e..924447701 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -494,11 +494,14 @@ bool t_command_parser_executor::set_limit_down(const std::vector<std::string>& a
bool t_command_parser_executor::out_peers(const std::vector<std::string>& args)
{
- if (args.empty()) return false;
-
- unsigned int limit;
+ bool set = false;
+ uint32_t limit = 0;
try {
- limit = std::stoi(args[0]);
+ if (!args.empty())
+ {
+ limit = std::stoi(args[0]);
+ set = true;
+ }
}
catch(const std::exception& ex) {
@@ -506,16 +509,19 @@ bool t_command_parser_executor::out_peers(const std::vector<std::string>& args)
return false;
}
- return m_executor.out_peers(limit);
+ return m_executor.out_peers(set, limit);
}
bool t_command_parser_executor::in_peers(const std::vector<std::string>& args)
{
- if (args.empty()) return false;
-
- unsigned int limit;
+ bool set = false;
+ uint32_t limit = 0;
try {
- limit = std::stoi(args[0]);
+ if (!args.empty())
+ {
+ limit = std::stoi(args[0]);
+ set = true;
+ }
}
catch(const std::exception& ex) {
@@ -523,19 +529,7 @@ bool t_command_parser_executor::in_peers(const std::vector<std::string>& args)
return false;
}
- return m_executor.in_peers(limit);
-}
-
-bool t_command_parser_executor::start_save_graph(const std::vector<std::string>& args)
-{
- if (!args.empty()) return false;
- return m_executor.start_save_graph();
-}
-
-bool t_command_parser_executor::stop_save_graph(const std::vector<std::string>& args)
-{
- if (!args.empty()) return false;
- return m_executor.stop_save_graph();
+ return m_executor.in_peers(set, limit);
}
bool t_command_parser_executor::hard_fork_info(const std::vector<std::string>& args)
@@ -596,6 +590,13 @@ bool t_command_parser_executor::unban(const std::vector<std::string>& args)
return m_executor.unban(ip);
}
+bool t_command_parser_executor::banned(const std::vector<std::string>& args)
+{
+ if (args.size() != 1) return false;
+ std::string address = args[0];
+ return m_executor.banned(address);
+}
+
bool t_command_parser_executor::flush_txpool(const std::vector<std::string>& args)
{
if (args.size() > 1) return false;
@@ -673,11 +674,38 @@ bool t_command_parser_executor::alt_chain_info(const std::vector<std::string>& a
{
if(args.size() > 1)
{
- std::cout << "usage: alt_chain_info [block_hash]" << std::endl;
+ std::cout << "usage: alt_chain_info [block_hash|>N|-N]" << std::endl;
return false;
}
- return m_executor.alt_chain_info(args.size() == 1 ? args[0] : "");
+ std::string tip;
+ size_t above = 0;
+ uint64_t last_blocks = 0;
+ if (args.size() == 1)
+ {
+ if (args[0].size() > 0 && args[0][0] == '>')
+ {
+ if (!epee::string_tools::get_xtype_from_string(above, args[0].c_str() + 1))
+ {
+ std::cout << "invalid above parameter" << std::endl;
+ return false;
+ }
+ }
+ else if (args[0].size() > 0 && args[0][0] == '-')
+ {
+ if (!epee::string_tools::get_xtype_from_string(last_blocks, args[0].c_str() + 1))
+ {
+ std::cout << "invalid last_blocks parameter" << std::endl;
+ return false;
+ }
+ }
+ else
+ {
+ tip = args[0];
+ }
+ }
+
+ return m_executor.alt_chain_info(tip, above, last_blocks);
}
bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector<std::string>& args)
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 2efd78ec0..d39bc1c9b 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -115,10 +115,6 @@ public:
bool in_peers(const std::vector<std::string>& args);
- bool start_save_graph(const std::vector<std::string>& args);
-
- bool stop_save_graph(const std::vector<std::string>& args);
-
bool hard_fork_info(const std::vector<std::string>& args);
bool show_bans(const std::vector<std::string>& args);
@@ -127,6 +123,8 @@ public:
bool unban(const std::vector<std::string>& args);
+ bool banned(const std::vector<std::string>& args);
+
bool flush_txpool(const std::vector<std::string>& args);
bool output_histogram(const std::vector<std::string>& args);
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index f665eec9c..aecdda52c 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -215,16 +215,6 @@ t_command_server::t_command_server(
, "Set the <max_number> of in peers."
);
m_command_lookup.set_handler(
- "start_save_graph"
- , std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1)
- , "Start saving data for dr monero."
- );
- m_command_lookup.set_handler(
- "stop_save_graph"
- , std::bind(&t_command_parser_executor::stop_save_graph, &m_parser, p::_1)
- , "Stop saving data for dr monero."
- );
- m_command_lookup.set_handler(
"hard_fork_info"
, std::bind(&t_command_parser_executor::hard_fork_info, &m_parser, p::_1)
, "Print the hard fork voting information."
@@ -243,10 +233,16 @@ t_command_server::t_command_server(
m_command_lookup.set_handler(
"unban"
, std::bind(&t_command_parser_executor::unban, &m_parser, p::_1)
- , "unban <IP>"
+ , "unban <address>"
, "Unban a given <IP>."
);
m_command_lookup.set_handler(
+ "banned"
+ , std::bind(&t_command_parser_executor::banned, &m_parser, p::_1)
+ , "banned <address>"
+ , "Check whether an <address> is banned."
+ );
+ m_command_lookup.set_handler(
"flush_txpool"
, std::bind(&t_command_parser_executor::flush_txpool, &m_parser, p::_1)
, "flush_txpool [<txid>]"
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 5084b6283..cb96b37b6 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -105,6 +105,7 @@ t_daemon::t_daemon(
{
zmq_rpc_bind_port = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_port);
zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip);
+ zmq_rpc_disabled = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_disabled);
}
t_daemon::~t_daemon() = default;
@@ -171,25 +172,30 @@ bool t_daemon::run(bool interactive)
cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get());
cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler);
- if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port))
+ if (!zmq_rpc_disabled)
{
- LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address
- + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server");
+ if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port))
+ {
+ LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address
+ + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server");
- if (rpc_commands)
- rpc_commands->stop_handling();
+ if (rpc_commands)
+ rpc_commands->stop_handling();
- for(auto& rpc : mp_internals->rpcs)
- rpc->stop();
+ for(auto& rpc : mp_internals->rpcs)
+ rpc->stop();
- return false;
- }
+ return false;
+ }
- MINFO("Starting ZMQ server...");
- zmq_server.run();
+ MINFO("Starting ZMQ server...");
+ zmq_server.run();
- MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address
- + ":" + zmq_rpc_bind_port + ".");
+ MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address
+ + ":" + zmq_rpc_bind_port + ".");
+ }
+ else
+ MINFO("ZMQ server disabled");
if (public_rpc_port > 0)
{
@@ -202,7 +208,8 @@ bool t_daemon::run(bool interactive)
if (rpc_commands)
rpc_commands->stop_handling();
- zmq_server.stop();
+ if (!zmq_rpc_disabled)
+ zmq_server.stop();
for(auto& rpc : mp_internals->rpcs)
rpc->stop();
diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h
index d44173177..c0efb68ee 100644
--- a/src/daemon/daemon.h
+++ b/src/daemon/daemon.h
@@ -46,6 +46,7 @@ private:
uint16_t public_rpc_port;
std::string zmq_rpc_bind_address;
std::string zmq_rpc_bind_port;
+ bool zmq_rpc_disabled;
public:
t_daemon(
boost::program_options::variables_map const & vm,
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 690d4d60e..461888062 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -141,6 +141,7 @@ int main(int argc, char const * argv[])
command_line::add_arg(core_settings, daemon_args::arg_public_node);
command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_ip);
command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_bind_port);
+ command_line::add_arg(core_settings, daemon_args::arg_zmq_rpc_disabled);
daemonizer::init_options(hidden_options, visible_options);
daemonize::t_executor::init_options(core_settings);
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index cca0f75f9..4d3debed6 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -35,6 +35,7 @@
#include "daemon/rpc_command_executor.h"
#include "rpc/core_rpc_server_commands_defs.h"
#include "cryptonote_core/cryptonote_core.h"
+#include "cryptonote_basic/difficulty.h"
#include "cryptonote_basic/hardfork.h"
#include <boost/format.hpp>
#include <ctime>
@@ -46,21 +47,30 @@
namespace daemonize {
namespace {
+ const char *get_address_type_name(epee::net_utils::address_type address_type)
+ {
+ switch (address_type)
+ {
+ default:
+ case epee::net_utils::address_type::invalid: return "invalid";
+ case epee::net_utils::address_type::ipv4: return "IPv4";
+ case epee::net_utils::address_type::ipv6: return "IPv6";
+ case epee::net_utils::address_type::i2p: return "I2P";
+ case epee::net_utils::address_type::tor: return "Tor";
+ }
+ }
+
void print_peer(std::string const & prefix, cryptonote::peer const & peer)
{
time_t now;
time(&now);
time_t last_seen = static_cast<time_t>(peer.last_seen);
- std::string id_str;
- std::string port_str;
std::string elapsed = epee::misc_utils::get_time_interval_string(now - last_seen);
- std::string ip_str = epee::string_tools::get_ip_string_from_int32(peer.ip);
- std::stringstream peer_id_str;
- peer_id_str << std::hex << std::setw(16) << peer.id;
- peer_id_str >> id_str;
+ std::string id_str = epee::string_tools::pad_string(epee::string_tools::to_string_hex(peer.id), 16, '0', true);
+ std::string port_str;
epee::string_tools::xtype_to_string(peer.port, port_str);
- std::string addr_str = ip_str + ":" + port_str;
+ std::string addr_str = peer.host + ":" + port_str;
std::string rpc_port = peer.rpc_port ? std::to_string(peer.rpc_port) : "-";
std::string pruning_seed = epee::string_tools::to_string_hex(peer.pruning_seed);
tools::msg_writer() << boost::format("%-10s %-25s %-25s %-5s %-4s %s") % prefix % id_str % addr_str % rpc_port % pruning_seed % elapsed;
@@ -76,7 +86,7 @@ namespace {
<< "height: " << boost::lexical_cast<std::string>(header.height) << std::endl
<< "depth: " << boost::lexical_cast<std::string>(header.depth) << std::endl
<< "hash: " << header.hash << std::endl
- << "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl
+ << "difficulty: " << header.wide_difficulty << std::endl
<< "POW hash: " << header.pow_hash << std::endl
<< "block size: " << header.block_size << std::endl
<< "block weight: " << header.block_weight << std::endl
@@ -208,6 +218,9 @@ bool t_rpc_command_executor::print_peer_list_stats() {
cryptonote::COMMAND_RPC_GET_PEER_LIST::response res;
std::string failure_message = "Couldn't retrieve peer list";
+
+ req.public_only = false;
+
if (m_is_rpc)
{
if (!m_rpc_client->rpc_request(req, res, "/get_peer_list", failure_message.c_str()))
@@ -337,18 +350,41 @@ bool t_rpc_command_executor::show_difficulty() {
tools::success_msg_writer() << "BH: " << res.height
<< ", TH: " << res.top_block_hash
- << ", DIFF: " << res.difficulty
- << ", HR: " << res.difficulty / res.target << " H/s";
+ << ", DIFF: " << res.wide_difficulty
+ << ", CUM_DIFF: " << res.wide_cumulative_difficulty
+ << ", HR: " << cryptonote::difficulty_type(res.wide_difficulty) / res.target << " H/s";
return true;
}
-static std::string get_mining_speed(uint64_t hr)
+static void get_metric_prefix(cryptonote::difficulty_type hr, double& hr_d, char& prefix)
+{
+ if (hr < 1000)
+ {
+ prefix = 0;
+ return;
+ }
+ static const char metric_prefixes[4] = { 'k', 'M', 'G', 'T' };
+ for (size_t i = 0; i < sizeof(metric_prefixes); ++i)
+ {
+ if (hr < 1000000)
+ {
+ hr_d = hr.convert_to<double>() / 1000;
+ prefix = metric_prefixes[i];
+ return;
+ }
+ hr /= 1000;
+ }
+ prefix = 0;
+}
+
+static std::string get_mining_speed(cryptonote::difficulty_type hr)
{
- if (hr>1e9) return (boost::format("%.2f GH/s") % (hr/1e9)).str();
- if (hr>1e6) return (boost::format("%.2f MH/s") % (hr/1e6)).str();
- if (hr>1e3) return (boost::format("%.2f kH/s") % (hr/1e3)).str();
- return (boost::format("%.0f H/s") % hr).str();
+ double hr_d;
+ char prefix;
+ get_metric_prefix(hr, hr_d, prefix);
+ if (prefix == 0) return (boost::format("%.0f H/s") % hr).str();
+ return (boost::format("%.2f %cH/s") % hr_d % prefix).str();
}
static std::string get_fork_extra_info(uint64_t t, uint64_t now, uint64_t block_time)
@@ -465,7 +501,7 @@ bool t_rpc_command_executor::show_status() {
% (ires.testnet ? "testnet" : ires.stagenet ? "stagenet" : "mainnet")
% bootstrap_msg
% (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed)) : "not mining")
- % get_mining_speed(ires.difficulty / ires.target)
+ % get_mining_speed(cryptonote::difficulty_type(ires.wide_difficulty) / ires.target)
% (unsigned)hfres.version
% get_fork_extra_info(hfres.earliest_height, net_height, ires.target)
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
@@ -589,6 +625,7 @@ bool t_rpc_command_executor::print_connections() {
}
tools::msg_writer() << std::setw(30) << std::left << "Remote Host"
+ << std::setw(8) << "Type"
<< std::setw(6) << "SSL"
<< std::setw(20) << "Peer id"
<< std::setw(20) << "Support Flags"
@@ -609,6 +646,7 @@ bool t_rpc_command_executor::print_connections() {
tools::msg_writer()
//<< std::setw(30) << std::left << in_out
<< std::setw(30) << std::left << address
+ << std::setw(8) << (get_address_type_name((epee::net_utils::address_type)info.address_type))
<< std::setw(6) << (info.ssl ? "yes" : "no")
<< std::setw(20) << epee::string_tools::pad_string(info.peer_id, 16, '0', true)
<< std::setw(20) << info.support_flags
@@ -726,7 +764,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
<< ", size: " << header.block_size << ", weight: " << header.block_weight << " (long term " << header.long_term_weight << "), transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
- << "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
+ << "difficulty: " << header.wide_difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
first = false;
}
@@ -1466,13 +1504,14 @@ bool t_rpc_command_executor::get_limit_down()
return true;
}
-bool t_rpc_command_executor::out_peers(uint64_t limit)
+bool t_rpc_command_executor::out_peers(bool set, uint32_t limit)
{
cryptonote::COMMAND_RPC_OUT_PEERS::request req;
cryptonote::COMMAND_RPC_OUT_PEERS::response res;
epee::json_rpc::error error_resp;
+ req.set = set;
req.out_peers = limit;
std::string fail_message = "Unsuccessful";
@@ -1493,18 +1532,20 @@ bool t_rpc_command_executor::out_peers(uint64_t limit)
}
}
- tools::msg_writer() << "Max number of out peers set to " << limit << std::endl;
+ const std::string s = res.out_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.out_peers);
+ tools::msg_writer() << "Max number of out peers set to " << s << std::endl;
return true;
}
-bool t_rpc_command_executor::in_peers(uint64_t limit)
+bool t_rpc_command_executor::in_peers(bool set, uint32_t limit)
{
cryptonote::COMMAND_RPC_IN_PEERS::request req;
cryptonote::COMMAND_RPC_IN_PEERS::response res;
epee::json_rpc::error error_resp;
+ req.set = set;
req.in_peers = limit;
std::string fail_message = "Unsuccessful";
@@ -1525,64 +1566,12 @@ bool t_rpc_command_executor::in_peers(uint64_t limit)
}
}
- tools::msg_writer() << "Max number of in peers set to " << limit << std::endl;
+ const std::string s = res.in_peers == (uint32_t)-1 ? "unlimited" : std::to_string(res.in_peers);
+ tools::msg_writer() << "Max number of in peers set to " << s << std::endl;
return true;
}
-bool t_rpc_command_executor::start_save_graph()
-{
- cryptonote::COMMAND_RPC_START_SAVE_GRAPH::request req;
- cryptonote::COMMAND_RPC_START_SAVE_GRAPH::response res;
- std::string fail_message = "Unsuccessful";
-
- if (m_is_rpc)
- {
- if (!m_rpc_client->rpc_request(req, res, "/start_save_graph", fail_message.c_str()))
- {
- return true;
- }
- }
-
- else
- {
- if (!m_rpc_server->on_start_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK)
- {
- tools::fail_msg_writer() << make_error(fail_message, res.status);
- return true;
- }
- }
-
- tools::success_msg_writer() << "Saving graph is now on";
- return true;
-}
-
-bool t_rpc_command_executor::stop_save_graph()
-{
- cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::request req;
- cryptonote::COMMAND_RPC_STOP_SAVE_GRAPH::response res;
- std::string fail_message = "Unsuccessful";
-
- if (m_is_rpc)
- {
- if (!m_rpc_client->rpc_request(req, res, "/stop_save_graph", fail_message.c_str()))
- {
- return true;
- }
- }
-
- else
- {
- if (!m_rpc_server->on_stop_save_graph(req, res) || res.status != CORE_RPC_STATUS_OK)
- {
- tools::fail_msg_writer() << make_error(fail_message, res.status);
- return true;
- }
- }
- tools::success_msg_writer() << "Saving graph is now off";
- return true;
-}
-
bool t_rpc_command_executor::hard_fork_info(uint8_t version)
{
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req;
@@ -1641,14 +1630,14 @@ bool t_rpc_command_executor::print_bans()
for (auto i = res.bans.begin(); i != res.bans.end(); ++i)
{
- tools::msg_writer() << epee::string_tools::get_ip_string_from_int32(i->ip) << " banned for " << i->seconds << " seconds";
+ tools::msg_writer() << i->host << " banned for " << i->seconds << " seconds";
}
return true;
}
-bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
+bool t_rpc_command_executor::ban(const std::string &address, time_t seconds)
{
cryptonote::COMMAND_RPC_SETBANS::request req;
cryptonote::COMMAND_RPC_SETBANS::response res;
@@ -1656,11 +1645,8 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
epee::json_rpc::error error_resp;
cryptonote::COMMAND_RPC_SETBANS::ban ban;
- if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip))
- {
- tools::fail_msg_writer() << "Invalid IP";
- return true;
- }
+ ban.host = address;
+ ban.ip = 0;
ban.ban = true;
ban.seconds = seconds;
req.bans.push_back(ban);
@@ -1684,7 +1670,7 @@ bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds)
return true;
}
-bool t_rpc_command_executor::unban(const std::string &ip)
+bool t_rpc_command_executor::unban(const std::string &address)
{
cryptonote::COMMAND_RPC_SETBANS::request req;
cryptonote::COMMAND_RPC_SETBANS::response res;
@@ -1692,11 +1678,8 @@ bool t_rpc_command_executor::unban(const std::string &ip)
epee::json_rpc::error error_resp;
cryptonote::COMMAND_RPC_SETBANS::ban ban;
- if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip))
- {
- tools::fail_msg_writer() << "Invalid IP";
- return true;
- }
+ ban.host = address;
+ ban.ip = 0;
ban.ban = false;
ban.seconds = 0;
req.bans.push_back(ban);
@@ -1720,6 +1703,39 @@ bool t_rpc_command_executor::unban(const std::string &ip)
return true;
}
+bool t_rpc_command_executor::banned(const std::string &address)
+{
+ cryptonote::COMMAND_RPC_BANNED::request req;
+ cryptonote::COMMAND_RPC_BANNED::response res;
+ std::string fail_message = "Unsuccessful";
+ epee::json_rpc::error error_resp;
+
+ req.address = address;
+
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(req, res, "banned", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_banned(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+
+ if (res.banned)
+ tools::msg_writer() << address << " is banned for " << res.seconds << " seconds";
+ else
+ tools::msg_writer() << address << " is not banned";
+
+ return true;
+}
+
bool t_rpc_command_executor::flush_txpool(const std::string &txid)
{
cryptonote::COMMAND_RPC_FLUSH_TRANSACTION_POOL::request req;
@@ -1824,7 +1840,7 @@ bool t_rpc_command_executor::print_coinbase_tx_sum(uint64_t height, uint64_t cou
return true;
}
-bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
+bool t_rpc_command_executor::alt_chain_info(const std::string &tip, size_t above, uint64_t last_blocks)
{
cryptonote::COMMAND_RPC_GET_INFO::request ireq;
cryptonote::COMMAND_RPC_GET_INFO::response ires;
@@ -1861,16 +1877,31 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
if (tip.empty())
{
- tools::msg_writer() << boost::lexical_cast<std::string>(res.chains.size()) << " alternate chains found:";
- for (const auto &chain: res.chains)
+ auto chains = res.chains;
+ std::sort(chains.begin(), chains.end(), [](const cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info0, cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info1){ return info0.height < info1.height; });
+ std::vector<size_t> display;
+ for (size_t i = 0; i < chains.size(); ++i)
{
- uint64_t start_height = (chain.height - chain.length + 1);
+ const auto &chain = chains[i];
+ if (chain.length <= above)
+ continue;
+ const uint64_t start_height = (chain.height - chain.length + 1);
+ if (last_blocks > 0 && ires.height - 1 - start_height >= last_blocks)
+ continue;
+ display.push_back(i);
+ }
+ tools::msg_writer() << boost::lexical_cast<std::string>(display.size()) << " alternate chains found:";
+ for (const size_t idx: display)
+ {
+ const auto &chain = chains[idx];
+ const uint64_t start_height = (chain.height - chain.length + 1);
tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
- << " deep), diff " << chain.difficulty << ": " << chain.block_hash;
+ << " deep), diff " << chain.wide_difficulty << ": " << chain.block_hash;
}
}
else
{
+ const uint64_t now = time(NULL);
const auto i = std::find_if(res.chains.begin(), res.chains.end(), [&tip](cryptonote::COMMAND_RPC_GET_ALTERNATE_CHAINS::chain_info &info){ return info.block_hash == tip; });
if (i != res.chains.end())
{
@@ -1878,10 +1909,53 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
tools::success_msg_writer() << "Found alternate chain with tip " << tip;
uint64_t start_height = (chain.height - chain.length + 1);
tools::msg_writer() << chain.length << " blocks long, from height " << start_height << " (" << (ires.height - start_height - 1)
- << " deep), diff " << chain.difficulty << ":";
+ << " deep), diff " << chain.wide_difficulty << ":";
for (const std::string &block_id: chain.block_hashes)
tools::msg_writer() << " " << block_id;
tools::msg_writer() << "Chain parent on main chain: " << chain.main_chain_parent_block;
+ cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request bhreq;
+ cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response bhres;
+ bhreq.hashes = chain.block_hashes;
+ bhreq.hashes.push_back(chain.main_chain_parent_block);
+ bhreq.fill_pow_hash = false;
+ if (m_is_rpc)
+ {
+ if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheaderbyhash", fail_message.c_str()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!m_rpc_server->on_get_block_header_by_hash(bhreq, bhres, error_resp))
+ {
+ tools::fail_msg_writer() << make_error(fail_message, res.status);
+ return true;
+ }
+ }
+ if (bhres.block_headers.size() != chain.length + 1)
+ {
+ tools::fail_msg_writer() << "Failed to get block header info for alt chain";
+ return true;
+ }
+ uint64_t t0 = bhres.block_headers.front().timestamp, t1 = t0;
+ for (const cryptonote::block_header_response &block_header: bhres.block_headers)
+ {
+ t0 = std::min<uint64_t>(t0, block_header.timestamp);
+ t1 = std::max<uint64_t>(t1, block_header.timestamp);
+ }
+ const uint64_t dt = t1 - t0;
+ const uint64_t age = std::max(dt, t0 < now ? now - t0 : 0);
+ tools::msg_writer() << "Age: " << tools::get_human_readable_timespan(age);
+ if (chain.length > 1)
+ {
+ tools::msg_writer() << "Time span: " << tools::get_human_readable_timespan(dt);
+ cryptonote::difficulty_type start_difficulty = bhres.block_headers.back().difficulty;
+ if (start_difficulty > 0)
+ tools::msg_writer() << "Approximated " << 100.f * DIFFICULTY_TARGET_V2 * chain.length / dt << "% of network hash rate";
+ else
+ tools::fail_msg_writer() << "Bad cmumulative difficulty reported by dameon";
+ }
}
else
tools::fail_msg_writer() << "Block hash " << tip << " is not the tip of any known alternate chain";
@@ -1939,7 +2013,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
}
}
- tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.difficulty << ", cum. diff " << ires.cumulative_difficulty
+ tools::msg_writer() << "Height: " << ires.height << ", diff " << ires.wide_difficulty << ", cum. diff " << ires.wide_cumulative_difficulty
<< ", target " << ires.target << " sec" << ", dyn fee " << cryptonote::print_money(feres.fee) << "/" << (hfres.enabled ? "byte" : "kB");
if (nblocks > 0)
@@ -1966,7 +2040,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
}
}
- double avgdiff = 0;
+ cryptonote::difficulty_type avgdiff = 0;
double avgnumtxes = 0;
double avgreward = 0;
std::vector<uint64_t> weights;
@@ -1975,7 +2049,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0);
for (const auto &bhr: bhres.headers)
{
- avgdiff += bhr.difficulty;
+ avgdiff += cryptonote::difficulty_type(bhr.wide_difficulty);
avgnumtxes += bhr.num_txes;
avgreward += bhr.reward;
weights.push_back(bhr.block_weight);
@@ -1990,7 +2064,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
avgnumtxes /= nblocks;
avgreward /= nblocks;
uint64_t median_block_weight = epee::misc_utils::median(weights);
- tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes
+ tools::msg_writer() << "Last " << nblocks << ": avg. diff " << avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes
<< ", avg. reward " << cryptonote::print_money(avgreward) << ", median block weight " << median_block_weight;
unsigned int max_major = 256, max_minor = 256;
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index df2894d09..f3ed48319 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -125,21 +125,19 @@ public:
bool set_limit(int64_t limit_down, int64_t limit_up);
- bool out_peers(uint64_t limit);
+ bool out_peers(bool set, uint32_t limit);
- bool in_peers(uint64_t limit);
+ bool in_peers(bool set, uint32_t limit);
- bool start_save_graph();
-
- bool stop_save_graph();
-
bool hard_fork_info(uint8_t version);
bool print_bans();
- bool ban(const std::string &ip, time_t seconds);
+ bool ban(const std::string &address, time_t seconds);
- bool unban(const std::string &ip);
+ bool unban(const std::string &address);
+
+ bool banned(const std::string &address);
bool flush_txpool(const std::string &txid);
@@ -147,7 +145,7 @@ public:
bool print_coinbase_tx_sum(uint64_t height, uint64_t count);
- bool alt_chain_info(const std::string &tip);
+ bool alt_chain_info(const std::string &tip, size_t above, uint64_t last_blocks);
bool print_blockchain_dynamic_stats(uint64_t nblocks);
diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt
index 7bc2c324f..03c2b3e20 100644
--- a/src/debug_utilities/CMakeLists.txt
+++ b/src/debug_utilities/CMakeLists.txt
@@ -69,3 +69,25 @@ set_property(TARGET object_sizes
PROPERTY
OUTPUT_NAME "monero-utils-object-sizes")
+
+set(dns_checks_sources
+ dns_checks.cpp
+ )
+
+monero_add_executable(dns_checks
+ ${dns_checks_sources}
+ ${dns_checks_private_headers})
+
+target_link_libraries(dns_checks
+ LINK_PRIVATE
+ common
+ epee
+ version
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_SYSTEM_LIBRARY}
+ ${CMAKE_THREAD_LIBS_INIT})
+
+set_property(TARGET dns_checks
+ PROPERTY
+ OUTPUT_NAME "monero-utils-dns-checks")
+
diff --git a/src/debug_utilities/dns_checks.cpp b/src/debug_utilities/dns_checks.cpp
new file mode 100644
index 000000000..3c9daa769
--- /dev/null
+++ b/src/debug_utilities/dns_checks.cpp
@@ -0,0 +1,149 @@
+// Copyright (c) 2019, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <boost/program_options.hpp>
+#include "misc_log_ex.h"
+#include "common/util.h"
+#include "common/command_line.h"
+#include "common/dns_utils.h"
+#include "version.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "debugtools.dnschecks"
+
+namespace po = boost::program_options;
+
+enum lookup_t { LOOKUP_A, LOOKUP_TXT };
+
+static std::vector<std::string> lookup(lookup_t type, const char *hostname)
+{
+ bool dnssec_available = false, dnssec_valid = false;
+ std::vector<std::string> res;
+ switch (type)
+ {
+ case LOOKUP_A: res = tools::DNSResolver::instance().get_ipv4(hostname, dnssec_available, dnssec_valid); break;
+ case LOOKUP_TXT: res = tools::DNSResolver::instance().get_txt_record(hostname, dnssec_available, dnssec_valid); break;
+ default: MERROR("Invalid lookup type: " << (int)type); return {};
+ }
+ if (!dnssec_available)
+ {
+ MWARNING("No DNSSEC for " << hostname);
+ return {};
+ }
+ if (!dnssec_valid)
+ {
+ MWARNING("Invalid DNSSEC check for " << hostname);
+ return {};
+ }
+ MINFO(res.size() << " valid signed result(s) for " << hostname);
+ return res;
+}
+
+static void lookup(lookup_t type, const std::vector<std::string> hostnames)
+{
+ std::vector<std::vector<std::string>> results;
+ for (const std::string &hostname: hostnames)
+ {
+ auto res = lookup(type, hostname.c_str());
+ if (!res.empty())
+ {
+ std::sort(res.begin(), res.end());
+ results.push_back(res);
+ }
+ }
+ std::map<std::vector<std::string>, size_t> counter;
+ for (const auto &e: results)
+ counter[e]++;
+ size_t count = 0;
+ for (const auto &e: counter)
+ count = std::max(count, e.second);
+ if (results.size() > 1)
+ {
+ if (count < results.size())
+ MERROR("Only " << count << "/" << results.size() << " records match");
+ else
+ MINFO(count << "/" << results.size() << " records match");
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ TRY_ENTRY();
+
+ tools::on_startup();
+
+ po::options_description desc_cmd_only("Command line options");
+ po::options_description desc_cmd_sett("Command line options and settings options");
+
+ command_line::add_arg(desc_cmd_only, command_line::arg_help);
+
+ po::options_description desc_options("Allowed options");
+ desc_options.add(desc_cmd_only).add(desc_cmd_sett);
+
+ po::variables_map vm;
+ bool r = command_line::handle_error_helper(desc_options, [&]()
+ {
+ po::store(po::parse_command_line(argc, argv, desc_options), vm);
+ po::notify(vm);
+ return true;
+ });
+ if (! r)
+ return 1;
+
+ if (command_line::get_arg(vm, command_line::arg_help))
+ {
+ std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL;
+ std::cout << desc_options << std::endl;
+ return 1;
+ }
+
+ mlog_configure("", true);
+ mlog_set_categories("+" MONERO_DEFAULT_LOG_CATEGORY ":INFO");
+
+ lookup(LOOKUP_A, {"seeds.moneroseeds.se", "seeds.moneroseeds.ae.org", "seeds.moneroseeds.ch", "seeds.moneroseeds.li"});
+
+ lookup(LOOKUP_TXT, {"updates.moneropulse.org", "updates.moneropulse.net", "updates.moneropulse.co", "updates.moneropulse.se"});
+
+ lookup(LOOKUP_TXT, {"checkpoints.moneropulse.org", "checkpoints.moneropulse.net", "checkpoints.moneropulse.co", "checkpoints.moneropulse.se"});
+
+ // those are in the code, but don't seem to actually exist
+#if 0
+ lookup(LOOKUP_TXT, {"testpoints.moneropulse.org", "testpoints.moneropulse.net", "testpoints.moneropulse.co", "testpoints.moneropulse.se");
+
+ lookup(LOOKUP_TXT, {"stagenetpoints.moneropulse.org", "stagenetpoints.moneropulse.net", "stagenetpoints.moneropulse.co", "stagenetpoints.moneropulse.se"});
+#endif
+
+ lookup(LOOKUP_TXT, {"segheights.moneropulse.org", "segheights.moneropulse.net", "segheights.moneropulse.co", "segheights.moneropulse.se"});
+
+ return 0;
+ CATCH_ENTRY_L0("main", 1);
+}
diff --git a/src/device/device.hpp b/src/device/device.hpp
index 866e2c676..215e97eb6 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -236,6 +236,7 @@ namespace hw {
virtual bool compute_key_image(const cryptonote::account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const cryptonote::subaddress_index& received_index, cryptonote::keypair& in_ephemeral, crypto::key_image& ki) { return false; }
virtual void computing_key_images(bool started) {};
virtual void set_network_type(cryptonote::network_type network_type) { }
+ virtual void display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) {}
protected:
device_mode mode;
diff --git a/src/device/device_cold.hpp b/src/device/device_cold.hpp
index 31b1504ab..22708c46a 100644
--- a/src/device/device_cold.hpp
+++ b/src/device/device_cold.hpp
@@ -47,6 +47,7 @@ namespace hw {
std::vector<cryptonote::address_parse_info> tx_recipients; // as entered by user
boost::optional<int> bp_version; // BP version to use
boost::optional<unsigned> client_version; // Signing client version to use (testing)
+ boost::optional<uint8_t> hard_fork; // hard fork being used for the transaction
};
class device_cold {
diff --git a/src/device_trezor/device_trezor.cpp b/src/device_trezor/device_trezor.cpp
index b1022dd9c..a77f6697f 100644
--- a/src/device_trezor/device_trezor.cpp
+++ b/src/device_trezor/device_trezor.cpp
@@ -200,6 +200,10 @@ namespace trezor {
}
}
+ void device_trezor::display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) {
+ get_address(index, payment_id, true);
+ }
+
/* ======================================================================= */
/* Helpers */
/* ======================================================================= */
@@ -209,8 +213,12 @@ namespace trezor {
/* ======================================================================= */
std::shared_ptr<messages::monero::MoneroAddress> device_trezor::get_address(
+ const boost::optional<cryptonote::subaddress_index> & subaddress,
+ const boost::optional<crypto::hash8> & payment_id,
+ bool show_address,
const boost::optional<std::vector<uint32_t>> & path,
const boost::optional<cryptonote::network_type> & network_type){
+ CHECK_AND_ASSERT_THROW_MES(!payment_id || !subaddress || subaddress->is_zero(), "Subaddress cannot be integrated");
TREZOR_AUTO_LOCK_CMD();
require_connected();
device_state_reset_unsafe();
@@ -218,6 +226,14 @@ namespace trezor {
auto req = std::make_shared<messages::monero::MoneroGetAddress>();
this->set_msg_addr<messages::monero::MoneroGetAddress>(req.get(), path, network_type);
+ req->set_show_display(show_address);
+ if (subaddress){
+ req->set_account(subaddress->major);
+ req->set_minor(subaddress->minor);
+ }
+ if (payment_id){
+ req->set_payment_id(std::string(payment_id->data, 8));
+ }
auto response = this->client_exchange<messages::monero::MoneroAddress>(req);
MTRACE("Get address response received");
diff --git a/src/device_trezor/device_trezor.hpp b/src/device_trezor/device_trezor.hpp
index 0e91847dc..a26a42788 100644
--- a/src/device_trezor/device_trezor.hpp
+++ b/src/device_trezor/device_trezor.hpp
@@ -110,6 +110,7 @@ namespace trezor {
/* ======================================================================= */
bool get_public_address(cryptonote::account_public_address &pubkey) override;
bool get_secret_keys(crypto::secret_key &viewkey , crypto::secret_key &spendkey) override;
+ void display_address(const cryptonote::subaddress_index& index, const boost::optional<crypto::hash8> &payment_id) override;
/* ======================================================================= */
/* TREZOR PROTOCOL */
@@ -119,6 +120,9 @@ namespace trezor {
* Get address. Throws.
*/
std::shared_ptr<messages::monero::MoneroAddress> get_address(
+ const boost::optional<cryptonote::subaddress_index> & subaddress = boost::none,
+ const boost::optional<crypto::hash8> & payment_id = boost::none,
+ bool show_address = false,
const boost::optional<std::vector<uint32_t>> & path = boost::none,
const boost::optional<cryptonote::network_type> & network_type = boost::none);
diff --git a/src/device_trezor/trezor/protocol.cpp b/src/device_trezor/trezor/protocol.cpp
index 5fe08abbe..61e51be14 100644
--- a/src/device_trezor/trezor/protocol.cpp
+++ b/src/device_trezor/trezor/protocol.cpp
@@ -27,6 +27,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
+#include "version.h"
#include "protocol.hpp"
#include <unordered_map>
#include <set>
@@ -502,6 +503,8 @@ namespace tx {
tsx_data.set_num_inputs(static_cast<google::protobuf::uint32>(tx.sources.size()));
tsx_data.set_mixin(static_cast<google::protobuf::uint32>(tx.sources[0].outputs.size() - 1));
tsx_data.set_account(tx.subaddr_account);
+ tsx_data.set_monero_version(std::string(MONERO_VERSION) + "|" + MONERO_VERSION_TAG);
+ tsx_data.set_hard_fork(m_aux_data->hard_fork ? m_aux_data->hard_fork.get() : 0);
assign_to_repeatable(tsx_data.mutable_minor_indices(), tx.subaddr_indices.begin(), tx.subaddr_indices.end());
// Rsig decision
diff --git a/src/net/error.h b/src/net/error.h
index c8338f7e2..7c852dd20 100644
--- a/src/net/error.h
+++ b/src/net/error.h
@@ -42,7 +42,8 @@ namespace net
invalid_i2p_address,
invalid_port, //!< Outside of 0-65535 range
invalid_tor_address,//!< Invalid base32 or length
- unsupported_address //!< Type not supported by `get_network_address`
+ unsupported_address,//!< Type not supported by `get_network_address`
+ invalid_mask, //!< Outside of 0-32 range
};
//! \return `std::error_category` for `net` namespace.
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index eaaadb67e..7b74f2b75 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -34,28 +34,92 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
+ {
+ // require ipv6 address format "[addr:addr:addr:...:addr]:port"
+ if (address.find(']') != std::string::npos)
+ {
+ host = address.substr(1, address.rfind(']') - 1);
+ if ((host.size() + 2) < address.size())
+ {
+ port = address.substr(address.rfind(':') + 1);
+ }
+ }
+ else
+ {
+ host = address.substr(0, address.rfind(':'));
+ if (host.size() < address.size())
+ {
+ port = address.substr(host.size() + 1);
+ }
+ }
+ }
+
expect<epee::net_utils::network_address>
get_network_address(const boost::string_ref address, const std::uint16_t default_port)
{
- const boost::string_ref host = address.substr(0, address.rfind(':'));
+ std::string host_str = "";
+ std::string port_str = "";
- if (host.empty())
+ bool ipv6 = false;
+
+ get_network_address_host_and_port(std::string(address), host_str, port_str);
+
+ boost::string_ref host_str_ref(host_str);
+ boost::string_ref port_str_ref(port_str);
+
+ if (host_str.empty())
return make_error_code(net::error::invalid_host);
- if (host.ends_with(".onion"))
+ if (host_str_ref.ends_with(".onion"))
return tor_address::make(address, default_port);
- if (host.ends_with(".i2p"))
+ if (host_str_ref.ends_with(".i2p"))
return i2p_address::make(address, default_port);
+ boost::system::error_code ec;
+ boost::asio::ip::address_v6 v6 = boost::asio::ip::make_address_v6(host_str, ec);
+ ipv6 = !ec;
+
std::uint16_t port = default_port;
- if (host.size() < address.size())
+ if (port_str.size())
{
- if (!epee::string_tools::get_xtype_from_string(port, std::string{address.substr(host.size() + 1)}))
+ if (!epee::string_tools::get_xtype_from_string(port, port_str))
return make_error_code(net::error::invalid_port);
}
- std::uint32_t ip = 0;
- if (epee::string_tools::get_ip_int32_from_string(ip, std::string{host}))
- return {epee::net_utils::ipv4_network_address{ip, port}};
+ if (ipv6)
+ {
+ return {epee::net_utils::ipv6_network_address{v6, port}};
+ }
+ else
+ {
+ std::uint32_t ip = 0;
+ if (epee::string_tools::get_ip_int32_from_string(ip, host_str))
+ return {epee::net_utils::ipv4_network_address{ip, port}};
+ }
+
return make_error_code(net::error::unsupported_address);
}
+
+ expect<epee::net_utils::ipv4_network_subnet>
+ get_ipv4_subnet_address(const boost::string_ref address, bool allow_implicit_32)
+ {
+ uint32_t mask = 32;
+ const boost::string_ref::size_type slash = address.find_first_of('/');
+ if (slash != boost::string_ref::npos)
+ {
+ if (!epee::string_tools::get_xtype_from_string(mask, std::string{address.substr(slash + 1)}))
+ return make_error_code(net::error::invalid_mask);
+ if (mask > 32)
+ return make_error_code(net::error::invalid_mask);
+ }
+ else if (!allow_implicit_32)
+ return make_error_code(net::error::invalid_mask);
+
+ std::uint32_t ip = 0;
+ boost::string_ref S(address.data(), slash != boost::string_ref::npos ? slash : address.size());
+ if (!epee::string_tools::get_ip_int32_from_string(ip, std::string(S)))
+ return make_error_code(net::error::invalid_host);
+
+ return {epee::net_utils::ipv4_network_subnet{ip, (uint8_t)mask}};
+ }
}
diff --git a/src/net/parse.h b/src/net/parse.h
index 5804c4128..0d8fda711 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -36,6 +36,8 @@
namespace net
{
+ void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port);
+
/*!
Identifies onion, i2p and IPv4 addresses and returns them as a generic
`network_address`. If the type is unsupported, it might be a hostname,
@@ -50,5 +52,18 @@ namespace net
*/
expect<epee::net_utils::network_address>
get_network_address(boost::string_ref address, std::uint16_t default_port);
+
+ /*!
+ Identifies an IPv4 subnet in CIDR notatioa and returns it as a generic
+ `network_address`. If the type is unsupported, it might be a hostname,
+ and `error() == net::error::kUnsupportedAddress` is returned.
+
+ \param address An ipv4 address.
+ \param allow_implicit_32 whether to accept "raw" IPv4 addresses, with CIDR notation
+
+ \return A tor or IPv4 address, else error.
+ */
+ expect<epee::net_utils::ipv4_network_subnet>
+ get_ipv4_subnet_address(boost::string_ref address, bool allow_implicit_32 = false);
}
diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp
index fcbcce58c..bb51be242 100644
--- a/src/p2p/net_node.cpp
+++ b/src/p2p/net_node.cpp
@@ -108,10 +108,11 @@ namespace
namespace nodetool
{
- const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol (IPv4)", "0.0.0.0"};
+ const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address = {"p2p-bind-ipv6-address", "Interface for p2p network protocol (IPv6)", "::"};
const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port = {
"p2p-bind-port"
- , "Port for p2p network protocol"
+ , "Port for p2p network protocol (IPv4)"
, std::to_string(config::P2P_DEFAULT_PORT)
, {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
, [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
@@ -122,6 +123,20 @@ namespace nodetool
return val;
}
};
+ const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6 = {
+ "p2p-bind-port-ipv6"
+ , "Port for p2p network protocol (IPv6)"
+ , std::to_string(config::P2P_DEFAULT_PORT)
+ , {{ &cryptonote::arg_testnet_on, &cryptonote::arg_stagenet_on }}
+ , [](std::array<bool, 2> testnet_stagenet, bool defaulted, std::string val)->std::string {
+ if (testnet_stagenet[0] && defaulted)
+ return std::to_string(config::testnet::P2P_DEFAULT_PORT);
+ else if (testnet_stagenet[1] && defaulted)
+ return std::to_string(config::stagenet::P2P_DEFAULT_PORT);
+ return val;
+ }
+ };
+
const command_line::arg_descriptor<uint32_t> arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0};
const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"};
const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"};
@@ -135,6 +150,9 @@ namespace nodetool
const command_line::arg_descriptor<bool> arg_no_sync = {"no-sync", "Don't synchronize the blockchain with other peers", false};
const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"};
+ const command_line::arg_descriptor<std::string> arg_igd = {"igd", "UPnP port mapping (disabled, enabled, delayed)", "delayed"};
+ const command_line::arg_descriptor<bool> arg_p2p_use_ipv6 = {"p2p-use-ipv6", "Enable IPv6 for p2p", false};
+ const command_line::arg_descriptor<bool> arg_p2p_require_ipv4 = {"p2p-require-ipv4", "Require successful IPv4 bind for p2p", true};
const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1};
const command_line::arg_descriptor<int64_t> arg_in_peers = {"in-peers", "set max number of in peers", -1};
const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1};
@@ -143,8 +161,6 @@ namespace nodetool
const command_line::arg_descriptor<int64_t> arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", P2P_DEFAULT_LIMIT_RATE_DOWN};
const command_line::arg_descriptor<int64_t> arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1};
- const command_line::arg_descriptor<bool> arg_save_graph = {"save-graph", "Save data for dr monero", false};
-
boost::optional<std::vector<proxy>> get_proxies(boost::program_options::variables_map const& vm)
{
namespace ip = boost::asio::ip;
diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h
index 42bb3b061..6d2ae878f 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -151,7 +151,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -167,7 +169,9 @@ namespace nodetool
: m_connect(nullptr),
m_net_server(public_service, epee::net_utils::e_connection_type_P2P),
m_bind_ip(),
+ m_bind_ipv6_address(),
m_port(),
+ m_port_ipv6(),
m_our_address(),
m_peerlist(),
m_config{},
@@ -182,7 +186,9 @@ namespace nodetool
connect_func* m_connect;
net_server m_net_server;
std::string m_bind_ip;
+ std::string m_bind_ipv6_address;
std::string m_port;
+ std::string m_port_ipv6;
epee::net_utils::network_address m_our_address; // in anonymity networks
peerlist_manager m_peerlist;
config m_config;
@@ -205,6 +211,13 @@ namespace nodetool
}
};
+ enum igd_t
+ {
+ no_igd,
+ igd,
+ delayed_igd,
+ };
+
public:
typedef t_payload_net_handler payload_net_handler;
@@ -214,9 +227,8 @@ namespace nodetool
m_rpc_port(0),
m_allow_local_ip(false),
m_hide_my_port(false),
- m_no_igd(false),
+ m_igd(no_igd),
m_offline(false),
- m_save_graph(false),
is_closing(false),
m_network_id()
{}
@@ -242,13 +254,20 @@ namespace nodetool
size_t get_public_white_peers_count();
size_t get_public_gray_peers_count();
void get_public_peerlist(std::vector<peerlist_entry>& gray, std::vector<peerlist_entry>& white);
+ void get_peerlist(std::vector<peerlist_entry>& gray, std::vector<peerlist_entry>& white);
size_t get_zone_count() const { return m_network_zones.size(); }
void change_max_out_public_peers(size_t count);
+ uint32_t get_max_out_public_peers() const;
void change_max_in_public_peers(size_t count);
+ uint32_t get_max_in_public_peers() const;
virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME);
virtual bool unblock_host(const epee::net_utils::network_address &address);
- virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
+ virtual bool block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds = P2P_IP_BLOCKTIME);
+ virtual bool unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet);
+ virtual bool is_host_blocked(const epee::net_utils::network_address &address, time_t *seconds) { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return !is_remote_host_allowed(address, seconds); }
+ virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; }
+ virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_subnets; }
virtual void add_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
virtual void remove_used_stripe_peer(const typename t_payload_net_handler::connection_context &context);
@@ -319,7 +338,7 @@ namespace nodetool
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f);
virtual bool add_host_fail(const epee::net_utils::network_address &address);
//----------------- i_connection_filter --------------------------------------------------------
- virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address);
+ virtual bool is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t = NULL);
//-----------------------------------------------------------------------------------------------
bool parse_peer_from_string(epee::net_utils::network_address& pe, const std::string& node_addr, uint16_t default_port = 0);
bool handle_command_line(
@@ -345,7 +364,13 @@ namespace nodetool
bool is_peer_used(const peerlist_entry& peer);
bool is_peer_used(const anchor_peerlist_entry& peer);
bool is_addr_connected(const epee::net_utils::network_address& peer);
- void add_upnp_port_mapping(uint32_t port);
+ void add_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void add_upnp_port_mapping_v4(uint32_t port);
+ void add_upnp_port_mapping_v6(uint32_t port);
+ void add_upnp_port_mapping(uint32_t port, bool ipv4=true, bool ipv6=false);
+ void delete_upnp_port_mapping_impl(uint32_t port, bool ipv6=false);
+ void delete_upnp_port_mapping_v4(uint32_t port);
+ void delete_upnp_port_mapping_v6(uint32_t port);
void delete_upnp_port_mapping(uint32_t port);
template<class t_callback>
bool try_ping(basic_node_data& node_data, p2p_connection_context& context, const t_callback &cb);
@@ -396,12 +421,6 @@ namespace nodetool
public:
- void set_save_graph(bool save_graph)
- {
- m_save_graph = save_graph;
- epee::net_utils::connection_basic::set_save_graph(save_graph);
- }
-
void set_rpc_port(uint16_t rpc_port)
{
m_rpc_port = rpc_port;
@@ -413,13 +432,15 @@ namespace nodetool
bool m_have_address;
bool m_first_connection_maker_call;
uint32_t m_listening_port;
+ uint32_t m_listening_port_ipv6;
uint32_t m_external_port;
uint16_t m_rpc_port;
bool m_allow_local_ip;
bool m_hide_my_port;
- bool m_no_igd;
+ igd_t m_igd;
bool m_offline;
- std::atomic<bool> m_save_graph;
+ bool m_use_ipv6;
+ bool m_require_ipv4;
std::atomic<bool> is_closing;
std::unique_ptr<boost::thread> mPeersLoggerThread;
//critical_section m_connections_lock;
@@ -461,8 +482,9 @@ namespace nodetool
std::map<epee::net_utils::network_address, time_t> m_conn_fails_cache;
epee::critical_section m_conn_fails_cache_lock;
- epee::critical_section m_blocked_hosts_lock;
- std::map<std::string, time_t> m_blocked_hosts;
+ epee::critical_section m_blocked_hosts_lock; // for both hosts and subnets
+ std::map<epee::net_utils::network_address, time_t> m_blocked_hosts;
+ std::map<epee::net_utils::ipv4_network_subnet, time_t> m_blocked_subnets;
epee::critical_section m_host_fails_score_lock;
std::map<std::string, uint64_t> m_host_fails_score;
@@ -479,7 +501,11 @@ namespace nodetool
const int64_t default_limit_up = P2P_DEFAULT_LIMIT_RATE_UP; // kB/s
const int64_t default_limit_down = P2P_DEFAULT_LIMIT_RATE_DOWN; // kB/s
extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ip;
+ extern const command_line::arg_descriptor<std::string> arg_p2p_bind_ipv6_address;
extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port;
+ extern const command_line::arg_descriptor<std::string, false, true, 2> arg_p2p_bind_port_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_use_ipv6;
+ extern const command_line::arg_descriptor<bool> arg_p2p_require_ipv4;
extern const command_line::arg_descriptor<uint32_t> arg_p2p_external_port;
extern const command_line::arg_descriptor<bool> arg_p2p_allow_local_ip;
extern const command_line::arg_descriptor<std::vector<std::string> > arg_p2p_add_peer;
@@ -492,6 +518,7 @@ namespace nodetool
extern const command_line::arg_descriptor<bool> arg_no_sync;
extern const command_line::arg_descriptor<bool> arg_no_igd;
+ extern const command_line::arg_descriptor<std::string> arg_igd;
extern const command_line::arg_descriptor<bool> arg_offline;
extern const command_line::arg_descriptor<int64_t> arg_out_peers;
extern const command_line::arg_descriptor<int64_t> arg_in_peers;
@@ -500,8 +527,6 @@ namespace nodetool
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up;
extern const command_line::arg_descriptor<int64_t> arg_limit_rate_down;
extern const command_line::arg_descriptor<int64_t> arg_limit_rate;
-
- extern const command_line::arg_descriptor<bool> arg_save_graph;
}
POP_WARNINGS
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index cec832231..8c0cff7e2 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -93,7 +93,11 @@ namespace nodetool
void node_server<t_payload_net_handler>::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_p2p_bind_ip);
+ command_line::add_arg(desc, arg_p2p_bind_ipv6_address);
command_line::add_arg(desc, arg_p2p_bind_port, false);
+ command_line::add_arg(desc, arg_p2p_bind_port_ipv6, false);
+ command_line::add_arg(desc, arg_p2p_use_ipv6);
+ command_line::add_arg(desc, arg_p2p_require_ipv4);
command_line::add_arg(desc, arg_p2p_external_port);
command_line::add_arg(desc, arg_p2p_allow_local_ip);
command_line::add_arg(desc, arg_p2p_add_peer);
@@ -105,13 +109,13 @@ namespace nodetool
command_line::add_arg(desc, arg_p2p_hide_my_port);
command_line::add_arg(desc, arg_no_sync);
command_line::add_arg(desc, arg_no_igd);
+ command_line::add_arg(desc, arg_igd);
command_line::add_arg(desc, arg_out_peers);
command_line::add_arg(desc, arg_in_peers);
command_line::add_arg(desc, arg_tos_flag);
command_line::add_arg(desc, arg_limit_rate_up);
command_line::add_arg(desc, arg_limit_rate_down);
command_line::add_arg(desc, arg_limit_rate);
- command_line::add_arg(desc, arg_save_graph);
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@@ -155,19 +159,55 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
- bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address)
+ bool node_server<t_payload_net_handler>::is_remote_host_allowed(const epee::net_utils::network_address &address, time_t *t)
{
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
- auto it = m_blocked_hosts.find(address.host_str());
- if(it == m_blocked_hosts.end())
- return true;
- if(time(nullptr) >= it->second)
+
+ const time_t now = time(nullptr);
+
+ // look in the hosts list
+ auto it = m_blocked_hosts.find(address);
+ if (it != m_blocked_hosts.end())
{
- m_blocked_hosts.erase(it);
- MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked.");
- return true;
+ if (now >= it->second)
+ {
+ m_blocked_hosts.erase(it);
+ MCLOG_CYAN(el::Level::Info, "global", "Host " << address.host_str() << " unblocked.");
+ it = m_blocked_hosts.end();
+ }
+ else
+ {
+ if (t)
+ *t = it->second - now;
+ return false;
+ }
}
- return false;
+
+ // manually loop in subnets
+ if (address.get_type_id() == epee::net_utils::address_type::ipv4)
+ {
+ auto ipv4_address = address.template as<epee::net_utils::ipv4_network_address>();
+ std::map<epee::net_utils::ipv4_network_subnet, time_t>::iterator it;
+ for (it = m_blocked_subnets.begin(); it != m_blocked_subnets.end(); )
+ {
+ if (now >= it->second)
+ {
+ it = m_blocked_subnets.erase(it);
+ MCLOG_CYAN(el::Level::Info, "global", "Subnet " << it->first.host_str() << " unblocked.");
+ continue;
+ }
+ if (it->first.matches(ipv4_address))
+ {
+ if (t)
+ *t = it->second - now;
+ return false;
+ }
+ ++it;
+ }
+ }
+
+ // not found in hosts or subnets, allowed
+ return true;
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
@@ -184,7 +224,7 @@ namespace nodetool
limit = std::numeric_limits<time_t>::max();
else
limit = now + seconds;
- m_blocked_hosts[addr.host_str()] = limit;
+ m_blocked_hosts[addr] = limit;
// drop any connection to that address. This should only have to look into
// the zone related to the connection, but really make sure everything is
@@ -214,7 +254,7 @@ namespace nodetool
bool node_server<t_payload_net_handler>::unblock_host(const epee::net_utils::network_address &address)
{
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
- auto i = m_blocked_hosts.find(address.host_str());
+ auto i = m_blocked_hosts.find(address);
if (i == m_blocked_hosts.end())
return false;
m_blocked_hosts.erase(i);
@@ -223,6 +263,58 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
+ bool node_server<t_payload_net_handler>::block_subnet(const epee::net_utils::ipv4_network_subnet &subnet, time_t seconds)
+ {
+ const time_t now = time(nullptr);
+
+ CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
+ time_t limit;
+ if (now > std::numeric_limits<time_t>::max() - seconds)
+ limit = std::numeric_limits<time_t>::max();
+ else
+ limit = now + seconds;
+ m_blocked_subnets[subnet] = limit;
+
+ // drop any connection to that subnet. This should only have to look into
+ // the zone related to the connection, but really make sure everything is
+ // swept ...
+ std::vector<boost::uuids::uuid> conns;
+ for(auto& zone : m_network_zones)
+ {
+ zone.second.m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt)
+ {
+ if (cntxt.m_remote_address.get_type_id() != epee::net_utils::ipv4_network_address::get_type_id())
+ return true;
+ auto ipv4_address = cntxt.m_remote_address.template as<epee::net_utils::ipv4_network_address>();
+ if (subnet.matches(ipv4_address))
+ {
+ conns.push_back(cntxt.m_connection_id);
+ }
+ return true;
+ });
+ for (const auto &c: conns)
+ zone.second.m_net_server.get_config_object().close(c);
+
+ conns.clear();
+ }
+
+ MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " blocked.");
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
+ bool node_server<t_payload_net_handler>::unblock_subnet(const epee::net_utils::ipv4_network_subnet &subnet)
+ {
+ CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
+ auto i = m_blocked_subnets.find(subnet);
+ if (i == m_blocked_subnets.end())
+ return false;
+ m_blocked_subnets.erase(i);
+ MCLOG_CYAN(el::Level::Info, "global", "Subnet " << subnet.host_str() << " unblocked.");
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::add_host_fail(const epee::net_utils::network_address &address)
{
if(!address.is_blockable())
@@ -253,12 +345,44 @@ namespace nodetool
network_zone& public_zone = m_network_zones[epee::net_utils::zone::public_];
public_zone.m_connect = &public_connect;
public_zone.m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip);
+ public_zone.m_bind_ipv6_address = command_line::get_arg(vm, arg_p2p_bind_ipv6_address);
public_zone.m_port = command_line::get_arg(vm, arg_p2p_bind_port);
+ public_zone.m_port_ipv6 = command_line::get_arg(vm, arg_p2p_bind_port_ipv6);
public_zone.m_can_pingback = true;
m_external_port = command_line::get_arg(vm, arg_p2p_external_port);
m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip);
- m_no_igd = command_line::get_arg(vm, arg_no_igd);
+ const bool has_no_igd = command_line::get_arg(vm, arg_no_igd);
+ const std::string sigd = command_line::get_arg(vm, arg_igd);
+ if (sigd == "enabled")
+ {
+ if (has_no_igd)
+ {
+ MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " enabled");
+ return false;
+ }
+ m_igd = igd;
+ }
+ else if (sigd == "disabled")
+ {
+ m_igd = no_igd;
+ }
+ else if (sigd == "delayed")
+ {
+ if (has_no_igd && !command_line::is_arg_defaulted(vm, arg_igd))
+ {
+ MFATAL("Cannot have both --" << arg_no_igd.name << " and --" << arg_igd.name << " delayed");
+ return false;
+ }
+ m_igd = has_no_igd ? no_igd : delayed_igd;
+ }
+ else
+ {
+ MFATAL("Invalid value for --" << arg_igd.name << ", expected enabled, disabled or delayed");
+ return false;
+ }
m_offline = command_line::get_arg(vm, cryptonote::arg_offline);
+ m_use_ipv6 = command_line::get_arg(vm, arg_p2p_use_ipv6);
+ m_require_ipv4 = command_line::get_arg(vm, arg_p2p_require_ipv4);
if (command_line::has_arg(vm, arg_p2p_add_peer))
{
@@ -292,11 +416,6 @@ namespace nodetool
}
}
- if(command_line::has_arg(vm, arg_save_graph))
- {
- set_save_graph(true);
- }
-
if (command_line::has_arg(vm,arg_p2p_add_exclusive_node))
{
if (!parse_peers_and_add_to_container(vm, arg_p2p_add_exclusive_node, m_exclusive_peers))
@@ -407,12 +526,17 @@ namespace nodetool
std::string host = addr;
std::string port = std::to_string(default_port);
- size_t pos = addr.find_last_of(':');
- if (std::string::npos != pos)
+ size_t colon_pos = addr.find_last_of(':');
+ size_t dot_pos = addr.find_last_of('.');
+ size_t square_brace_pos = addr.find('[');
+
+ // IPv6 will have colons regardless. IPv6 and IPv4 address:port will have a colon but also either a . or a [
+ // as IPv6 addresses specified as address:port are to be specified as "[addr:addr:...:addr]:port"
+ // One may also specify an IPv6 address as simply "[addr:addr:...:addr]" without the port; in that case
+ // the square braces will be stripped here.
+ if ((std::string::npos != colon_pos && std::string::npos != dot_pos) || std::string::npos != square_brace_pos)
{
- CHECK_AND_ASSERT_MES(addr.length() - 1 != pos && 0 != pos, false, "Failed to parse seed address from string: '" << addr << '\'');
- host = addr.substr(0, pos);
- port = addr.substr(pos + 1);
+ net::get_network_address_host_and_port(addr, host, port);
}
MINFO("Resolving node address: host=" << host << ", port=" << port);
@@ -435,7 +559,9 @@ namespace nodetool
}
else
{
- MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec));
+ epee::net_utils::network_address na{epee::net_utils::ipv6_network_address{endpoint.address().to_v6(), endpoint.port()}};
+ seed_nodes.push_back(na);
+ MINFO("Added node: " << na.str());
}
}
return true;
@@ -669,21 +795,40 @@ namespace nodetool
if (!zone.second.m_bind_ip.empty())
{
+ std::string ipv6_addr = "";
+ std::string ipv6_port = "";
zone.second.m_net_server.set_connection_filter(this);
- MINFO("Binding on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
- res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
+ MINFO("Binding (IPv4) on " << zone.second.m_bind_ip << ":" << zone.second.m_port);
+ if (!zone.second.m_bind_ipv6_address.empty() && m_use_ipv6)
+ {
+ ipv6_addr = zone.second.m_bind_ipv6_address;
+ ipv6_port = zone.second.m_port_ipv6;
+ MINFO("Binding (IPv6) on " << zone.second.m_bind_ipv6_address << ":" << zone.second.m_port_ipv6);
+ }
+ res = zone.second.m_net_server.init_server(zone.second.m_port, zone.second.m_bind_ip, ipv6_port, ipv6_addr, m_use_ipv6, m_require_ipv4, epee::net_utils::ssl_support_t::e_ssl_support_disabled);
CHECK_AND_ASSERT_MES(res, false, "Failed to bind server");
}
}
m_listening_port = public_zone.m_net_server.get_binded_port();
- MLOG_GREEN(el::Level::Info, "Net service bound to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv4) to " << public_zone.m_bind_ip << ":" << m_listening_port);
+ if (m_use_ipv6)
+ {
+ m_listening_port_ipv6 = public_zone.m_net_server.get_binded_port_ipv6();
+ MLOG_GREEN(el::Level::Info, "Net service bound (IPv6) to " << public_zone.m_bind_ipv6_address << ":" << m_listening_port_ipv6);
+ }
if(m_external_port)
MDEBUG("External port defined as " << m_external_port);
// add UPnP port mapping
- if(!m_no_igd)
- add_upnp_port_mapping(m_listening_port);
+ if(m_igd == igd)
+ {
+ add_upnp_port_mapping_v4(m_listening_port);
+ if (m_use_ipv6)
+ {
+ add_upnp_port_mapping_v6(m_listening_port_ipv6);
+ }
+ }
return res;
}
@@ -776,7 +921,7 @@ namespace nodetool
for(auto& zone : m_network_zones)
zone.second.m_net_server.deinit_server();
// remove UPnP port mapping
- if(!m_no_igd)
+ if(m_igd == igd)
delete_upnp_port_mapping(m_listening_port);
}
return store_config();
@@ -1594,6 +1739,15 @@ namespace nodetool
}
//-----------------------------------------------------------------------------------
template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::get_peerlist(std::vector<peerlist_entry>& gray, std::vector<peerlist_entry>& white)
+ {
+ for (auto &zone: m_network_zones)
+ {
+ zone.second.m_peerlist.get_peerlist(gray, white); // appends
+ }
+ }
+ //-----------------------------------------------------------------------------------
+ template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::idle_worker()
{
m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::peer_sync_idle_maker, this));
@@ -1619,8 +1773,17 @@ namespace nodetool
}
else
{
- const el::Level level = el::Level::Warning;
- MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port());
+ if (m_igd == delayed_igd)
+ {
+ MWARNING("No incoming connections, trying to setup IGD");
+ add_upnp_port_mapping(m_listening_port);
+ m_igd = igd;
+ }
+ else
+ {
+ const el::Level level = el::Level::Warning;
+ MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port());
+ }
}
}
return true;
@@ -1877,19 +2040,43 @@ namespace nodetool
if(!node_data.my_port)
return false;
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), false,
- "Only IPv4 addresses are supported here");
+ bool address_ok = (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id());
+ CHECK_AND_ASSERT_MES(address_ok, false,
+ "Only IPv4 or IPv6 addresses are supported here");
const epee::net_utils::network_address na = context.m_remote_address;
- uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ std::string ip;
+ uint32_t ipv4_addr;
+ boost::asio::ip::address_v6 ipv6_addr;
+ bool is_ipv4;
+ if (na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ ipv4_addr = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ ip = epee::string_tools::get_ip_string_from_int32(ipv4_addr);
+ is_ipv4 = true;
+ }
+ else
+ {
+ ipv6_addr = na.as<const epee::net_utils::ipv6_network_address>().ip();
+ ip = ipv6_addr.to_string();
+ is_ipv4 = false;
+ }
network_zone& zone = m_network_zones.at(na.get_zone());
if(!zone.m_peerlist.is_host_allowed(context.m_remote_address))
return false;
- std::string ip = epee::string_tools::get_ip_string_from_int32(actual_ip);
std::string port = epee::string_tools::num_to_string_fast(node_data.my_port);
- epee::net_utils::network_address address{epee::net_utils::ipv4_network_address(actual_ip, node_data.my_port)};
+
+ epee::net_utils::network_address address;
+ if (is_ipv4)
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv4_network_address(ipv4_addr, node_data.my_port)};
+ }
+ else
+ {
+ address = epee::net_utils::network_address{epee::net_utils::ipv6_network_address(ipv6_addr, node_data.my_port)};
+ }
peerid_type pr = node_data.peer_id;
bool r = zone.m_net_server.connect_async(ip, port, zone.m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ address, pr, this](
const typename net_server::t_connection_context& ping_context,
@@ -2073,12 +2260,19 @@ namespace nodetool
//try ping to be sure that we can add this peer to peer_list
try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]()
{
- CHECK_AND_ASSERT_MES(context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), void(),
- "Only IPv4 addresses are supported here");
+ CHECK_AND_ASSERT_MES((context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id() || context.m_remote_address.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id()), void(),
+ "Only IPv4 or IPv6 addresses are supported here");
//called only(!) if success pinged, update local peerlist
peerlist_entry pe;
const epee::net_utils::network_address na = context.m_remote_address;
- pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ if (context.m_remote_address.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ pe.adr = epee::net_utils::ipv4_network_address(na.as<epee::net_utils::ipv4_network_address>().ip(), port_l);
+ }
+ else
+ {
+ pe.adr = epee::net_utils::ipv6_network_address(na.as<epee::net_utils::ipv6_network_address>().ip(), port_l);
+ }
time_t last_seen;
time(&last_seen);
pe.last_seen = static_cast<int64_t>(last_seen);
@@ -2247,20 +2441,30 @@ namespace nodetool
auto public_zone = m_network_zones.find(epee::net_utils::zone::public_);
if (public_zone != m_network_zones.end())
{
- const auto current = public_zone->second.m_config.m_net_config.max_out_connection_count;
+ const auto current = public_zone->second.m_net_server.get_config_object().get_out_connections_count();
public_zone->second.m_config.m_net_config.max_out_connection_count = count;
if(current > count)
public_zone->second.m_net_server.get_config_object().del_out_connections(current - count);
+ m_payload_handler.set_max_out_peers(count);
}
}
template<class t_payload_net_handler>
+ uint32_t node_server<t_payload_net_handler>::get_max_out_public_peers() const
+ {
+ const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_);
+ if (public_zone == m_network_zones.end())
+ return 0;
+ return public_zone->second.m_config.m_net_config.max_out_connection_count;
+ }
+
+ template<class t_payload_net_handler>
void node_server<t_payload_net_handler>::change_max_in_public_peers(size_t count)
{
auto public_zone = m_network_zones.find(epee::net_utils::zone::public_);
if (public_zone != m_network_zones.end())
{
- const auto current = public_zone->second.m_config.m_net_config.max_in_connection_count;
+ const auto current = public_zone->second.m_net_server.get_config_object().get_in_connections_count();
public_zone->second.m_config.m_net_config.max_in_connection_count = count;
if(current > count)
public_zone->second.m_net_server.get_config_object().del_in_connections(current - count);
@@ -2268,6 +2472,15 @@ namespace nodetool
}
template<class t_payload_net_handler>
+ uint32_t node_server<t_payload_net_handler>::get_max_in_public_peers() const
+ {
+ const auto public_zone = m_network_zones.find(epee::net_utils::zone::public_);
+ if (public_zone == m_network_zones.end())
+ return 0;
+ return public_zone->second.m_config.m_net_config.max_in_connection_count;
+ }
+
+ template<class t_payload_net_handler>
bool node_server<t_payload_net_handler>::set_tos_flag(const boost::program_options::variables_map& vm, int flag)
{
if(flag==-1){
@@ -2427,16 +2640,19 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_impl(uint32_t port, bool ipv6) // if ipv6 false, do ipv4
{
- MDEBUG("Attempting to add IGD port mapping.");
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to add IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
+
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2473,16 +2689,38 @@ namespace nodetool
}
template<class t_payload_net_handler>
- void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v4(uint32_t port)
+ {
+ add_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping_v6(uint32_t port)
+ {
+ add_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::add_upnp_port_mapping(uint32_t port, bool ipv4, bool ipv6)
{
- MDEBUG("Attempting to delete IGD port mapping.");
+ if (ipv4) add_upnp_port_mapping_v4(port);
+ if (ipv6) add_upnp_port_mapping_v6(port);
+ }
+
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_impl(uint32_t port, bool ipv6)
+ {
+ std::string ipversion = ipv6 ? "(IPv6)" : "(IPv4)";
+ MDEBUG("Attempting to delete IGD port mapping " << ipversion << ".");
int result;
+ const int ipv6_arg = ipv6 ? 1 : 0;
#if MINIUPNPC_API_VERSION > 13
// default according to miniupnpc.h
unsigned char ttl = 2;
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, ttl, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, ttl, &result);
#else
- UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, 0, &result);
+ UPNPDev* deviceList = upnpDiscover(1000, NULL, NULL, 0, ipv6_arg, &result);
#endif
UPNPUrls urls;
IGDdatas igdData;
@@ -2515,6 +2753,25 @@ namespace nodetool
}
}
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v4(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, false);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping_v6(uint32_t port)
+ {
+ delete_upnp_port_mapping_impl(port, true);
+ }
+
+ template<class t_payload_net_handler>
+ void node_server<t_payload_net_handler>::delete_upnp_port_mapping(uint32_t port)
+ {
+ delete_upnp_port_mapping_v4(port);
+ delete_upnp_port_mapping_v6(port);
+ }
+
template<typename t_payload_net_handler>
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::socks_connect(network_zone& zone, const epee::net_utils::network_address& remote, epee::net_utils::ssl_support_t ssl_support)
@@ -2533,13 +2790,34 @@ namespace nodetool
boost::optional<p2p_connection_context_t<typename t_payload_net_handler::connection_context>>
node_server<t_payload_net_handler>::public_connect(network_zone& zone, epee::net_utils::network_address const& na, epee::net_utils::ssl_support_t ssl_support)
{
- CHECK_AND_ASSERT_MES(na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id(), boost::none,
- "Only IPv4 addresses are supported here");
- const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ bool is_ipv4 = na.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id();
+ bool is_ipv6 = na.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id();
+ CHECK_AND_ASSERT_MES(is_ipv4 || is_ipv6, boost::none,
+ "Only IPv4 or IPv6 addresses are supported here");
+
+ std::string address;
+ std::string port;
+
+ if (is_ipv4)
+ {
+ const epee::net_utils::ipv4_network_address &ipv4 = na.as<const epee::net_utils::ipv4_network_address>();
+ address = epee::string_tools::get_ip_string_from_int32(ipv4.ip());
+ port = epee::string_tools::num_to_string_fast(ipv4.port());
+ }
+ else if (is_ipv6)
+ {
+ const epee::net_utils::ipv6_network_address &ipv6 = na.as<const epee::net_utils::ipv6_network_address>();
+ address = ipv6.ip().to_string();
+ port = epee::string_tools::num_to_string_fast(ipv6.port());
+ }
+ else
+ {
+ LOG_ERROR("Only IPv4 or IPv6 addresses are supported here");
+ return boost::none;
+ }
typename net_server::t_connection_context con{};
- const bool res = zone.m_net_server.connect(epee::string_tools::get_ip_string_from_int32(ipv4.ip()),
- epee::string_tools::num_to_string_fast(ipv4.port()),
+ const bool res = zone.m_net_server.connect(address, port,
zone.m_config.m_net_config.connection_timeout,
con, "0.0.0.0", ssl_support);
diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h
index 26451b333..34d151f5f 100644
--- a/src/p2p/net_node_common.h
+++ b/src/p2p/net_node_common.h
@@ -56,7 +56,8 @@ namespace nodetool
virtual bool for_connection(const boost::uuids::uuid&, std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0;
virtual bool block_host(const epee::net_utils::network_address &address, time_t seconds = 0)=0;
virtual bool unblock_host(const epee::net_utils::network_address &address)=0;
- virtual std::map<std::string, time_t> get_blocked_hosts()=0;
+ virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()=0;
+ virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()=0;
virtual bool add_host_fail(const epee::net_utils::network_address &address)=0;
virtual void add_used_stripe_peer(const t_connection_context &context)=0;
virtual void remove_used_stripe_peer(const t_connection_context &context)=0;
@@ -112,9 +113,13 @@ namespace nodetool
{
return true;
}
- virtual std::map<std::string, time_t> get_blocked_hosts()
+ virtual std::map<epee::net_utils::network_address, time_t> get_blocked_hosts()
{
- return std::map<std::string, time_t>();
+ return std::map<epee::net_utils::network_address, time_t>();
+ }
+ virtual std::map<epee::net_utils::ipv4_network_subnet, time_t> get_blocked_subnets()
+ {
+ return std::map<epee::net_utils::ipv4_network_subnet, time_t>();
}
virtual bool add_host_fail(const epee::net_utils::network_address &address)
{
diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h
index 883997fd6..c65b9dd82 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -278,6 +278,9 @@ namespace nodetool
// was moved to the gray list (if it's not accessibe, which the attacker can check if
// the address accepts incoming connections) or it was the oldest to still fit in the 250 items,
// so its last_seen is old.
+ //
+ // See Cao, Tong et al. "Exploring the Monero Peer-to-Peer Network". https://eprint.iacr.org/2019/411
+ //
const uint32_t pick_depth = anonymize ? depth + depth / 5 : depth;
bs_head.reserve(pick_depth);
for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index))
@@ -290,7 +293,7 @@ namespace nodetool
if (anonymize)
{
- std::random_shuffle(bs_head.begin(), bs_head.end());
+ std::shuffle(bs_head.begin(), bs_head.end(), crypto::random_device{});
if (bs_head.size() > depth)
bs_head.resize(depth);
for (auto &e: bs_head)
diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h
index 32f30adca..05eb36e65 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -76,6 +76,9 @@ namespace boost
case epee::net_utils::ipv4_network_address::get_type_id():
do_serialize<epee::net_utils::ipv4_network_address>(is_saving, a, na);
break;
+ case epee::net_utils::ipv6_network_address::get_type_id():
+ do_serialize<epee::net_utils::ipv6_network_address>(is_saving, a, na);
+ break;
case net::tor_address::get_type_id():
do_serialize<net::tor_address>(is_saving, a, na);
break;
@@ -99,6 +102,34 @@ namespace boost
}
template <class Archive, class ver_type>
+ inline void serialize(Archive &a, boost::asio::ip::address_v6& v6, const ver_type ver)
+ {
+ if (typename Archive::is_saving())
+ {
+ auto bytes = v6.to_bytes();
+ for (auto &e: bytes) a & e;
+ }
+ else
+ {
+ boost::asio::ip::address_v6::bytes_type bytes;
+ for (auto &e: bytes) a & e;
+ v6 = boost::asio::ip::address_v6(bytes);
+ }
+ }
+
+ template <class Archive, class ver_type>
+ inline void serialize(Archive &a, epee::net_utils::ipv6_network_address& na, const ver_type ver)
+ {
+ boost::asio::ip::address_v6 ip{na.ip()};
+ uint16_t port{na.port()};
+ a & ip;
+ a & port;
+ if (!typename Archive::is_saving())
+ na = epee::net_utils::ipv6_network_address{ip, port};
+ }
+
+
+ template <class Archive, class ver_type>
inline void save(Archive& a, const net::tor_address& na, const ver_type)
{
const size_t length = std::strlen(na.host_str());
diff --git a/src/ringct/multiexp.cc b/src/ringct/multiexp.cc
index 6f77fed34..f69b4a12c 100644
--- a/src/ringct/multiexp.cc
+++ b/src/ringct/multiexp.cc
@@ -447,7 +447,6 @@ rct::key straus(const std::vector<MultiexpData> &data, const std::shared_ptr<str
{
CHECK_AND_ASSERT_THROW_MES(cache == NULL || cache->size >= data.size(), "Cache is too small");
MULTIEXP_PERF(PERF_TIMER_UNIT(straus, 1000000));
- bool HiGi = cache != NULL;
STEP = STEP ? STEP : 192;
MULTIEXP_PERF(PERF_TIMER_START_UNIT(setup, 1000000));
diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp
index f01e683cb..2c4e5fc3b 100644
--- a/src/ringct/rctTypes.cpp
+++ b/src/ringct/rctTypes.cpp
@@ -190,7 +190,6 @@ namespace rct {
int byte, i, j;
for (j = 0; j < 8; j++) {
byte = 0;
- i = 8 * j;
for (i = 7; i > -1; i--) {
byte = byte * 2 + amountb2[8 * j + i];
}
diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt
index cffe8e1eb..06577d37e 100644
--- a/src/rpc/CMakeLists.txt
+++ b/src/rpc/CMakeLists.txt
@@ -47,7 +47,7 @@ set(rpc_base_headers
rpc_args.h)
set(rpc_headers
- rpc_handler.cpp)
+ rpc_handler.h)
set(daemon_rpc_server_headers)
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index b6836e636..73138686d 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -105,6 +105,35 @@ namespace cryptonote
, m_p2p(p2p)
{}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::set_bootstrap_daemon(const std::string &address, const std::string &username_password)
+ {
+ boost::optional<epee::net_utils::http::login> credentials;
+ const auto loc = username_password.find(':');
+ if (loc != std::string::npos)
+ {
+ credentials = epee::net_utils::http::login(username_password.substr(0, loc), username_password.substr(loc + 1));
+ }
+ return set_bootstrap_daemon(address, credentials);
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ 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);
+
+ if (!address.empty())
+ {
+ if (!m_http_client.set_server(address, credentials, epee::net_utils::ssl_support_t::e_ssl_support_autodetect))
+ {
+ return false;
+ }
+ }
+
+ m_bootstrap_daemon_address = address;
+ m_should_use_bootstrap_daemon = !m_bootstrap_daemon_address.empty();
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::init(
const boost::program_options::variables_map& vm
, const bool restricted
@@ -113,34 +142,18 @@ namespace cryptonote
{
m_restricted = restricted;
m_net_server.set_threads_prefix("RPC");
+ m_net_server.set_connection_filter(&m_p2p);
auto rpc_config = cryptonote::rpc_args::process(vm, true);
if (!rpc_config)
return false;
- m_bootstrap_daemon_address = command_line::get_arg(vm, arg_bootstrap_daemon_address);
- if (!m_bootstrap_daemon_address.empty())
- {
- const std::string &bootstrap_daemon_login = command_line::get_arg(vm, arg_bootstrap_daemon_login);
- const auto loc = bootstrap_daemon_login.find(':');
- if (!bootstrap_daemon_login.empty() && loc != std::string::npos)
- {
- epee::net_utils::http::login login;
- login.username = bootstrap_daemon_login.substr(0, loc);
- login.password = bootstrap_daemon_login.substr(loc + 1);
- m_http_client.set_server(m_bootstrap_daemon_address, login, epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
- }
- else
- {
- m_http_client.set_server(m_bootstrap_daemon_address, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
- }
- m_should_use_bootstrap_daemon = true;
- }
- else
+ if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address),
+ command_line::get_arg(vm, arg_bootstrap_daemon_login)))
{
- m_should_use_bootstrap_daemon = false;
+ MERROR("Failed to parse bootstrap daemon address");
+ return false;
}
- m_was_bootstrap_ever_used = false;
boost::optional<epee::net_utils::http::login> http_login{};
@@ -149,7 +162,9 @@ namespace cryptonote
auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); };
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
- rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
+ rng, std::move(port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login), std::move(rpc_config->ssl_options)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -161,6 +176,24 @@ namespace cryptonote
}
return true;
}
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::add_host_fail(const connection_context *ctx)
+ {
+ if(!ctx || !ctx->m_remote_address.is_blockable())
+ return false;
+
+ CRITICAL_REGION_LOCAL(m_host_fails_score_lock);
+ uint64_t fails = ++m_host_fails_score[ctx->m_remote_address.host_str()];
+ MDEBUG("Host " << ctx->m_remote_address.host_str() << " fail score=" << fails);
+ if(fails > RPC_IP_FAILS_BEFORE_BLOCK)
+ {
+ auto it = m_host_fails_score.find(ctx->m_remote_address.host_str());
+ CHECK_AND_ASSERT_MES(it != m_host_fails_score.end(), false, "internal error");
+ it->second = RPC_IP_FAILS_BEFORE_BLOCK/2;
+ m_p2p.block_host(ctx->m_remote_address);
+ }
+ return true;
+ }
#define CHECK_CORE_READY() do { if(!check_core_ready()){res.status = CORE_RPC_STATUS_BUSY;return true;} } while(0)
//------------------------------------------------------------------------------------------------------------------------------
@@ -185,7 +218,10 @@ namespace cryptonote
bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_INFO>(invoke_http_mode::JON, "/getinfo", req, res, r))
{
- res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ {
+ boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
+ }
crypto::hash top_hash;
m_core.get_blockchain_top(res.height_without_bootstrap, top_hash);
++res.height_without_bootstrap; // turn top block height into blockchain height
@@ -224,13 +260,16 @@ namespace cryptonote
res.start_time = restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
- res.bootstrap_daemon_address = restricted ? "" : m_bootstrap_daemon_address;
res.height_without_bootstrap = restricted ? 0 : res.height;
if (restricted)
+ {
+ res.bootstrap_daemon_address = "";
res.was_bootstrap_ever_used = false;
+ }
else
{
boost::shared_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
+ res.bootstrap_daemon_address = m_bootstrap_daemon_address;
res.was_bootstrap_ever_used = m_was_bootstrap_ever_used;
}
res.database_size = m_core.get_blockchain_storage().get_db().get_database_size();
@@ -282,6 +321,7 @@ namespace cryptonote
if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, req.prune, !req.no_miner_tx, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT))
{
res.status = "Failed";
+ add_host_fail(ctx);
return false;
}
@@ -405,6 +445,7 @@ namespace cryptonote
if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, res.start_height, res.current_height, false))
{
res.status = "Failed";
+ add_host_fail(ctx);
return false;
}
@@ -585,7 +626,8 @@ namespace cryptonote
return true;
}
const cryptonote::blobdata pruned = ss.str();
- sorted_txs.push_back(std::make_tuple(h, pruned, get_transaction_prunable_hash(tx), std::string(i->tx_blob, pruned.size())));
+ const crypto::hash prunable_hash = tx.version == 1 ? crypto::null_hash : get_transaction_prunable_hash(tx);
+ sorted_txs.push_back(std::make_tuple(h, pruned, prunable_hash, std::string(i->tx_blob, pruned.size())));
missed_txs.erase(std::find(missed_txs.begin(), missed_txs.end(), h));
pool_tx_hashes.insert(h);
const std::string hash_string = epee::string_tools::pod_to_hex(h);
@@ -844,6 +886,8 @@ namespace cryptonote
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() ? "" : ": ";
if (tvc.m_verifivation_failed)
{
@@ -1000,24 +1044,36 @@ namespace cryptonote
PERF_TIMER(on_get_peer_list);
std::vector<nodetool::peerlist_entry> white_list;
std::vector<nodetool::peerlist_entry> gray_list;
- m_p2p.get_public_peerlist(gray_list, white_list);
- res.white_list.reserve(white_list.size());
+ if (req.public_only)
+ {
+ m_p2p.get_public_peerlist(gray_list, white_list);
+ }
+ else
+ {
+ m_p2p.get_peerlist(gray_list, white_list);
+ }
+
for (auto & entry : white_list)
{
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
else
res.white_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
- res.gray_list.reserve(gray_list.size());
for (auto & entry : gray_list)
{
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ else if (entry.adr.get_type_id() == epee::net_utils::ipv6_network_address::get_type_id())
+ res.gray_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv6_network_address>().host_str(),
+ entry.adr.as<epee::net_utils::ipv6_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
else
res.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
@@ -1026,6 +1082,45 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx)
+ {
+ PERF_TIMER(on_get_public_nodes);
+
+ COMMAND_RPC_GET_PEER_LIST::response peer_list_res;
+ const bool success = on_get_peer_list(COMMAND_RPC_GET_PEER_LIST::request(), peer_list_res, ctx);
+ res.status = peer_list_res.status;
+ if (!success)
+ {
+ return false;
+ }
+ if (res.status != CORE_RPC_STATUS_OK)
+ {
+ return true;
+ }
+
+ const auto collect = [](const std::vector<peer> &peer_list, std::vector<public_node> &public_nodes)
+ {
+ for (const auto &entry : peer_list)
+ {
+ if (entry.rpc_port != 0)
+ {
+ public_nodes.emplace_back(entry);
+ }
+ }
+ };
+
+ if (req.white)
+ {
+ collect(peer_list_res.white_list, res.white);
+ }
+ if (req.gray)
+ {
+ collect(peer_list_res.gray_list, res.gray);
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx)
{
PERF_TIMER(on_set_log_hash_rate);
@@ -1125,6 +1220,28 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx)
+ {
+ PERF_TIMER(on_set_bootstrap_daemon);
+
+ boost::optional<epee::net_utils::http::login> credentials;
+ if (!req.username.empty() || !req.password.empty())
+ {
+ credentials = epee::net_utils::http::login(req.username, req.password);
+ }
+
+ if (set_bootstrap_daemon(req.address, credentials))
+ {
+ res.status = CORE_RPC_STATUS_OK;
+ }
+ else
+ {
+ res.status = "Failed to set bootstrap daemon";
+ }
+
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx)
{
PERF_TIMER(on_stop_daemon);
@@ -1216,6 +1333,20 @@ namespace cryptonote
return false;
}
+ if(req.reserve_size && !req.extra_nonce.empty())
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Cannot specify both a reserve_size and an extra_nonce";
+ return false;
+ }
+
+ if(req.extra_nonce.size() > 510)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE;
+ error_resp.message = "Too big extra_nonce size, maximum 510 hex chars";
+ return false;
+ }
+
cryptonote::address_parse_info info;
if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(info, nettype(), req.wallet_address))
@@ -1233,7 +1364,17 @@ namespace cryptonote
block b;
cryptonote::blobdata blob_reserve;
- blob_reserve.resize(req.reserve_size, 0);
+ if(!req.extra_nonce.empty())
+ {
+ if(!string_tools::parse_hexstr_to_binbuff(req.extra_nonce, blob_reserve))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Parameter extra_nonce should be a hex string";
+ return false;
+ }
+ }
+ else
+ blob_reserve.resize(req.reserve_size, 0);
cryptonote::difficulty_type wdiff;
crypto::hash prev_block;
if (!req.prev_block.empty())
@@ -1449,10 +1590,12 @@ namespace cryptonote
bool core_rpc_server::use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r)
{
res.untrusted = false;
+
+ boost::upgrade_lock<boost::shared_mutex> upgrade_lock(m_bootstrap_daemon_mutex);
+
if (m_bootstrap_daemon_address.empty())
return false;
- boost::unique_lock<boost::shared_mutex> lock(m_bootstrap_daemon_mutex);
if (!m_should_use_bootstrap_daemon)
{
MINFO("The local daemon is fully synced. Not switching back to the bootstrap daemon");
@@ -1462,7 +1605,10 @@ namespace cryptonote
auto current_time = std::chrono::system_clock::now();
if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s
{
- m_bootstrap_height_check_time = current_time;
+ {
+ boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock);
+ m_bootstrap_height_check_time = current_time;
+ }
uint64_t top_height;
crypto::hash top_hash;
@@ -1476,7 +1622,7 @@ namespace cryptonote
ok = ok && getheight_res.status == CORE_RPC_STATUS_OK;
m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height;
- MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")");
+ MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << (ok ? getheight_res.height : 0) << ")");
}
if (!m_should_use_bootstrap_daemon)
return false;
@@ -1506,7 +1652,12 @@ namespace cryptonote
MERROR("Unknown invoke_http_mode: " << mode);
return false;
}
- m_was_bootstrap_ever_used = true;
+
+ {
+ boost::upgrade_to_unique_lock<boost::shared_mutex> lock(upgrade_lock);
+ m_was_bootstrap_ever_used = true;
+ }
+
r = r && res.status == CORE_RPC_STATUS_OK;
res.untrusted = true;
return true;
@@ -1550,38 +1701,55 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH>(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r))
return r;
- crypto::hash block_hash;
- bool hash_parsed = parse_hash256(req.hash, block_hash);
- if(!hash_parsed)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
- error_resp.message = "Failed to parse hex representation of block hash. Hex = " + req.hash + '.';
- return false;
- }
- block blk;
- bool orphan = false;
- bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan);
- if (!have_block)
- {
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't get block by hash. Hash = " + req.hash + '.';
- return false;
- }
- if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ auto get = [this](const std::string &hash, bool fill_pow_hash, block_header_response &block_header, bool restricted, epee::json_rpc::error& error_resp) -> bool {
+ crypto::hash block_hash;
+ bool hash_parsed = parse_hash256(hash, block_hash);
+ if(!hash_parsed)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Failed to parse hex representation of block hash. Hex = " + hash + '.';
+ return false;
+ }
+ block blk;
+ bool orphan = false;
+ bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan);
+ if (!have_block)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: can't get block by hash. Hash = " + hash + '.';
+ return false;
+ }
+ if (blk.miner_tx.vin.size() != 1 || blk.miner_tx.vin.front().type() != typeid(txin_gen))
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
+ return false;
+ }
+ uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
+ bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, block_header, fill_pow_hash && !restricted);
+ if (!response_filled)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
+ error_resp.message = "Internal error: can't produce valid response.";
+ return false;
+ }
+ return true;
+ };
+
+ const bool restricted = m_restricted && ctx;
+ if (!req.hash.empty())
{
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: coinbase transaction in the block has the wrong type";
- return false;
+ if (!get(req.hash, req.fill_pow_hash, res.block_header, restricted, error_resp))
+ return false;
}
- uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height;
- const bool restricted = m_restricted && ctx;
- bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header, req.fill_pow_hash && !restricted);
- if (!response_filled)
+ res.block_headers.reserve(req.hashes.size());
+ for (const std::string &hash: req.hashes)
{
- error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
- error_resp.message = "Internal error: can't produce valid response.";
- return false;
+ res.block_headers.push_back({});
+ if (!get(hash, req.fill_pow_hash, res.block_headers.back(), restricted, error_resp))
+ return false;
}
+
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -1772,20 +1940,60 @@ namespace cryptonote
PERF_TIMER(on_get_bans);
auto now = time(nullptr);
- std::map<std::string, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
- for (std::map<std::string, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
+ std::map<epee::net_utils::network_address, time_t> blocked_hosts = m_p2p.get_blocked_hosts();
+ for (std::map<epee::net_utils::network_address, time_t>::const_iterator i = blocked_hosts.begin(); i != blocked_hosts.end(); ++i)
{
if (i->second > now) {
COMMAND_RPC_GETBANS::ban b;
- b.host = i->first;
+ b.host = i->first.host_str();
b.ip = 0;
uint32_t ip;
- if (epee::string_tools::get_ip_int32_from_string(ip, i->first))
+ if (epee::string_tools::get_ip_int32_from_string(ip, b.host))
b.ip = ip;
b.seconds = i->second - now;
res.bans.push_back(b);
}
}
+ std::map<epee::net_utils::ipv4_network_subnet, time_t> blocked_subnets = m_p2p.get_blocked_subnets();
+ for (std::map<epee::net_utils::ipv4_network_subnet, time_t>::const_iterator i = blocked_subnets.begin(); i != blocked_subnets.end(); ++i)
+ {
+ if (i->second > now) {
+ COMMAND_RPC_GETBANS::ban b;
+ b.host = i->first.host_str();
+ b.ip = 0;
+ b.seconds = i->second - now;
+ res.bans.push_back(b);
+ }
+ }
+
+ res.status = CORE_RPC_STATUS_OK;
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool core_rpc_server::on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
+ {
+ PERF_TIMER(on_banned);
+
+ auto na_parsed = net::get_network_address(req.address, 0);
+ if (!na_parsed)
+ {
+ error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
+ error_resp.message = "Unsupported host type";
+ return false;
+ }
+ epee::net_utils::network_address na = std::move(*na_parsed);
+
+ time_t seconds;
+ if (m_p2p.is_host_blocked(na, &seconds))
+ {
+ res.banned = true;
+ res.seconds = seconds;
+ }
+ else
+ {
+ res.banned = false;
+ res.seconds = 0;
+ }
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -1798,13 +2006,29 @@ namespace cryptonote
for (auto i = req.bans.begin(); i != req.bans.end(); ++i)
{
epee::net_utils::network_address na;
+
+ // try subnet first
+ if (!i->host.empty())
+ {
+ auto ns_parsed = net::get_ipv4_subnet_address(i->host);
+ if (ns_parsed)
+ {
+ if (i->ban)
+ m_p2p.block_subnet(*ns_parsed, i->seconds);
+ else
+ m_p2p.unblock_subnet(*ns_parsed);
+ continue;
+ }
+ }
+
+ // then host
if (!i->host.empty())
{
auto na_parsed = net::get_network_address(i->host, 0);
if (!na_parsed)
{
error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM;
- error_resp.message = "Unsupported host type";
+ error_resp.message = "Unsupported host/subnet type";
return false;
}
na = std::move(*na_parsed);
@@ -1955,7 +2179,7 @@ namespace cryptonote
PERF_TIMER(on_get_alternate_chains);
try
{
- std::list<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
+ std::vector<std::pair<Blockchain::block_extended_info, std::vector<crypto::hash>>> chains = m_core.get_blockchain_storage().get_alternative_chains();
for (const auto &i: chains)
{
difficulty_type wdiff = i.first.cumulative_difficulty;
@@ -2039,7 +2263,9 @@ namespace cryptonote
bool core_rpc_server::on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx)
{
PERF_TIMER(on_out_peers);
- m_p2p.change_max_out_public_peers(req.out_peers);
+ if (req.set)
+ m_p2p.change_max_out_public_peers(req.out_peers);
+ res.out_peers = m_p2p.get_max_out_public_peers();
res.status = CORE_RPC_STATUS_OK;
return true;
}
@@ -2047,27 +2273,13 @@ namespace cryptonote
bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx)
{
PERF_TIMER(on_in_peers);
- m_p2p.change_max_in_public_peers(req.in_peers);
+ if (req.set)
+ m_p2p.change_max_in_public_peers(req.in_peers);
+ res.in_peers = m_p2p.get_max_in_public_peers();
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx)
- {
- PERF_TIMER(on_start_save_graph);
- m_p2p.set_save_graph(true);
- res.status = CORE_RPC_STATUS_OK;
- return true;
- }
- //------------------------------------------------------------------------------------------------------------------------------
- bool core_rpc_server::on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx)
- {
- PERF_TIMER(on_stop_save_graph);
- m_p2p.set_save_graph(false);
- res.status = CORE_RPC_STATUS_OK;
- return true;
- }
- //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx)
{
PERF_TIMER(on_update);
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index e4683bbe2..e91d4c953 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -108,6 +108,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC, !m_restricted)
MAP_URI_AUTO_JON2_IF("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST, !m_restricted)
+ MAP_URI_AUTO_JON2_IF("/get_public_nodes", on_get_public_nodes, COMMAND_RPC_GET_PUBLIC_NODES, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted)
MAP_URI_AUTO_JON2_IF("/set_log_categories", on_set_log_categories, COMMAND_RPC_SET_LOG_CATEGORIES, !m_restricted)
@@ -115,6 +116,7 @@ namespace cryptonote
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes.bin", on_get_transaction_pool_hashes_bin, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN)
MAP_URI_AUTO_JON2("/get_transaction_pool_hashes", on_get_transaction_pool_hashes, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES)
MAP_URI_AUTO_JON2("/get_transaction_pool_stats", on_get_transaction_pool_stats, COMMAND_RPC_GET_TRANSACTION_POOL_STATS)
+ MAP_URI_AUTO_JON2_IF("/set_bootstrap_daemon", on_set_bootstrap_daemon, COMMAND_RPC_SET_BOOTSTRAP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO)
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
@@ -123,8 +125,6 @@ namespace cryptonote
MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted)
MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted)
MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted)
- MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted)
- MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted)
MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS)
MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted)
MAP_URI_AUTO_BIN2("/get_output_distribution.bin", on_get_output_distribution_bin, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION)
@@ -154,6 +154,7 @@ namespace cryptonote
MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO)
MAP_JON_RPC_WE_IF("set_bans", on_set_bans, COMMAND_RPC_SETBANS, !m_restricted)
MAP_JON_RPC_WE_IF("get_bans", on_get_bans, COMMAND_RPC_GETBANS, !m_restricted)
+ MAP_JON_RPC_WE_IF("banned", on_banned, COMMAND_RPC_BANNED, !m_restricted)
MAP_JON_RPC_WE_IF("flush_txpool", on_flush_txpool, COMMAND_RPC_FLUSH_TRANSACTION_POOL, !m_restricted)
MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION)
@@ -186,6 +187,7 @@ namespace cryptonote
bool on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx = NULL);
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx = NULL);
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx = NULL);
+ bool on_get_public_nodes(const COMMAND_RPC_GET_PUBLIC_NODES::request& req, COMMAND_RPC_GET_PUBLIC_NODES::response& res, const connection_context *ctx = NULL);
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx = NULL);
bool on_set_log_level(const COMMAND_RPC_SET_LOG_LEVEL::request& req, COMMAND_RPC_SET_LOG_LEVEL::response& res, const connection_context *ctx = NULL);
bool on_set_log_categories(const COMMAND_RPC_SET_LOG_CATEGORIES::request& req, COMMAND_RPC_SET_LOG_CATEGORIES::response& res, const connection_context *ctx = NULL);
@@ -193,13 +195,12 @@ namespace cryptonote
bool on_get_transaction_pool_hashes_bin(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, const connection_context *ctx = NULL);
bool on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, const connection_context *ctx = NULL);
+ bool on_set_bootstrap_daemon(const COMMAND_RPC_SET_BOOTSTRAP_DAEMON::request& req, COMMAND_RPC_SET_BOOTSTRAP_DAEMON::response& res, const connection_context *ctx = NULL);
bool on_stop_daemon(const COMMAND_RPC_STOP_DAEMON::request& req, COMMAND_RPC_STOP_DAEMON::response& res, const connection_context *ctx = NULL);
bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res, const connection_context *ctx = NULL);
bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res, const connection_context *ctx = NULL);
bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res, const connection_context *ctx = NULL);
- bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res, const connection_context *ctx = NULL);
- bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res, const connection_context *ctx = NULL);
bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx = NULL);
bool on_get_output_distribution_bin(const COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request& req, COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response& res, const connection_context *ctx = NULL);
bool on_pop_blocks(const COMMAND_RPC_POP_BLOCKS::request& req, COMMAND_RPC_POP_BLOCKS::response& res, const connection_context *ctx = NULL);
@@ -220,6 +221,7 @@ namespace cryptonote
bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+ bool on_banned(const COMMAND_RPC_BANNED::request& req, COMMAND_RPC_BANNED::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_flush_txpool(const COMMAND_RPC_FLUSH_TRANSACTION_POOL::request& req, COMMAND_RPC_FLUSH_TRANSACTION_POOL::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
@@ -236,10 +238,13 @@ namespace cryptonote
private:
bool check_core_busy();
bool check_core_ready();
+ bool add_host_fail(const connection_context *ctx);
//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);
+ 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 };
template <typename COMMAND_TYPE>
bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r);
@@ -254,6 +259,8 @@ private:
bool m_was_bootstrap_ever_used;
network_type m_nettype;
bool m_restricted;
+ epee::critical_section m_host_fails_score_lock;
+ std::map<std::string, uint64_t> m_host_fails_score;
};
}
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index cfe4bbf23..aed967efb 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -29,6 +29,9 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
+
+#include "string_tools.h"
+
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/difficulty.h"
@@ -84,7 +87,7 @@ namespace cryptonote
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 2
-#define CORE_RPC_VERSION_MINOR 6
+#define CORE_RPC_VERSION_MINOR 9
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@@ -609,6 +612,7 @@ namespace cryptonote
bool overspend;
bool fee_too_low;
bool not_rct;
+ bool too_few_outputs;
bool sanity_check_failed;
bool untrusted;
@@ -624,6 +628,7 @@ namespace cryptonote
KV_SERIALIZE(overspend)
KV_SERIALIZE(fee_too_low)
KV_SERIALIZE(not_rct)
+ KV_SERIALIZE(too_few_outputs)
KV_SERIALIZE(sanity_check_failed)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
@@ -919,11 +924,13 @@ namespace cryptonote
uint64_t reserve_size; //max 255 bytes
std::string wallet_address;
std::string prev_block;
+ std::string extra_nonce;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(reserve_size)
KV_SERIALIZE(wallet_address)
KV_SERIALIZE(prev_block)
+ KV_SERIALIZE(extra_nonce)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -1091,10 +1098,12 @@ namespace cryptonote
struct request_t
{
std::string hash;
+ std::vector<std::string> hashes;
bool fill_pow_hash;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(hash)
+ KV_SERIALIZE(hashes)
KV_SERIALIZE_OPT(fill_pow_hash, false);
END_KV_SERIALIZE_MAP()
};
@@ -1104,10 +1113,12 @@ namespace cryptonote
{
std::string status;
block_header_response block_header;
+ std::vector<block_header_response> block_headers;
bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(block_header)
+ KV_SERIALIZE(block_headers)
KV_SERIALIZE(status)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
@@ -1197,8 +1208,11 @@ namespace cryptonote
peer(uint64_t id, const std::string &host, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
: id(id), host(host), ip(0), port(0), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
+ peer(uint64_t id, const std::string &host, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
+ : id(id), host(host), ip(0), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ {}
peer(uint64_t id, uint32_t ip, uint16_t port, uint64_t last_seen, uint32_t pruning_seed, uint16_t rpc_port)
- : id(id), host(std::to_string(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
+ : id(id), host(epee::string_tools::get_ip_string_from_int32(ip)), ip(ip), port(port), rpc_port(rpc_port), last_seen(last_seen), pruning_seed(pruning_seed)
{}
BEGIN_KV_SERIALIZE_MAP()
@@ -1216,7 +1230,10 @@ namespace cryptonote
{
struct request_t
{
+ bool public_only;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(public_only, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -1236,6 +1253,54 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct public_node
+ {
+ std::string host;
+ uint64_t last_seen;
+ uint16_t rpc_port;
+
+ public_node() = delete;
+
+ public_node(const peer &peer)
+ : host(peer.host), last_seen(peer.last_seen), rpc_port(peer.rpc_port)
+ {}
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(host)
+ KV_SERIALIZE(last_seen)
+ KV_SERIALIZE(rpc_port)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct COMMAND_RPC_GET_PUBLIC_NODES
+ {
+ struct request_t
+ {
+ bool gray;
+ bool white;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(gray, false)
+ KV_SERIALIZE_OPT(white, true)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ std::vector<public_node> gray;
+ std::vector<public_node> white;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(gray)
+ KV_SERIALIZE(white)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_SET_LOG_HASH_RATE
{
struct request_t
@@ -1583,6 +1648,33 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_SET_BOOTSTRAP_DAEMON
+ {
+ struct request_t
+ {
+ std::string address;
+ std::string username;
+ std::string password;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ KV_SERIALIZE(username)
+ KV_SERIALIZE(password)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_STOP_DAEMON
{
struct request_t
@@ -1682,8 +1774,10 @@ namespace cryptonote
{
struct request_t
{
- uint64_t out_peers;
+ bool set;
+ uint32_t out_peers;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(out_peers)
END_KV_SERIALIZE_MAP()
};
@@ -1691,9 +1785,11 @@ namespace cryptonote
struct response_t
{
+ uint32_t out_peers;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(out_peers)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
@@ -1704,8 +1800,10 @@ namespace cryptonote
{
struct request_t
{
- uint64_t in_peers;
+ bool set;
+ uint32_t in_peers;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(set, true)
KV_SERIALIZE(in_peers)
END_KV_SERIALIZE_MAP()
};
@@ -1713,55 +1811,17 @@ namespace cryptonote
struct response_t
{
+ uint32_t in_peers;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(in_peers)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
- struct COMMAND_RPC_START_SAVE_GRAPH
- {
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string status;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
- struct COMMAND_RPC_STOP_SAVE_GRAPH
- {
- struct request_t
- {
- BEGIN_KV_SERIALIZE_MAP()
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<request_t> request;
-
- struct response_t
- {
- std::string status;
-
- BEGIN_KV_SERIALIZE_MAP()
- KV_SERIALIZE(status)
- END_KV_SERIALIZE_MAP()
- };
- typedef epee::misc_utils::struct_init<response_t> response;
- };
-
struct COMMAND_RPC_HARD_FORK_INFO
{
struct request_t
@@ -1876,6 +1936,33 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_BANNED
+ {
+ struct request_t
+ {
+ std::string address;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(address)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string status;
+ bool banned;
+ uint32_t seconds;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(status)
+ KV_SERIALIZE(banned)
+ KV_SERIALIZE(seconds)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
struct COMMAND_RPC_FLUSH_TRANSACTION_POOL
{
struct request_t
@@ -2070,7 +2157,7 @@ namespace cryptonote
struct response_t
{
std::string status;
- std::list<chain_info> chains;
+ std::vector<chain_info> chains;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 612b2cab6..890380dc8 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -343,6 +343,11 @@ namespace rpc
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 ";
+ res.error_details = "too few outputs";
+ }
if (res.error_details.empty())
{
res.error_details = "an unknown issue was found with the transaction";
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index 4479bd1f1..68b33cb8c 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -90,6 +90,9 @@ namespace cryptonote
rpc_args::descriptors::descriptors()
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"})
+ , rpc_bind_ipv6_address({"rpc-bind-ipv6-address", rpc_args::tr("Specify IPv6 address to bind RPC server"), "::1"})
+ , rpc_use_ipv6({"rpc-use-ipv6", rpc_args::tr("Allow IPv6 for RPC"), false})
+ , rpc_require_ipv4({"rpc-require-ipv4", rpc_args::tr("Require successful IPv4 bind for RPC"), true})
, rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
, confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
, rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
@@ -108,6 +111,9 @@ namespace cryptonote
{
const descriptors arg{};
command_line::add_arg(desc, arg.rpc_bind_ip);
+ command_line::add_arg(desc, arg.rpc_bind_ipv6_address);
+ command_line::add_arg(desc, arg.rpc_use_ipv6);
+ command_line::add_arg(desc, arg.rpc_require_ipv4);
command_line::add_arg(desc, arg.rpc_login);
command_line::add_arg(desc, arg.confirm_external_bind);
command_line::add_arg(desc, arg.rpc_access_control_origins);
@@ -127,6 +133,9 @@ namespace cryptonote
rpc_args config{};
config.bind_ip = command_line::get_arg(vm, arg.rpc_bind_ip);
+ config.bind_ipv6_address = command_line::get_arg(vm, arg.rpc_bind_ipv6_address);
+ config.use_ipv6 = command_line::get_arg(vm, arg.rpc_use_ipv6);
+ config.require_ipv4 = command_line::get_arg(vm, arg.rpc_require_ipv4);
if (!config.bind_ip.empty())
{
// always parse IP here for error consistency
@@ -148,6 +157,34 @@ namespace cryptonote
return boost::none;
}
}
+ if (!config.bind_ipv6_address.empty())
+ {
+ // allow square braces, but remove them here if present
+ if (config.bind_ipv6_address.find('[') != std::string::npos)
+ {
+ config.bind_ipv6_address = config.bind_ipv6_address.substr(1, config.bind_ipv6_address.size() - 2);
+ }
+
+
+ // always parse IP here for error consistency
+ boost::system::error_code ec{};
+ const auto parsed_ip = boost::asio::ip::address::from_string(config.bind_ipv6_address, ec);
+ if (ec)
+ {
+ LOG_ERROR(tr("Invalid IP address given for --") << arg.rpc_bind_ipv6_address.name);
+ return boost::none;
+ }
+
+ if (!parsed_ip.is_loopback() && !command_line::get_arg(vm, arg.confirm_external_bind))
+ {
+ LOG_ERROR(
+ "--" << arg.rpc_bind_ipv6_address.name <<
+ tr(" permits inbound unencrypted external connections. Consider SSH tunnel or SSL proxy instead. Override with --") <<
+ arg.confirm_external_bind.name
+ );
+ return boost::none;
+ }
+ }
const char *env_rpc_login = nullptr;
const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login);
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index 619f02b42..cd154a4d0 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -52,6 +52,9 @@ namespace cryptonote
descriptors& operator=(descriptors&&) = delete;
const command_line::arg_descriptor<std::string> rpc_bind_ip;
+ const command_line::arg_descriptor<std::string> rpc_bind_ipv6_address;
+ const command_line::arg_descriptor<bool> rpc_use_ipv6;
+ const command_line::arg_descriptor<bool> rpc_require_ipv4;
const command_line::arg_descriptor<std::string> rpc_login;
const command_line::arg_descriptor<bool> confirm_external_bind;
const command_line::arg_descriptor<std::string> rpc_access_control_origins;
@@ -76,6 +79,9 @@ namespace cryptonote
static boost::optional<epee::net_utils::ssl_options_t> process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option = false);
std::string bind_ip;
+ std::string bind_ipv6_address;
+ bool use_ipv6;
+ bool require_ipv4;
std::vector<std::string> access_control_origins;
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp
index af5cb98a3..d528ffef3 100644
--- a/src/rpc/rpc_handler.cpp
+++ b/src/rpc/rpc_handler.cpp
@@ -63,7 +63,9 @@ namespace rpc
d.cached_to -= 10;
d.cached_top_hash = hash10;
d.cached_m10_hash = crypto::null_hash;
- d.cached_distribution.resize(d.cached_distribution.size() - 10);
+ CHECK_AND_ASSERT_MES(d.cached_distribution.size() >= 10, boost::none, "Cached distribution size does not match cached bounds");
+ for (int p = 0; p < 10; ++p)
+ d.cached_distribution.pop_back();
can_extend = true;
}
}
diff --git a/src/rpc/zmq_server.cpp b/src/rpc/zmq_server.cpp
index ae748e052..668a2e5cd 100644
--- a/src/rpc/zmq_server.cpp
+++ b/src/rpc/zmq_server.cpp
@@ -59,7 +59,7 @@ void ZmqServer::serve()
{
throw std::runtime_error("ZMQ RPC server reply socket is null");
}
- while (rep_socket->recv(&message))
+ while (rep_socket->recv(&message, 0))
{
std::string message_string(reinterpret_cast<const char *>(message.data()), message.size());
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index 73e17a775..cc52bde58 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -566,6 +566,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::connection_info& in
INSERT_INTO_JSON_OBJECT(val, doc, incoming, info.incoming);
INSERT_INTO_JSON_OBJECT(val, doc, localhost, info.localhost);
INSERT_INTO_JSON_OBJECT(val, doc, local_ip, info.local_ip);
+ INSERT_INTO_JSON_OBJECT(val, doc, address_type, info.address_type);
INSERT_INTO_JSON_OBJECT(val, doc, ip, info.ip);
INSERT_INTO_JSON_OBJECT(val, doc, port, info.port);
@@ -601,6 +602,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::connection_info& inf
GET_FROM_JSON_OBJECT(val, info.incoming, incoming);
GET_FROM_JSON_OBJECT(val, info.localhost, localhost);
GET_FROM_JSON_OBJECT(val, info.local_ip, local_ip);
+ GET_FROM_JSON_OBJECT(val, info.address_type, address_type);
GET_FROM_JSON_OBJECT(val, info.ip, ip);
GET_FROM_JSON_OBJECT(val, info.port, port);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 02a099811..24a6eec43 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -92,6 +92,8 @@ typedef cryptonote::simple_wallet sw;
#define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol
+#define OLD_AGE_WARN_THRESHOLD (30 * 86400 / DIFFICULTY_TARGET_V2) // 30 days
+
#define LOCK_IDLE_SCOPE() \
bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \
@@ -177,8 +179,8 @@ namespace
" account tag <tag_name> <account_index_1> [<account_index_2> ...]\n"
" account untag <account_index_1> [<account_index_2> ...]\n"
" account tag_description <tag_name> <description>");
- const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]");
- const char* USAGE_INTEGRATED_ADDRESS("integrated_address [<payment_id> | <address>]");
+ const char* USAGE_ADDRESS("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> | device [<index>]]");
+ const char* USAGE_INTEGRATED_ADDRESS("integrated_address [device] [<payment_id> | <address>]");
const char* USAGE_ADDRESS_BOOK("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]");
const char* USAGE_SET_VARIABLE("set <option> [<value>]");
const char* USAGE_GET_TX_KEY("get_tx_key <txid>");
@@ -199,11 +201,11 @@ namespace
const char* USAGE_SET_DESCRIPTION("set_description [free text note]");
const char* USAGE_SIGN("sign <filename>");
const char* USAGE_VERIFY("verify <filename> <address> <signature>");
- const char* USAGE_EXPORT_KEY_IMAGES("export_key_images <filename>");
+ const char* USAGE_EXPORT_KEY_IMAGES("export_key_images [all] <filename>");
const char* USAGE_IMPORT_KEY_IMAGES("import_key_images <filename>");
const char* USAGE_HW_KEY_IMAGES_SYNC("hw_key_images_sync");
const char* USAGE_HW_RECONNECT("hw_reconnect");
- const char* USAGE_EXPORT_OUTPUTS("export_outputs <filename>");
+ const char* USAGE_EXPORT_OUTPUTS("export_outputs [all] <filename>");
const char* USAGE_IMPORT_OUTPUTS("import_outputs <filename>");
const char* USAGE_SHOW_TRANSFER("show_transfer <txid>");
const char* USAGE_MAKE_MULTISIG("make_multisig <threshold> <string1> [<string>...]");
@@ -801,6 +803,12 @@ bool simple_wallet::encrypted_seed(const std::vector<std::string> &args/* = std:
return print_seed(true);
}
+bool simple_wallet::restore_height(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
+{
+ success_msg_writer() << m_wallet->get_refresh_from_block_height();
+ return true;
+}
+
bool simple_wallet::seed_set_language(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if (m_wallet->key_on_device())
@@ -2843,6 +2851,9 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("seed",
boost::bind(&simple_wallet::seed, this, _1),
tr("Display the Electrum-style mnemonic seed"));
+ m_cmd_binder.set_handler("restore_height",
+ boost::bind(&simple_wallet::restore_height, this, _1),
+ tr("Display the restore height"));
m_cmd_binder.set_handler("set",
boost::bind(&simple_wallet::set_variable, this, _1),
tr(USAGE_SET_VARIABLE),
@@ -2886,7 +2897,7 @@ simple_wallet::simple_wallet()
"segregate-pre-fork-outputs <1|0>\n "
" Set this if you intend to spend outputs on both Monero AND a key reusing fork.\n "
"key-reuse-mitigation2 <1|0>\n "
- " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n"
+ " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n "
"subaddress-lookahead <major>:<minor>\n "
" Set the lookahead sizes for the subaddress hash table.\n "
" Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n "
@@ -3128,7 +3139,7 @@ simple_wallet::simple_wallet()
tr("Available options:\n "
"auto-send <1|0>\n "
" Whether to automatically send newly generated messages right away.\n "));
- m_cmd_binder.set_handler("mms send_message_config",
+ m_cmd_binder.set_handler("mms send_signer_config",
boost::bind(&simple_wallet::mms, this, _1),
tr(USAGE_MMS_SEND_SIGNER_CONFIG),
tr("Send completed signer config to all other authorized signers"));
@@ -3970,7 +3981,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (m_restoring && m_generate_from_json.empty() && m_generate_from_device.empty())
{
- m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) ||
+ m_wallet->explicit_refresh_from_block_height(!(command_line::is_arg_defaulted(vm, arg_restore_height) &&
command_line::is_arg_defaulted(vm, arg_restore_date)));
if (command_line::is_arg_defaulted(vm, arg_restore_height) && !command_line::is_arg_defaulted(vm, arg_restore_date))
{
@@ -4087,7 +4098,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
m_wallet->callback(this);
- check_background_mining(password);
+ bool skip_check_backround_mining = !command_line::get_arg(vm, arg_command).empty();
+ if (!skip_check_backround_mining)
+ check_background_mining(password);
if (welcome)
message_writer(console_color_yellow, true) << tr("If you are new to Monero, type \"welcome\" for a brief overview.");
@@ -4237,7 +4250,9 @@ boost::optional<tools::password_container> simple_wallet::get_and_verify_passwor
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const crypto::secret_key& recovery_key, bool recover, bool two_random, const std::string &old_language)
{
- auto rc = tools::wallet2::make_new(vm, false, password_prompter);
+ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc;
+ try { rc = tools::wallet2::make_new(vm, false, password_prompter); }
+ catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; }
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -4332,7 +4347,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey)
{
- auto rc = tools::wallet2::make_new(vm, false, password_prompter);
+ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc;
+ try { rc = tools::wallet2::make_new(vm, false, password_prompter); }
+ catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; }
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -4378,7 +4395,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm)
{
- auto rc = tools::wallet2::make_new(vm, false, password_prompter);
+ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc;
+ try { rc = tools::wallet2::make_new(vm, false, password_prompter); }
+ catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; }
m_wallet = std::move(rc.first);
m_wallet->callback(this);
if (!m_wallet)
@@ -4419,7 +4438,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const epee::wipeable_string &multisig_keys, const std::string &old_language)
{
- auto rc = tools::wallet2::make_new(vm, false, password_prompter);
+ std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc;
+ try { rc = tools::wallet2::make_new(vm, false, password_prompter); }
+ catch(const std::exception &e) { fail_msg_writer() << tr("Error creating wallet: ") << e.what(); return {}; }
m_wallet = std::move(rc.first);
if (!m_wallet)
{
@@ -5608,6 +5629,43 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector)
+{
+ // count the number of old outputs
+ std::string err;
+ uint64_t bc_height = get_daemon_blockchain_height(err);
+ if (!err.empty())
+ return true;
+
+ int max_n_old = 0;
+ for (const auto &ptx: ptx_vector)
+ {
+ int n_old = 0;
+ for (const auto i: ptx.selected_transfers)
+ {
+ const tools::wallet2::transfer_details &td = m_wallet->get_transfer_details(i);
+ uint64_t age = bc_height - td.m_block_height;
+ if (age > OLD_AGE_WARN_THRESHOLD)
+ ++n_old;
+ }
+ max_n_old = std::max(max_n_old, n_old);
+ }
+ if (max_n_old > 1)
+ {
+ std::stringstream prompt;
+ prompt << tr("Transaction spends more than one very old output. Privacy would be better if they were sent separately.");
+ prompt << ENDL << tr("Spend them now anyway?");
+ std::string accepted = input_line(prompt.str(), true);
+ if (std::cin.eof())
+ return false;
+ if (!command_line::is_yes(accepted))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::string> &args_, bool called_by_mms)
{
// "transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"
@@ -5909,6 +5967,12 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
}
}
+ if (!prompt_if_old(ptx_vector))
+ {
+ fail_msg_writer() << tr("transaction cancelled.");
+ return false;
+ }
+
// if more than one tx necessary, prompt user to confirm
if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
{
@@ -6092,7 +6156,8 @@ bool simple_wallet::locked_transfer(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::locked_sweep_all(const std::vector<std::string> &args_)
{
- return sweep_main(0, true, args_);
+ sweep_main(0, true, args_);
+ return true;
}
//----------------------------------------------------------------------------------------------------
@@ -6412,6 +6477,12 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
return true;
}
+ if (!prompt_if_old(ptx_vector))
+ {
+ fail_msg_writer() << tr("transaction cancelled.");
+ return false;
+ }
+
// give user total and fee, and prompt to confirm
uint64_t total_fee = 0, total_sent = 0;
for (size_t n = 0; n < ptx_vector.size(); ++n)
@@ -6758,7 +6829,8 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_all(const std::vector<std::string> &args_)
{
- return sweep_main(0, false, args_);
+ sweep_main(0, false, args_);
+ return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
@@ -6774,7 +6846,8 @@ bool simple_wallet::sweep_below(const std::vector<std::string> &args_)
fail_msg_writer() << tr("invalid amount threshold");
return true;
}
- return sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end()));
+ sweep_main(below, false, std::vector<std::string>(++args_.begin(), args_.end()));
+ return true;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::donate(const std::vector<std::string> &args_)
@@ -7747,7 +7820,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
std::vector<std::pair<std::string, uint64_t>> destinations;
for (const auto &d: pd.m_dests) {
- destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
+ destinations.push_back({d.address(m_wallet->nettype(), pd.m_payment_id), d.amount});
}
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
@@ -7823,7 +7896,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
uint64_t fee = amount - pd.m_amount_out;
std::vector<std::pair<std::string, uint64_t>> destinations;
for (const auto &d: pd.m_dests) {
- destinations.push_back({get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr), d.amount});
+ destinations.push_back({d.address(m_wallet->nettype(), pd.m_payment_id), d.amount});
}
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
@@ -8501,6 +8574,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
// address all
// address <index_min> [<index_max>]
// address label <index> <label text with white spaces allowed>
+ // address device [<index>]
std::vector<std::string> local_args = args;
tools::wallet2::transfer_container transfers;
@@ -8537,6 +8611,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
label = tr("(Untitled address)");
m_wallet->add_subaddress(m_current_subaddress_account, label);
print_address_sub(m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1);
+ m_wallet->device_show_address(m_current_subaddress_account, m_wallet->get_num_subaddresses(m_current_subaddress_account) - 1, boost::none);
}
else if (local_args.size() >= 2 && local_args[0] == "label")
{
@@ -8585,6 +8660,27 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
for (index = index_min; index <= index_max; ++index)
print_address_sub(index);
}
+ else if (local_args[0] == "device")
+ {
+ index = 0;
+ local_args.erase(local_args.begin());
+ if (local_args.size() > 0)
+ {
+ if (!epee::string_tools::get_xtype_from_string(index, local_args[0]))
+ {
+ fail_msg_writer() << tr("failed to parse index: ") << local_args[0];
+ return true;
+ }
+ if (index >= m_wallet->get_num_subaddresses(m_current_subaddress_account))
+ {
+ fail_msg_writer() << tr("<index> is out of bounds");
+ return true;
+ }
+ }
+
+ print_address_sub(index);
+ m_wallet->device_show_address(m_current_subaddress_account, index, boost::none);
+ }
else
{
PRINT_USAGE(USAGE_ADDRESS);
@@ -8596,12 +8692,29 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
bool simple_wallet::print_integrated_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
crypto::hash8 payment_id;
- if (args.size() > 1)
+ bool display_on_device = false;
+ std::vector<std::string> local_args = args;
+
+ if (local_args.size() > 0 && local_args[0] == "device")
+ {
+ local_args.erase(local_args.begin());
+ display_on_device = true;
+ }
+
+ auto device_show_integrated = [this, display_on_device](crypto::hash8 payment_id)
+ {
+ if (display_on_device)
+ {
+ m_wallet->device_show_address(m_current_subaddress_account, 0, payment_id);
+ }
+ };
+
+ if (local_args.size() > 1)
{
PRINT_USAGE(USAGE_INTEGRATED_ADDRESS);
return true;
}
- if (args.size() == 0)
+ if (local_args.size() == 0)
{
if (m_current_subaddress_account != 0)
{
@@ -8611,9 +8724,10 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg
payment_id = crypto::rand<crypto::hash8>();
success_msg_writer() << tr("Random payment ID: ") << payment_id;
success_msg_writer() << tr("Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
+ device_show_integrated(payment_id);
return true;
}
- if(tools::wallet2::parse_short_payment_id(args.back(), payment_id))
+ if(tools::wallet2::parse_short_payment_id(local_args.back(), payment_id))
{
if (m_current_subaddress_account != 0)
{
@@ -8621,16 +8735,18 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg
return true;
}
success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->nettype());
+ device_show_integrated(payment_id);
return true;
}
else {
address_parse_info info;
- if(get_account_address_from_str(info, m_wallet->nettype(), args.back()))
+ if(get_account_address_from_str(info, m_wallet->nettype(), local_args.back()))
{
if (info.has_payment_id)
{
success_msg_writer() << boost::format(tr("Integrated address: %s, payment ID: %s")) %
get_account_address_as_str(m_wallet->nettype(), false, info.address) % epee::string_tools::pod_to_hex(info.payment_id);
+ device_show_integrated(info.payment_id);
}
else
{
@@ -8937,21 +9053,31 @@ bool simple_wallet::verify(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::export_key_images(const std::vector<std::string> &args)
+bool simple_wallet::export_key_images(const std::vector<std::string> &args_)
{
if (m_wallet->key_on_device())
{
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
- if (args.size() != 1)
+ auto args = args_;
+
+ if (m_wallet->watch_only())
{
- PRINT_USAGE(USAGE_EXPORT_KEY_IMAGES);
+ fail_msg_writer() << tr("wallet is watch-only and cannot export key images");
return true;
}
- if (m_wallet->watch_only())
+
+ bool all = false;
+ if (args.size() >= 2 && args[0] == "all")
{
- fail_msg_writer() << tr("wallet is watch-only and cannot export key images");
+ all = true;
+ args.erase(args.begin());
+ }
+
+ if (args.size() != 1)
+ {
+ PRINT_USAGE(USAGE_EXPORT_KEY_IMAGES);
return true;
}
@@ -8963,7 +9089,7 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args)
try
{
- if (!m_wallet->export_key_images(filename))
+ if (!m_wallet->export_key_images(filename, all))
{
fail_msg_writer() << tr("failed to save file ") << filename;
return true;
@@ -9088,13 +9214,22 @@ bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
-bool simple_wallet::export_outputs(const std::vector<std::string> &args)
+bool simple_wallet::export_outputs(const std::vector<std::string> &args_)
{
if (m_wallet->key_on_device())
{
fail_msg_writer() << tr("command not supported by HW wallet");
return true;
}
+ auto args = args_;
+
+ bool all = false;
+ if (args.size() >= 2 && args[0] == "all")
+ {
+ all = true;
+ args.erase(args.begin());
+ }
+
if (args.size() != 1)
{
PRINT_USAGE(USAGE_EXPORT_OUTPUTS);
@@ -9109,7 +9244,7 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args)
try
{
- std::string data = m_wallet->export_outputs_to_str();
+ std::string data = m_wallet->export_outputs_to_str(all);
bool r = epee::file_io_utils::save_string_to_file(filename, data);
if (!r)
{
@@ -9236,7 +9371,7 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args)
for (const auto &d: pd.m_dests) {
if (!dests.empty())
dests += ", ";
- dests += get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) + ": " + print_money(d.amount);
+ dests += d.address(m_wallet->nettype(), pd.m_payment_id) + ": " + print_money(d.amount);
}
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 33b18612c..2de390666 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -109,6 +109,7 @@ namespace cryptonote
bool spendkey(const std::vector<std::string> &args = std::vector<std::string>());
bool seed(const std::vector<std::string> &args = std::vector<std::string>());
bool encrypted_seed(const std::vector<std::string> &args = std::vector<std::string>());
+ bool restore_height(const std::vector<std::string> &args = std::vector<std::string>());
/*!
* \brief Sets seed language.
@@ -261,6 +262,7 @@ namespace cryptonote
void on_refresh_finished(uint64_t start_height, uint64_t fetched_blocks, bool is_init, bool received_money);
std::pair<std::string, std::string> show_outputs_line(const std::vector<uint64_t> &heights, uint64_t blockchain_height, uint64_t highlight_height = std::numeric_limits<uint64_t>::max()) const;
bool freeze_thaw(const std::vector<std::string>& args, bool freeze);
+ bool prompt_if_old(const std::vector<tools::wallet2::pending_tx> &ptx_vector);
struct transfer_view
{
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp
index f4ad8b1f6..ad7029a3c 100644
--- a/src/wallet/api/transaction_history.cpp
+++ b/src/wallet/api/transaction_history.cpp
@@ -181,7 +181,7 @@ void TransactionHistoryImpl::refresh()
// single output transaction might contain multiple transfers
for (const auto &d: pd.m_dests) {
- ti->m_transfers.push_back({d.amount, get_account_address_as_str(m_wallet->m_wallet->nettype(), d.is_subaddress, d.addr)});
+ ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id)});
}
m_history.push_back(ti);
}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 1711db482..e632b8d23 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1407,8 +1407,7 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat
// - unconfirmed_transfer_details;
// - confirmed_transfer_details)
-PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count,
- PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
+PendingTransaction *WalletImpl::createTransactionMultDest(const std::vector<string> &dst_addr, const string &payment_id, optional<std::vector<uint64_t>> amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
{
clearStatus();
@@ -1429,75 +1428,75 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
do {
- if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr)) {
- // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
- setStatusError(tr("Invalid destination address"));
+ std::vector<uint8_t> extra;
+ std::string extra_nonce;
+ vector<cryptonote::tx_destination_entry> dsts;
+ if (!amount && dst_addr.size() > 1) {
+ setStatusError(tr("Sending all requires one destination address"));
break;
}
-
-
- std::vector<uint8_t> extra;
- // if dst_addr is not an integrated address, parse payment_id
- if (!info.has_payment_id && !payment_id.empty()) {
- // copy-pasted from simplewallet.cpp:2212
+ if (amount && (dst_addr.size() != (*amount).size())) {
+ setStatusError(tr("Destinations and amounts are unequal"));
+ break;
+ }
+ if (!payment_id.empty()) {
crypto::hash payment_id_long;
- bool r = tools::wallet2::parse_long_payment_id(payment_id, payment_id_long);
- if (r) {
- std::string extra_nonce;
+ if (tools::wallet2::parse_long_payment_id(payment_id, payment_id_long)) {
cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id_long);
- r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
} else {
- r = tools::wallet2::parse_short_payment_id(payment_id, info.payment_id);
- if (r) {
- std::string extra_nonce;
- set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
- r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
- }
- }
-
- if (!r) {
- setStatusError(tr("payment id has invalid format, expected 16 or 64 character hex string: ") + payment_id);
+ setStatusError(tr("payment id has invalid format, expected 64 character hex string: ") + payment_id);
break;
}
}
- else if (info.has_payment_id) {
- std::string extra_nonce;
- set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
- bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
- if (!r) {
- setStatusError(tr("Failed to add short payment id: ") + epee::string_tools::pod_to_hex(info.payment_id));
+ bool error = false;
+ for (size_t i = 0; i < dst_addr.size() && !error; i++) {
+ if(!cryptonote::get_account_address_from_str(info, m_wallet->nettype(), dst_addr[i])) {
+ // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982
+ setStatusError(tr("Invalid destination address"));
+ error = true;
break;
}
- }
-
-
- //std::vector<tools::wallet2::pending_tx> ptx_vector;
+ if (info.has_payment_id) {
+ if (!extra_nonce.empty()) {
+ setStatusError(tr("a single transaction cannot use more than one payment id"));
+ error = true;
+ break;
+ }
+ set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, info.payment_id);
+ }
- try {
if (amount) {
- vector<cryptonote::tx_destination_entry> dsts;
cryptonote::tx_destination_entry de;
- de.original = dst_addr;
+ de.original = dst_addr[i];
de.addr = info.address;
- de.amount = *amount;
+ de.amount = (*amount)[i];
de.is_subaddress = info.is_subaddress;
de.is_integrated = info.has_payment_id;
dsts.push_back(de);
- transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */,
- adjusted_priority,
- extra, subaddr_account, subaddr_indices);
} else {
- // for the GUI, sweep_all (i.e. amount set as "(all)") will always sweep all the funds in all the addresses
- if (subaddr_indices.empty())
- {
+ if (subaddr_indices.empty()) {
for (uint32_t index = 0; index < m_wallet->get_num_subaddresses(subaddr_account); ++index)
subaddr_indices.insert(index);
}
+ }
+ }
+ if (error) {
+ break;
+ }
+ if (!extra_nonce.empty() && !add_extra_nonce_to_tx_extra(extra, extra_nonce)) {
+ setStatusError(tr("failed to set up payment id, though it was decoded correctly"));
+ break;
+ }
+ try {
+ if (amount) {
+ transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */,
+ adjusted_priority,
+ extra, subaddr_account, subaddr_indices);
+ } else {
transaction->m_pending_tx = m_wallet->create_transactions_all(0, info.address, info.is_subaddress, 1, fake_outs_count, 0 /* unlock_time */,
- adjusted_priority,
- extra, subaddr_account, subaddr_indices);
+ adjusted_priority,
+ extra, subaddr_account, subaddr_indices);
}
-
pendingTxPostProcess(transaction);
if (multisig().isMultisig) {
@@ -1574,6 +1573,13 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
return transaction;
}
+PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count,
+ PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
+
+{
+ return createTransactionMultDest(std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {*amount}) : (optional<std::vector<uint64_t>>()), mixin_count, priority, subaddr_account, subaddr_indices);
+}
+
PendingTransaction *WalletImpl::createSweepUnmixableTransaction()
{
@@ -1697,6 +1703,19 @@ void WalletImpl::setDefaultMixin(uint32_t arg)
m_wallet->default_mixin(arg);
}
+bool WalletImpl::setCacheAttribute(const std::string &key, const std::string &val)
+{
+ m_wallet->set_attribute(key, val);
+ return true;
+}
+
+std::string WalletImpl::getCacheAttribute(const std::string &key) const
+{
+ std::string value;
+ m_wallet->get_attribute(key, value);
+ return value;
+}
+
bool WalletImpl::setUserNote(const std::string &txid, const std::string &note)
{
cryptonote::blobdata txid_data;
@@ -1729,18 +1748,27 @@ std::string WalletImpl::getTxKey(const std::string &txid_str) const
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
+ try
{
clearStatus();
- std::ostringstream oss;
- oss << epee::string_tools::pod_to_hex(tx_key);
- for (size_t i = 0; i < additional_tx_keys.size(); ++i)
- oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
- return oss.str();
+ if (m_wallet->get_tx_key(txid, tx_key, additional_tx_keys))
+ {
+ clearStatus();
+ std::ostringstream oss;
+ oss << epee::string_tools::pod_to_hex(tx_key);
+ for (size_t i = 0; i < additional_tx_keys.size(); ++i)
+ oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
+ return oss.str();
+ }
+ else
+ {
+ setStatusError(tr("no tx keys found for this txid"));
+ return "";
+ }
}
- else
+ catch (const std::exception &e)
{
- setStatusError(tr("no tx keys found for this txid"));
+ setStatusError(e.what());
return "";
}
}
@@ -2419,6 +2447,23 @@ uint64_t WalletImpl::coldKeyImageSync(uint64_t &spent, uint64_t &unspent)
{
return m_wallet->cold_key_image_sync(spent, unspent);
}
+
+void WalletImpl::deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId)
+{
+ boost::optional<crypto::hash8> payment_id_param = boost::none;
+ if (!paymentId.empty())
+ {
+ crypto::hash8 payment_id;
+ bool res = tools::wallet2::parse_short_payment_id(paymentId, payment_id);
+ if (!res)
+ {
+ throw runtime_error("Invalid payment ID");
+ }
+ payment_id_param = payment_id;
+ }
+
+ m_wallet->device_show_address(accountIndex, addressIndex, payment_id_param);
+}
} // namespace
namespace Bitmonero = Monero;
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 9e07b6e19..331bf4b38 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -149,6 +149,11 @@ public:
bool hasMultisigPartialKeyImages() const override;
PendingTransaction* restoreMultisigTransaction(const std::string& signData) override;
+ PendingTransaction * createTransactionMultDest(const std::vector<std::string> &dst_addr, const std::string &payment_id,
+ optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
+ PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
+ uint32_t subaddr_account = 0,
+ std::set<uint32_t> subaddr_indices = {}) override;
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
@@ -168,6 +173,10 @@ public:
virtual void setListener(WalletListener * l) override;
virtual uint32_t defaultMixin() const override;
virtual void setDefaultMixin(uint32_t arg) override;
+
+ virtual bool setCacheAttribute(const std::string &key, const std::string &val) override;
+ virtual std::string getCacheAttribute(const std::string &key) const override;
+
virtual bool setUserNote(const std::string &txid, const std::string &note) override;
virtual std::string getUserNote(const std::string &txid) const override;
virtual std::string getTxKey(const std::string &txid) const override;
@@ -201,6 +210,7 @@ public:
virtual bool unlockKeysFile() override;
virtual bool isKeysFileLocked() override;
virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) override;
+ virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) override;
private:
void clearStatus() const;
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 0af3b1867..e543a115b 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -812,6 +812,26 @@ struct Wallet
* @return PendingTransaction
*/
virtual PendingTransaction* restoreMultisigTransaction(const std::string& signData) = 0;
+
+ /*!
+ * \brief createTransactionMultDest creates transaction with multiple destinations. if dst_addr is an integrated address, payment_id is ignored
+ * \param dst_addr vector of destination address as string
+ * \param payment_id optional payment_id, can be empty string
+ * \param amount vector of amounts
+ * \param mixin_count mixin count. if 0 passed, wallet will use default value
+ * \param subaddr_account subaddress account from which the input funds are taken
+ * \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
+ * \param priority
+ * \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
+ * after object returned
+ */
+
+ virtual PendingTransaction * createTransactionMultDest(const std::vector<std::string> &dst_addr, const std::string &payment_id,
+ optional<std::vector<uint64_t>> amount, uint32_t mixin_count,
+ PendingTransaction::Priority = PendingTransaction::Priority_Low,
+ uint32_t subaddr_account = 0,
+ std::set<uint32_t> subaddr_indices = {}) = 0;
+
/*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
* \param dst_addr destination address as string
@@ -891,6 +911,19 @@ struct Wallet
virtual void setDefaultMixin(uint32_t arg) = 0;
/*!
+ * \brief setCacheAttribute - attach an arbitrary string to a wallet cache attribute
+ * \param key - the key
+ * \param val - the value
+ * \return true if successful, false otherwise
+ */
+ virtual bool setCacheAttribute(const std::string &key, const std::string &val) = 0;
+ /*!
+ * \brief getCacheAttribute - return an arbitrary string attached to a wallet cache attribute
+ * \param key - the key
+ * \return the attached string, or empty string if there is none
+ */
+ virtual std::string getCacheAttribute(const std::string &key) const = 0;
+ /*!
* \brief setUserNote - attach an arbitrary string note to a txid
* \param txid - the transaction id to attach the note to
* \param note - the note
@@ -1003,6 +1036,9 @@ struct Wallet
//! cold-device protocol key image sync
virtual uint64_t coldKeyImageSync(uint64_t &spent, uint64_t &unspent) = 0;
+
+ //! shows address on device display
+ virtual void deviceShowAddress(uint32_t accountIndex, uint32_t addressIndex, const std::string &paymentId) = 0;
};
/**
diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp
index ef2ed2015..d589dcc75 100644
--- a/src/wallet/api/wallet_manager.cpp
+++ b/src/wallet/api/wallet_manager.cpp
@@ -227,9 +227,6 @@ std::string WalletManagerImpl::errorString() const
void WalletManagerImpl::setDaemonAddress(const std::string &address)
{
- m_daemonAddress = address;
- if(m_http_client.is_connected())
- m_http_client.disconnect();
m_http_client.set_server(address, boost::none);
}
diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h
index 235f96e17..537fc5ba6 100644
--- a/src/wallet/api/wallet_manager.h
+++ b/src/wallet/api/wallet_manager.h
@@ -95,7 +95,6 @@ public:
private:
WalletManagerImpl() {}
friend struct WalletManagerFactory;
- std::string m_daemonAddress;
epee::net_utils::http::http_simple_client m_http_client;
std::string m_errorString;
};
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 8f3f30da1..9782e4b1e 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -217,6 +217,8 @@ namespace
add_reason(reason, "invalid input");
if (res.invalid_output)
add_reason(reason, "invalid output");
+ if (res.too_few_outputs)
+ add_reason(reason, "too few outputs");
if (res.too_big)
add_reason(reason, "too big");
if (res.overspend)
@@ -1140,7 +1142,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_devices_registered(false),
m_device_last_key_image_sync(0),
m_use_dns(true),
- m_offline(false)
+ m_offline(false),
+ m_rpc_version(0)
{
}
@@ -5157,6 +5160,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
if (m_offline)
{
+ m_rpc_version = 0;
if (version)
*version = 0;
if (ssl)
@@ -5166,6 +5170,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
// TODO: Add light wallet version check.
if(m_light_wallet) {
+ m_rpc_version = 0;
if (version)
*version = 0;
if (ssl)
@@ -5177,6 +5182,7 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
if(!m_http_client.is_connected(ssl))
{
+ m_rpc_version = 0;
m_node_rpc_proxy.invalidate();
if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
return false;
@@ -5185,20 +5191,21 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
}
}
- if (version)
+ if (!m_rpc_version)
{
cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_VERSION::response resp_t = AUTO_VAL_INIT(resp_t);
bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t);
if(!r) {
- *version = 0;
+ if(version)
+ *version = 0;
return false;
}
- if (resp_t.status != CORE_RPC_STATUS_OK)
- *version = 0;
- else
- *version = resp_t.version;
+ if (resp_t.status == CORE_RPC_STATUS_OK)
+ m_rpc_version = resp_t.version;
}
+ if (version)
+ *version = m_rpc_version;
return true;
}
@@ -6118,7 +6125,7 @@ void wallet2::commit_tx(pending_tx& ptx)
amount_in += m_transfers[idx].amount();
}
add_unconfirmed_tx(ptx.tx, amount_in, dests, payment_id, ptx.change_dts.amount, ptx.construction_data.subaddr_account, ptx.construction_data.subaddr_indices);
- if (store_tx_info())
+ if (store_tx_info() && ptx.tx_key != crypto::null_skey)
{
m_tx_keys.insert(std::make_pair(txid, ptx.tx_key));
m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
@@ -6307,7 +6314,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
// normally, the tx keys are saved in commit_tx, when the tx is actually sent to the daemon.
// we can't do that here since the tx will be sent from the compromised wallet, which we don't want
// to see that info, so we save it here
- if (store_tx_info())
+ if (store_tx_info() && ptx.tx_key != crypto::null_skey)
{
const crypto::hash txid = get_transaction_hash(ptx.tx);
m_tx_keys.insert(std::make_pair(txid, tx_key));
@@ -6931,7 +6938,7 @@ uint64_t wallet2::get_base_fee() const
else
return m_light_wallet_per_kb_fee;
}
- bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
+ bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -30 * 1);
if (!use_dyn_fee)
return FEE_PER_KB;
@@ -6962,7 +6969,7 @@ int wallet2::get_fee_algorithm() const
return 3;
if (use_fork_rules(5, 0))
return 2;
- if (use_fork_rules(3, -720 * 14))
+ if (use_fork_rules(3, -30 * 14))
return 1;
return 0;
}
@@ -7443,7 +7450,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
order.resize(light_wallet_requested_outputs_count);
for (size_t n = 0; n < order.size(); ++n)
order[n] = n;
- std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
+ std::shuffle(order.begin(), order.end(), crypto::random_device{});
LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs with amounts " << print_money(td.is_rct() ? 0 : td.amount()));
@@ -7726,7 +7733,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
uint64_t num_found = 0;
// if we have a known ring, use it
- bool existing_ring_found = false;
if (td.m_key_image_known && !td.m_key_image_partial)
{
std::vector<uint64_t> ring;
@@ -7738,7 +7744,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
std::to_string(ring.size()) + ", it cannot be spent now with ring size " +
std::to_string(fake_outputs_count + 1) + " as it is smaller: use a higher ring size");
bool own_found = false;
- existing_ring_found = true;
for (const auto &out: ring)
{
MINFO("Ring has output " << out);
@@ -7984,7 +7989,6 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
outs.back().push_back(std::make_tuple(td.m_global_output_index, boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key, mask));
// then pick outs from an existing ring, if any
- bool existing_ring_found = false;
if (td.m_key_image_known && !td.m_key_image_partial)
{
std::vector<uint64_t> ring;
@@ -8021,7 +8025,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
order.resize(requested_outputs_count);
for (size_t n = 0; n < order.size(); ++n)
order[n] = n;
- std::shuffle(order.begin(), order.end(), std::default_random_engine(crypto::rand<unsigned>()));
+ std::shuffle(order.begin(), order.end(), crypto::random_device{});
LOG_PRINT_L2("Looking for " << (fake_outputs_count+1) << " outputs of size " << print_money(td.is_rct() ? 0 : td.amount()));
for (size_t o = 0; o < requested_outputs_count && outs.back().size() < fake_outputs_count + 1; ++o)
@@ -8163,6 +8167,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
if (needed_money < found_money)
{
change_dts.addr = get_subaddress({subaddr_account, 0});
+ change_dts.is_subaddress = subaddr_account != 0;
change_dts.amount = found_money - needed_money;
}
@@ -10051,6 +10056,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_
setup_shim(&wallet_shim, this);
aux_data.tx_recipients = dsts_info;
aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1;
+ aux_data.hard_fork = get_current_hard_fork();
dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data);
tx_device_aux = aux_data.tx_device_aux;
@@ -10078,6 +10084,35 @@ uint64_t wallet2::cold_key_image_sync(uint64_t &spent, uint64_t &unspent) {
return import_res;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::device_show_address(uint32_t account_index, uint32_t address_index, const boost::optional<crypto::hash8> &payment_id)
+{
+ if (!key_on_device())
+ {
+ return;
+ }
+
+ auto & hwdev = get_account().get_device();
+ hwdev.display_address(subaddress_index{account_index, address_index}, payment_id);
+}
+//----------------------------------------------------------------------------------------------------
+uint8_t wallet2::get_current_hard_fork()
+{
+ if (m_offline)
+ return 0;
+
+ cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req_t = AUTO_VAL_INIT(req_t);
+ cryptonote::COMMAND_RPC_HARD_FORK_INFO::response resp_t = AUTO_VAL_INIT(resp_t);
+
+ m_daemon_rpc_mutex.lock();
+ req_t.version = 0;
+ bool r = net_utils::invoke_http_json_rpc("/json_rpc", "hard_fork_info", req_t, resp_t, m_http_client, rpc_timeout);
+ m_daemon_rpc_mutex.unlock();
+ THROW_WALLET_EXCEPTION_IF(!r, tools::error::no_connection_to_daemon, "hard_fork_info");
+ THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, "hard_fork_info");
+ THROW_WALLET_EXCEPTION_IF(resp_t.status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, "hard_fork_info", m_trusted_daemon ? resp_t.status : "daemon error");
+ return resp_t.version;
+}
+//----------------------------------------------------------------------------------------------------
void wallet2::get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const
{
boost::optional<std::string> result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
@@ -10276,6 +10311,8 @@ bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx
if (i == m_tx_keys.end())
return false;
tx_key = i->second;
+ if (tx_key == crypto::null_skey)
+ return false;
const auto j = m_additional_tx_keys.find(txid);
if (j != m_additional_tx_keys.end())
additional_tx_keys = j->second;
@@ -10287,6 +10324,7 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys);
if (r)
{
+ MDEBUG("tx key cached for txid: " << txid);
return true;
}
@@ -10348,13 +10386,18 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key);
if (tx_keys.empty())
{
+ MDEBUG("Empty tx keys for txid: " << txid);
+ return false;
+ }
+
+ if (tx_keys[0] == crypto::null_skey)
+ {
return false;
}
tx_key = tx_keys[0];
tx_keys.erase(tx_keys.begin());
additional_tx_keys = tx_keys;
-
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -11387,12 +11430,13 @@ void wallet2::set_attribute(const std::string &key, const std::string &value)
m_attributes[key] = value;
}
-std::string wallet2::get_attribute(const std::string &key) const
+bool wallet2::get_attribute(const std::string &key, std::string &value) const
{
std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(key);
if (i == m_attributes.end())
- return std::string();
- return i->second;
+ return false;
+ value = i->second;
+ return true;
}
void wallet2::set_description(const std::string &description)
@@ -11402,7 +11446,10 @@ void wallet2::set_description(const std::string &description)
std::string wallet2::get_description() const
{
- return get_attribute(ATTRIBUTE_DESCRIPTION);
+ std::string s;
+ if (get_attribute(ATTRIBUTE_DESCRIPTION, s))
+ return s;
+ return "";
}
const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& wallet2::get_account_tags()
@@ -11560,10 +11607,10 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle
return tx_pub_key;
}
-bool wallet2::export_key_images(const std::string &filename) const
+bool wallet2::export_key_images(const std::string &filename, bool all) const
{
PERF_TIMER(export_key_images);
- std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images();
+ std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = export_key_images(all);
std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC));
const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address;
const uint32_t offset = ski.first;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 921c150cb..c2e34dd76 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -843,6 +843,7 @@ private:
void cold_tx_aux_import(const std::vector<pending_tx>& ptx, const std::vector<std::string>& tx_device_aux);
void cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_set &exported_txs, std::vector<cryptonote::address_parse_info> &dsts_info, std::vector<std::string> & tx_device_aux);
uint64_t cold_key_image_sync(uint64_t &spent, uint64_t &unspent);
+ void device_show_address(uint32_t account_index, uint32_t address_index, const boost::optional<crypto::hash8> &payment_id);
bool parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const;
bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL);
@@ -1094,6 +1095,7 @@ private:
size_t get_num_transfer_details() const { return m_transfers.size(); }
const transfer_details &get_transfer_details(size_t idx) const;
+ uint8_t get_current_hard_fork();
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height) const;
bool use_fork_rules(uint8_t version, int64_t early_blocks = 0) const;
int get_fee_algorithm() const;
@@ -1173,7 +1175,7 @@ private:
void import_payments_out(const std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>> &confirmed_payments);
std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> export_blockchain() const;
void import_blockchain(const std::tuple<size_t, crypto::hash, std::vector<crypto::hash>> &bc);
- bool export_key_images(const std::string &filename) const;
+ bool export_key_images(const std::string &filename, bool all = false) const;
std::pair<size_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> export_key_images(bool all = false) const;
uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true);
uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent);
@@ -1247,7 +1249,7 @@ private:
*/
const char* const ATTRIBUTE_DESCRIPTION = "wallet2.description";
void set_attribute(const std::string &key, const std::string &value);
- std::string get_attribute(const std::string &key) const;
+ bool get_attribute(const std::string &key, std::string &value) const;
crypto::public_key get_multisig_signer_public_key(const crypto::secret_key &spend_skey) const;
crypto::public_key get_multisig_signer_public_key() const;
@@ -1504,6 +1506,7 @@ private:
uint64_t m_device_last_key_image_sync;
bool m_use_dns;
bool m_offline;
+ uint32_t m_rpc_version;
// Aux transaction data from device
std::unordered_map<crypto::hash, std::string> m_tx_device;
diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp
index a4bb342ca..9da9d109c 100644
--- a/src/wallet/wallet_args.cpp
+++ b/src/wallet/wallet_args.cpp
@@ -206,7 +206,10 @@ namespace wallet_args
if (!command_line::is_arg_defaulted(vm, arg_log_level))
MINFO("Setting log level = " << command_line::get_arg(vm, arg_log_level));
else
- MINFO("Setting log levels = " << getenv("MONERO_LOGS"));
+ {
+ const char *logs = getenv("MONERO_LOGS");
+ MINFO("Setting log levels = " << (logs ? logs : "<default>"));
+ }
MINFO(wallet_args::tr("Logging to: ") << log_path);
Print(print) << boost::format(wallet_args::tr("Logging to %s")) % log_path;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 0e2a34470..c64b662f3 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -247,7 +247,9 @@ namespace tools
m_net_server.set_threads_prefix("RPC");
auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); };
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
- rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login),
+ rng, std::move(bind_port), std::move(rpc_config->bind_ip),
+ std::move(rpc_config->bind_ipv6_address), std::move(rpc_config->use_ipv6), std::move(rpc_config->require_ipv4),
+ std::move(rpc_config->access_control_origins), std::move(http_login),
std::move(rpc_config->ssl_options)
);
}
@@ -350,7 +352,7 @@ namespace tools
entry.destinations.push_back(wallet_rpc::transfer_destination());
wallet_rpc::transfer_destination &td = entry.destinations.back();
td.amount = d.amount;
- td.address = d.original.empty() ? get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) : d.original;
+ td.address = d.address(m_wallet->nettype(), pd.m_payment_id);
}
entry.type = "out";
@@ -380,7 +382,7 @@ namespace tools
entry.destinations.push_back(wallet_rpc::transfer_destination());
wallet_rpc::transfer_destination &td = entry.destinations.back();
td.amount = d.amount;
- td.address = d.original.empty() ? get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) : d.original;
+ td.address = d.address(m_wallet->nettype(), pd.m_payment_id);
}
entry.type = is_failed ? "failed" : "pending";
@@ -1811,14 +1813,12 @@ namespace tools
wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
- bool transfers_found = false;
for (const auto& td : transfers)
{
if (!filter || available != td.m_spent)
{
if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0))
continue;
- transfers_found = true;
wallet_rpc::transfer_details rpc_transfers;
rpc_transfers.amount = td.amount();
rpc_transfers.spent = td.m_spent;
@@ -2104,7 +2104,12 @@ namespace tools
return false;
}
- res.value = m_wallet->get_attribute(req.key);
+ if (!m_wallet->get_attribute(req.key, res.value))
+ {
+ er.code = WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND;
+ er.message = "Attribute not found.";
+ return false;
+ }
return true;
}
bool wallet_rpc_server::on_get_tx_key(const wallet_rpc::COMMAND_RPC_GET_TX_KEY::request& req, wallet_rpc::COMMAND_RPC_GET_TX_KEY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
@@ -4086,9 +4091,8 @@ namespace tools
}
}
- er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
- er.message = std::string("Invalid address");
- return false;
+ res.valid = false;
+ return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx)
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index 011d146d4..9434fbc3e 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -75,3 +75,4 @@
#define WALLET_RPC_ERROR_CODE_SIGN_UNSIGNED -42
#define WALLET_RPC_ERROR_CODE_NON_DETERMINISTIC -43
#define WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL -44
+#define WALLET_RPC_ERROR_CODE_ATTRIBUTE_NOT_FOUND -45