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.cpp208
-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/blocks/checkpoints.datbin232004 -> 234980 bytes
-rw-r--r--src/common/dns_utils.cpp1
-rw-r--r--src/common/util.cpp17
-rw-r--r--src/common/util.h2
-rw-r--r--src/crypto/keccak.c9
-rw-r--r--src/crypto/slow-hash.c2
-rw-r--r--src/cryptonote_basic/verification_context.h1
-rw-r--r--src/cryptonote_config.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp233
-rw-r--r--src/cryptonote_core/blockchain.h35
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp17
-rw-r--r--src/cryptonote_core/cryptonote_core.h1
-rw-r--r--src/cryptonote_core/tx_sanity_check.cpp4
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h10
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl24
-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.cpp204
-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.h58
-rw-r--r--src/p2p/net_node.inl438
-rw-r--r--src/p2p/net_node_common.h11
-rw-r--r--src/p2p/net_peerlist.h20
-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/core_rpc_server.cpp347
-rw-r--r--src/rpc/core_rpc_server.h15
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h172
-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/serialization/json_object.cpp2
-rw-r--r--src/simplewallet/simplewallet.cpp131
-rw-r--r--src/simplewallet/simplewallet.h1
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/wallet.cpp30
-rw-r--r--src/wallet/api/wallet.h5
-rw-r--r--src/wallet/api/wallet2_api.h16
-rw-r--r--src/wallet/api/wallet_manager.cpp3
-rw-r--r--src/wallet/api/wallet_manager.h1
-rw-r--r--src/wallet/wallet2.cpp70
-rw-r--r--src/wallet/wallet2.h5
-rw-r--r--src/wallet/wallet_rpc_server.cpp23
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h1
68 files changed, 2212 insertions, 559 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 f05eb0f30..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))
@@ -1953,7 +1960,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
TIME_MEASURE_START(t);
- size_t n_total_records = 0, n_prunable_records = 0, n_pruned_records = 0;
+ size_t n_total_records = 0, n_prunable_records = 0, n_pruned_records = 0, commit_counter = 0;
uint64_t n_bytes = 0;
mdb_txn_safe txn;
@@ -2056,6 +2063,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
{
MDEBUG("Pruning at height " << block_height << "/" << blockchain_height);
++n_pruned_records;
+ ++commit_counter;
n_bytes += k.mv_size + v.mv_size;
result = mdb_cursor_del(c_txs_prunable, 0);
if (result)
@@ -2065,6 +2073,25 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
result = mdb_cursor_del(c_txs_prunable_tip, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to delete transaction tip data: ", result).c_str()));
+
+ if (mode != prune_mode_check && commit_counter >= 4096)
+ {
+ MDEBUG("Committing txn at checkpoint...");
+ txn.commit();
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_pruned, &c_txs_pruned);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_pruned: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_prunable, &c_txs_prunable);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable_tip: ", result).c_str()));
+ commit_counter = 0;
+ }
}
}
}
@@ -2134,6 +2161,7 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
result = mdb_cursor_del(c_txs_prunable, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to delete transaction prunable data: ", result).c_str()));
+ ++commit_counter;
}
}
}
@@ -2150,6 +2178,34 @@ bool BlockchainLMDB::prune_worker(int mode, uint32_t pruning_seed)
", seed " << epee::string_tools::to_string_hex(pruning_seed));
}
}
+
+ if (mode != prune_mode_check && commit_counter >= 4096)
+ {
+ MDEBUG("Committing txn at checkpoint...");
+ txn.commit();
+ result = mdb_txn_begin(m_env, NULL, 0, txn);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_pruned, &c_txs_pruned);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_pruned: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_prunable, &c_txs_prunable);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_txs_prunable_tip, &c_txs_prunable_tip);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for txs_prunable_tip: ", result).c_str()));
+ result = mdb_cursor_open(txn, m_tx_indices, &c_tx_indices);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to open a cursor for tx_indices: ", result).c_str()));
+ MDB_val val;
+ val.mv_size = sizeof(ti);
+ val.mv_data = (void *)&ti;
+ result = mdb_cursor_get(c_tx_indices, (MDB_val*)&zerokval, &val, MDB_GET_BOTH);
+ if (result)
+ throw0(DB_ERROR(lmdb_error("Failed to restore cursor for tx_indices: ", result).c_str()));
+ commit_counter = 0;
+ }
}
mdb_cursor_close(c_tx_indices);
}
@@ -2241,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__);
@@ -4062,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/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index b975af6b4..a7d309753 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp
index 5e03bf897..dc1f335a7 100644
--- a/src/common/dns_utils.cpp
+++ b/src/common/dns_utils.cpp
@@ -48,7 +48,6 @@ static const char *DEFAULT_DNS_PUBLIC_ADDR[] =
"80.67.169.40", // FDN (France)
"89.233.43.71", // http://censurfridns.dk (Denmark)
"109.69.8.51", // punCAT (Spain)
- "77.109.148.137", // Xiala.net (Switzerland)
"193.58.251.251", // SkyDNS (Russia)
};
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/keccak.c b/src/crypto/keccak.c
index 18ed3152f..851c70a25 100644
--- a/src/crypto/keccak.c
+++ b/src/crypto/keccak.c
@@ -105,9 +105,12 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
memset(st, 0, sizeof(st));
for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) {
- for (i = 0; i < rsizw; i++)
- st[i] ^= swap64le(((uint64_t *) in)[i]);
- keccakf(st, KECCAK_ROUNDS);
+ for (i = 0; i < rsizw; i++) {
+ uint64_t ina;
+ memcpy(&ina, in + i * 8, 8);
+ st[i] ^= swap64le(ina);
+ }
+ keccakf(st, KECCAK_ROUNDS);
}
// last block and padding
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/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 23534f0d3..0ea81f19a 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -731,9 +731,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 +858,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 +1024,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 +1035,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 +1044,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 +1055,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 +1075,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 +1111,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 +1129,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 +1147,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 +1165,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 +1180,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 +1402,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 +1427,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 +1442,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 +1628,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 +1654,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 +1722,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 +1736,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 +1783,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 +1795,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 +1821,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 +1841,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() << ")");
}
@@ -1893,10 +1920,14 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
if (missed_tx_ids.size() != 0)
{
- LOG_ERROR("Error retrieving blocks, missed " << missed_tx_ids.size()
- << " transactions for block with hash: " << get_block_hash(bl.second)
- << std::endl
- );
+ // do not display an error if the peer asked for an unpruned block which we are not meant to have
+ if (tools::has_unpruned_block(get_block_height(bl.second), get_current_blockchain_height(), get_blockchain_pruning_seed()))
+ {
+ LOG_ERROR("Error retrieving blocks, missed " << missed_tx_ids.size()
+ << " transactions for block with hash: " << get_block_hash(bl.second)
+ << std::endl
+ );
+ }
// append missed transaction hashes to response missed_ids field,
// as done below if any standalone transactions were requested
@@ -1908,8 +1939,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;
}
@@ -1919,11 +1948,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;
}
//------------------------------------------------------------------
@@ -1931,7 +1969,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
@@ -2035,7 +2073,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;
@@ -2049,7 +2086,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);
@@ -2419,9 +2456,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;
}
@@ -2803,6 +2840,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)
@@ -3250,7 +3300,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;
@@ -3958,14 +4007,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
{
@@ -3987,7 +4034,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)
{
@@ -4806,15 +4853,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)
{
@@ -4828,7 +4899,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;
@@ -4845,7 +4916,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "cfca50ea0c87718ac92a14654c60d7ee8f6453e2765b329b40d10da4ed85a4f2";
+static const char expected_block_hashes_hash[] = "7dafb40b414a0e59bfced6682ef519f0b416bc914dd3d622b72e0dd1a47117c2";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h
index 32ed96b5b..d95f2dceb 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
};
@@ -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,7 +1014,6 @@ 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;
@@ -1074,9 +1054,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
@@ -1186,7 +1163,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 +1226,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 +1236,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
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 55e1b287f..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,10 +679,13 @@ 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
- if (m_blockchain_storage.get_current_blockchain_height() > 1 && !m_blockchain_storage.get_blockchain_pruning_seed())
+ if (!m_blockchain_storage.get_blockchain_pruning_seed())
{
MGINFO("Pruning blockchain...");
CHECK_AND_ASSERT_MES(m_blockchain_storage.prune_blockchain(), false, "Failed to prune blockchain");
@@ -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/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp
index 10198a3d3..e95350f76 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;
}
@@ -88,7 +88,7 @@ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob
std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
uint64_t median = epee::misc_utils::median(offsets);
- if (median < n_available * 9 / 10)
+ if (median < n_available * 6 / 10)
{
MERROR("median is " << median << "/" << n_available);
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 b38407840..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);
@@ -341,6 +342,11 @@ namespace cryptonote
if(m_core.have_block(hshd.top_id))
{
+ if (target > hshd.current_height)
+ {
+ MINFO(context << "peer is not ahead of us and we're syncing, disconnecting");
+ return false;
+ }
context.m_state = cryptonote_connection_context::state_normal;
if(is_inital && target == m_core.get_current_blockchain_height())
on_connection_synchronized();
@@ -928,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;
@@ -947,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);
@@ -973,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;
@@ -981,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();
@@ -1942,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..309e5c8dd 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -46,6 +46,19 @@
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;
@@ -54,8 +67,8 @@ namespace {
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::string elapsed = peer.last_seen == 0 ? "never" : epee::misc_utils::get_time_interval_string(now - last_seen);
+ std::string ip_str = peer.ip != 0 ? epee::string_tools::get_ip_string_from_int32(peer.ip) : std::string("[") + peer.host + "]";
std::stringstream peer_id_str;
peer_id_str << std::hex << std::setw(16) << peer.id;
peer_id_str >> id_str;
@@ -338,6 +351,7 @@ bool t_rpc_command_executor::show_difficulty() {
tools::success_msg_writer() << "BH: " << res.height
<< ", TH: " << res.top_block_hash
<< ", DIFF: " << res.difficulty
+ << ", CUM_DIFF: " << res.cumulative_difficulty
<< ", HR: " << res.difficulty / res.target << " H/s";
return true;
@@ -589,6 +603,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 +624,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
@@ -1466,13 +1482,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 +1510,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,61 +1544,9 @@ bool t_rpc_command_executor::in_peers(uint64_t limit)
}
}
- tools::msg_writer() << "Max number of in peers set to " << limit << 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;
-}
+ 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;
-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;
}
@@ -1641,14 +1608,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 +1623,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 +1648,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 +1656,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 +1681,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 +1818,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 +1855,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;
}
}
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())
{
@@ -1882,6 +1891,49 @@ bool t_rpc_command_executor::alt_chain_info(const std::string &tip)
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";
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..cf6b2c67b 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()
{}
@@ -245,10 +257,16 @@ namespace nodetool
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 +337,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 +363,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 +420,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 +431,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 +481,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 +500,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 +517,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 +526,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 ba29d92c9..426767365 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();
@@ -944,7 +1089,10 @@ namespace nodetool
}
if(!context.m_is_income)
m_network_zones.at(context.m_remote_address.get_zone()).m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_address, context.m_pruning_seed, context.m_rpc_port);
- m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false);
+ if (!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false))
+ {
+ m_network_zones.at(context.m_remote_address.get_zone()).m_net_server.get_config_object().close(context.m_connection_id );
+ }
});
if(!r)
@@ -1090,6 +1238,7 @@ namespace nodetool
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer "
<< na.str()
/*<< ", try " << try_count*/);
+ zone.m_net_server.get_config_object().close(con->m_connection_id);
return false;
}
@@ -1149,7 +1298,7 @@ namespace nodetool
bool is_priority = is_priority_node(na);
LOG_PRINT_CC_PRIORITY_NODE(is_priority, *con, "Failed to HANDSHAKE with peer " << na.str());
-
+ zone.m_net_server.get_config_object().close(con->m_connection_id);
return false;
}
@@ -1226,19 +1375,53 @@ namespace nodetool
size_t random_index;
const uint32_t next_needed_pruning_stripe = m_payload_handler.get_next_needed_pruning_stripe().second;
+ // build a set of all the /16 we're connected to, and prefer a peer that's not in that set
+ std::set<uint32_t> classB;
+ if (&zone == &m_network_zones.at(epee::net_utils::zone::public_)) // at returns reference, not copy
+ {
+ zone.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())
+ {
+
+ const epee::net_utils::network_address na = cntxt.m_remote_address;
+ const uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ classB.insert(actual_ip & 0x0000ffff);
+ }
+ return true;
+ });
+ }
+
std::deque<size_t> filtered;
const size_t limit = use_white_list ? 20 : std::numeric_limits<size_t>::max();
- size_t idx = 0;
- zone.m_peerlist.foreach (use_white_list, [&filtered, &idx, limit, next_needed_pruning_stripe](const peerlist_entry &pe){
- if (filtered.size() >= limit)
- return false;
- if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0)
- filtered.push_back(idx);
- else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed))
- filtered.push_front(idx);
- ++idx;
- return true;
- });
+ size_t idx = 0, skipped = 0;
+ for (int step = 0; step < 2; ++step)
+ {
+ bool skip_duplicate_class_B = step == 0;
+ zone.m_peerlist.foreach (use_white_list, [&classB, &filtered, &idx, &skipped, skip_duplicate_class_B, limit, next_needed_pruning_stripe](const peerlist_entry &pe){
+ if (filtered.size() >= limit)
+ return false;
+ bool skip = false;
+ if (skip_duplicate_class_B && pe.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
+ {
+ const epee::net_utils::network_address na = pe.adr;
+ uint32_t actual_ip = na.as<const epee::net_utils::ipv4_network_address>().ip();
+ skip = classB.find(actual_ip & 0x0000ffff) != classB.end();
+ }
+ if (skip)
+ ++skipped;
+ else if (next_needed_pruning_stripe == 0 || pe.pruning_seed == 0)
+ filtered.push_back(idx);
+ else if (next_needed_pruning_stripe == tools::get_pruning_stripe(pe.pruning_seed))
+ filtered.push_front(idx);
+ ++idx;
+ return true;
+ });
+ if (skipped == 0 || !filtered.empty())
+ break;
+ if (skipped)
+ MGINFO("Skipping " << skipped << " possible peers as they share a class B with existing peers");
+ }
if (filtered.empty())
{
MDEBUG("No available peer in " << (use_white_list ? "white" : "gray") << " list filtered by " << next_needed_pruning_stripe);
@@ -1581,8 +1764,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;
@@ -1839,19 +2031,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,
@@ -2035,12 +2251,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);
@@ -2217,6 +2440,15 @@ namespace nodetool
}
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_);
@@ -2230,6 +2462,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){
@@ -2389,16 +2630,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;
@@ -2435,16 +2679,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)
{
- MDEBUG("Attempting to delete IGD port mapping.");
+ 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)
+ {
+ 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;
@@ -2477,6 +2743,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)
@@ -2495,13 +2780,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 f4fa921e2..883997fd6 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -344,8 +344,14 @@ namespace nodetool
trim_white_peerlist();
}else
{
- //update record in white list
- m_peers_white.replace(by_addr_it_wt, ple);
+ //update record in white list
+ peerlist_entry new_ple = ple;
+ if (by_addr_it_wt->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
+ new_ple.pruning_seed = by_addr_it_wt->pruning_seed;
+ if (by_addr_it_wt->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
+ new_ple.rpc_port = by_addr_it_wt->rpc_port;
+ new_ple.last_seen = by_addr_it_wt->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
+ m_peers_white.replace(by_addr_it_wt, new_ple);
}
//remove from gray list, if need
auto by_addr_it_gr = m_peers_gray.get<by_addr>().find(ple.adr);
@@ -379,8 +385,14 @@ namespace nodetool
trim_gray_peerlist();
}else
{
- //update record in white list
- m_peers_gray.replace(by_addr_it_gr, ple);
+ //update record in gray list
+ peerlist_entry new_ple = ple;
+ if (by_addr_it_gr->pruning_seed && ple.pruning_seed == 0) // guard against older nodes not passing pruning info around
+ new_ple.pruning_seed = by_addr_it_gr->pruning_seed;
+ if (by_addr_it_gr->rpc_port && ple.rpc_port == 0) // guard against older nodes not passing RPC port around
+ new_ple.rpc_port = by_addr_it_gr->rpc_port;
+ new_ple.last_seen = by_addr_it_gr->last_seen; // do not overwrite the last seen timestamp, incoming peer list are untrusted
+ m_peers_gray.replace(by_addr_it_gr, new_ple);
}
return true;
CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false);
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/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 3db138719..9aaaa026d 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())
+ if (!set_bootstrap_daemon(command_line::get_arg(vm, arg_bootstrap_daemon_address),
+ command_line::get_arg(vm, arg_bootstrap_daemon_login)))
{
- 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
- {
- 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);
@@ -820,6 +862,7 @@ namespace cryptonote
res.sanity_check_failed = true;
return true;
}
+ res.sanity_check_failed = false;
cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context);
tx_verification_context tvc = AUTO_VAL_INIT(tvc);
@@ -843,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)
{
@@ -1007,6 +1052,11 @@ namespace cryptonote
if (entry.adr.get_type_id() == epee::net_utils::ipv4_network_address::get_type_id())
res.white_list.emplace_back(entry.id, entry.adr.as<epee::net_utils::ipv4_network_address>().ip(),
entry.adr.as<epee::net_utils::ipv4_network_address>().port(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
+ 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);
}
@@ -1017,6 +1067,9 @@ namespace cryptonote
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.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.gray_list.emplace_back(entry.id, entry.adr.str(), entry.last_seen, entry.pruning_seed, entry.rpc_port);
}
@@ -1025,6 +1078,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);
@@ -1124,6 +1216,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);
@@ -1448,10 +1562,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");
@@ -1461,7 +1577,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;
@@ -1475,7 +1594,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;
@@ -1505,7 +1624,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;
@@ -1549,38 +1673,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;
}
@@ -1771,20 +1912,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;
@@ -1797,13 +1978,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);
@@ -1954,7 +2151,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;
@@ -2038,7 +2235,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;
}
@@ -2046,27 +2245,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..7ae0c77b2 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 8
#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()
@@ -1091,10 +1096,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 +1111,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 +1206,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()
@@ -1236,6 +1248,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 +1643,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 +1769,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 +1780,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 +1795,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 +1806,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 +1931,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 +2152,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/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 4fcd5396b..10901bb51 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>");
@@ -4237,7 +4239,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 +4336,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 +4384,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 +4427,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 +5618,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 +5956,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 +6145,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 +6466,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 +6818,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 +6835,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_)
@@ -8501,6 +8563,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 +8600,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 +8649,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 +8681,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 +8713,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 +8724,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
{
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 33b18612c..4bf7fa334 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -261,6 +261,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/version.cpp.in b/src/version.cpp.in
index 8aaa41b19..28ce38df7 100644
--- a/src/version.cpp.in
+++ b/src/version.cpp.in
@@ -1,5 +1,5 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
-#define DEF_MONERO_VERSION "0.14.1.0"
+#define DEF_MONERO_VERSION "0.14.1.2"
#define DEF_MONERO_RELEASE_NAME "Boron Butterfly"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index 1711db482..140261f0b 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -1697,6 +1697,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;
@@ -2419,6 +2432,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..a367a1917 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -168,6 +168,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 +205,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..9e556cb2f 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -891,6 +891,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 +1016,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..e44fea7d7 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;
}
@@ -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;
}
@@ -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;
@@ -10051,6 +10055,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 +10083,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);
@@ -11387,12 +11421,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 +11437,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()
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 921c150cb..a6d042297 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;
@@ -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_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 8b8d832dc..844ecf90c 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)
);
}
@@ -1745,6 +1747,11 @@ namespace tools
else if (payment_id_str.size() == 2 * sizeof(payment_id8))
{
r = epee::string_tools::hex_to_pod(payment_id_str, payment_id8);
+ if (r)
+ {
+ memcpy(payment_id.data, payment_id8.data, 8);
+ memset(payment_id.data + 8, 0, 24);
+ }
}
else
{
@@ -1806,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;
@@ -2099,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)
@@ -4081,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