aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.cpp30
-rw-r--r--src/blockchain_db/berkeleydb/db_bdb.h1
-rw-r--r--src/blockchain_db/blockchain_db.cpp4
-rw-r--r--src/blockchain_db/blockchain_db.h70
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.cpp135
-rw-r--r--src/blockchain_db/lmdb/db_lmdb.h11
-rw-r--r--src/blockchain_db/testdb.h200
-rw-r--r--src/blockchain_utilities/README.md2
-rw-r--r--src/blockchain_utilities/blockchain_import.cpp6
-rw-r--r--src/blocks/checkpoints.datbin221956 -> 234980 bytes
-rw-r--r--src/checkpoints/checkpoints.cpp1
-rw-r--r--src/common/dns_utils.cpp1
-rw-r--r--src/common/password.cpp6
-rw-r--r--src/common/rpc_client.h4
-rw-r--r--src/common/util.cpp3
-rw-r--r--src/crypto/keccak.c12
-rw-r--r--src/crypto/tree-hash.c12
-rw-r--r--src/cryptonote_basic/cryptonote_basic.h2
-rw-r--r--src/cryptonote_basic/cryptonote_boost_serialization.h4
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp6
-rw-r--r--src/cryptonote_basic/hardfork.cpp35
-rw-r--r--src/cryptonote_basic/miner.cpp15
-rw-r--r--src/cryptonote_basic/miner.h2
-rw-r--r--src/cryptonote_core/CMakeLists.txt2
-rw-r--r--src/cryptonote_core/blockchain.cpp179
-rw-r--r--src/cryptonote_core/blockchain.h19
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp15
-rw-r--r--src/cryptonote_core/tx_pool.cpp18
-rw-r--r--src/cryptonote_core/tx_sanity_check.cpp100
-rw-r--r--src/cryptonote_core/tx_sanity_check.h36
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.h1
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl37
-rw-r--r--src/daemon/command_parser_executor.cpp10
-rw-r--r--src/daemon/command_parser_executor.h4
-rw-r--r--src/daemon/command_server.cpp11
-rw-r--r--src/daemon/command_server.h2
-rw-r--r--src/daemon/daemon.cpp3
-rw-r--r--src/daemon/main.cpp9
-rw-r--r--src/daemon/rpc_command_executor.cpp62
-rw-r--r--src/daemon/rpc_command_executor.h8
-rw-r--r--src/daemonizer/windows_daemonizer.inl10
-rw-r--r--src/device/device_ledger.cpp14
-rw-r--r--src/device/device_ledger.hpp1
-rw-r--r--src/device_trezor/device_trezor_base.cpp9
-rw-r--r--src/device_trezor/trezor/transport.cpp120
-rw-r--r--src/device_trezor/trezor/transport.hpp5
-rw-r--r--src/lmdb/error.cpp2
-rw-r--r--src/mnemonics/language_base.h2
-rw-r--r--src/net/error.h3
-rw-r--r--src/net/parse.cpp23
-rw-r--r--src/net/parse.h13
-rw-r--r--src/p2p/net_node.h13
-rw-r--r--src/p2p/net_node.inl185
-rw-r--r--src/p2p/net_node_common.h11
-rw-r--r--src/p2p/net_peerlist.h53
-rw-r--r--src/p2p/net_peerlist_boost_serialization.h10
-rw-r--r--src/p2p/p2p_protocol_defs.h5
-rw-r--r--src/ringct/bulletproofs.cc62
-rw-r--r--src/ringct/rctSigs.cpp1
-rw-r--r--src/ringct/rctTypes.h10
-rw-r--r--src/rpc/core_rpc_server.cpp214
-rw-r--r--src/rpc/core_rpc_server.h5
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h49
-rw-r--r--src/rpc/daemon_handler.cpp15
-rw-r--r--src/rpc/rpc_args.cpp82
-rw-r--r--src/rpc/rpc_args.h18
-rw-r--r--src/rpc/rpc_handler.cpp33
-rw-r--r--src/rpc/rpc_handler.h3
-rw-r--r--src/serialization/binary_archive.h3
-rw-r--r--src/serialization/difficulty_type.h4
-rw-r--r--src/serialization/serialization.h2
-rw-r--r--src/simplewallet/simplewallet.cpp43
-rw-r--r--src/simplewallet/simplewallet.h2
-rw-r--r--src/version.cpp.in2
-rw-r--r--src/wallet/api/wallet.cpp2
-rw-r--r--src/wallet/node_rpc_proxy.cpp13
-rw-r--r--src/wallet/node_rpc_proxy.h6
-rw-r--r--src/wallet/wallet2.cpp314
-rw-r--r--src/wallet/wallet2.h44
-rw-r--r--src/wallet/wallet_rpc_server.cpp218
-rw-r--r--src/wallet/wallet_rpc_server.h4
-rw-r--r--src/wallet/wallet_rpc_server_commands_defs.h63
-rw-r--r--src/wallet/wallet_rpc_server_error_codes.h1
83 files changed, 1912 insertions, 858 deletions
diff --git a/src/blockchain_db/berkeleydb/db_bdb.cpp b/src/blockchain_db/berkeleydb/db_bdb.cpp
index 3eb24494f..d138a1e7e 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.cpp
+++ b/src/blockchain_db/berkeleydb/db_bdb.cpp
@@ -1654,36 +1654,6 @@ tx_out_index BlockchainBDB::get_output_tx_and_index(const uint64_t& amount, cons
return indices[0];
}
-std::vector<uint64_t> BlockchainBDB::get_tx_output_indices(const crypto::hash& h) const
-{
- LOG_PRINT_L3("BlockchainBDB::" << __func__);
- check_open();
- std::vector<uint64_t> index_vec;
-
- bdb_cur cur(DB_DEFAULT_TX, m_tx_outputs);
-
- Dbt_copy<crypto::hash> k(h);
- Dbt_copy<uint32_t> v;
- auto result = cur->get(&k, &v, DB_SET);
- if (result == DB_NOTFOUND)
- throw1(OUTPUT_DNE("Attempting to get an output by tx hash and tx index, but output not found"));
- else if (result)
- throw0(DB_ERROR("DB error attempting to get an output"));
-
- db_recno_t num_elems = 0;
- cur->count(&num_elems, 0);
-
- for (uint64_t i = 0; i < num_elems; ++i)
- {
- index_vec.push_back(v);
- cur->get(&k, &v, DB_NEXT_DUP);
- }
-
- cur.close();
-
- return index_vec;
-}
-
std::vector<uint64_t> BlockchainBDB::get_tx_amount_output_indices(const crypto::hash& h) const
{
LOG_PRINT_L3("BlockchainBDB::" << __func__);
diff --git a/src/blockchain_db/berkeleydb/db_bdb.h b/src/blockchain_db/berkeleydb/db_bdb.h
index 04a33d7c6..3ae90efe1 100644
--- a/src/blockchain_db/berkeleydb/db_bdb.h
+++ b/src/blockchain_db/berkeleydb/db_bdb.h
@@ -312,7 +312,6 @@ public:
virtual tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index);
virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<tx_out_index> &indices);
- virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const;
virtual std::vector<uint64_t> get_tx_amount_output_indices(const crypto::hash& h) const;
virtual bool has_key_image(const crypto::key_image& img) const;
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index d772bf4bb..2b039f557 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -211,8 +211,6 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
if (blk.tx_hashes.size() != txs.size())
throw std::runtime_error("Inconsistent tx/hashes sizes");
- block_txn_start(false);
-
TIME_MEASURE_START(time1);
crypto::hash blk_hash = get_block_hash(blk);
TIME_MEASURE_FINISH(time1);
@@ -252,8 +250,6 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
m_hardfork->add(blk, prev_height);
- block_txn_stop();
-
++num_calls;
return prev_height;
diff --git a/src/blockchain_db/blockchain_db.h b/src/blockchain_db/blockchain_db.h
index 2c40b5a78..b6b8c6c3e 100644
--- a/src/blockchain_db/blockchain_db.h
+++ b/src/blockchain_db/blockchain_db.h
@@ -754,6 +754,21 @@ public:
virtual void batch_stop() = 0;
/**
+ * @brief aborts a batch transaction
+ *
+ * If the subclass implements batching, this function should abort the
+ * batch it is currently on.
+ *
+ * If no batch is in-progress, this function should throw a DB_ERROR.
+ * This exception may change in the future if it is deemed necessary to
+ * have a more granular exception type for this scenario.
+ *
+ * If any of this cannot be done, the subclass should throw the corresponding
+ * subclass of DB_EXCEPTION
+ */
+ virtual void batch_abort() = 0;
+
+ /**
* @brief sets whether or not to batch transactions
*
* If the subclass implements batching, this function tells it to begin
@@ -770,9 +785,12 @@ public:
*/
virtual void set_batch_transactions(bool) = 0;
- virtual void block_txn_start(bool readonly=false) = 0;
- virtual void block_txn_stop() = 0;
- virtual void block_txn_abort() = 0;
+ virtual void block_wtxn_start() = 0;
+ virtual void block_wtxn_stop() = 0;
+ virtual void block_wtxn_abort() = 0;
+ virtual bool block_rtxn_start() const = 0;
+ virtual void block_rtxn_stop() const = 0;
+ virtual void block_rtxn_abort() const = 0;
virtual void set_hard_fork(HardFork* hf);
@@ -1699,6 +1717,52 @@ public:
}; // class BlockchainDB
+class db_txn_guard
+{
+public:
+ db_txn_guard(BlockchainDB *db, bool readonly): db(db), readonly(readonly), active(false)
+ {
+ if (readonly)
+ {
+ active = db->block_rtxn_start();
+ }
+ else
+ {
+ db->block_wtxn_start();
+ active = true;
+ }
+ }
+ virtual ~db_txn_guard()
+ {
+ if (active)
+ stop();
+ }
+ void stop()
+ {
+ if (readonly)
+ db->block_rtxn_stop();
+ else
+ db->block_wtxn_stop();
+ active = false;
+ }
+ void abort()
+ {
+ if (readonly)
+ db->block_rtxn_abort();
+ else
+ db->block_wtxn_abort();
+ active = false;
+ }
+
+private:
+ BlockchainDB *db;
+ bool readonly;
+ bool active;
+};
+
+class db_rtxn_guard: public db_txn_guard { public: db_rtxn_guard(BlockchainDB *db): db_txn_guard(db, true) {} };
+class db_wtxn_guard: public db_txn_guard { public: db_wtxn_guard(BlockchainDB *db): db_txn_guard(db, false) {} };
+
BlockchainDB *new_db(const std::string& db_type);
} // namespace cryptonote
diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp
index a07e9ac55..d0fd787db 100644
--- a/src/blockchain_db/lmdb/db_lmdb.cpp
+++ b/src/blockchain_db/lmdb/db_lmdb.cpp
@@ -770,8 +770,8 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
bi.bi_timestamp = blk.timestamp;
bi.bi_coins = coins_generated;
bi.bi_weight = block_weight;
- bi.bi_diff_hi = (cumulative_difficulty >> 64).convert_to<uint64_t>();
- bi.bi_diff_lo = (cumulative_difficulty << 64 >> 64).convert_to<uint64_t>();
+ bi.bi_diff_hi = ((cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
+ bi.bi_diff_lo = (cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
bi.bi_hash = blk_hash;
bi.bi_cum_rct = num_rct_outs;
if (blk.major_version >= 4)
@@ -1077,11 +1077,11 @@ void BlockchainLMDB::add_tx_amount_output_indices(const uint64_t tx_id,
int result = 0;
- int num_outputs = amount_output_indices.size();
+ size_t num_outputs = amount_output_indices.size();
MDB_val_set(k_tx_id, tx_id);
MDB_val v;
- v.mv_data = (void *)amount_output_indices.data();
+ v.mv_data = num_outputs ? (void *)amount_output_indices.data() : (void*)"";
v.mv_size = sizeof(uint64_t) * num_outputs;
// LOG_PRINT_L1("tx_outputs[tx_hash] size: " << v.mv_size);
@@ -1953,7 +1953,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 +2056,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 +2066,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 +2154,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 +2171,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);
}
@@ -3611,16 +3660,15 @@ void BlockchainLMDB::block_rtxn_stop() const
memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
}
-void BlockchainLMDB::block_txn_start(bool readonly)
+bool BlockchainLMDB::block_rtxn_start() const
{
- if (readonly)
- {
- MDB_txn *mtxn;
- mdb_txn_cursors *mcur;
- block_rtxn_start(&mtxn, &mcur);
- return;
- }
+ MDB_txn *mtxn;
+ mdb_txn_cursors *mcur;
+ return block_rtxn_start(&mtxn, &mcur);
+}
+void BlockchainLMDB::block_wtxn_start()
+{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
// Distinguish the exceptions here from exceptions that would be thrown while
// using the txn and committing it.
@@ -3652,10 +3700,13 @@ void BlockchainLMDB::block_txn_start(bool readonly)
throw0(DB_ERROR_TXN_START((std::string("Attempted to start new write txn when batch txn already exists in ")+__FUNCTION__).c_str()));
}
-void BlockchainLMDB::block_txn_stop()
+void BlockchainLMDB::block_wtxn_stop()
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
- if (m_write_txn && m_writer == boost::this_thread::get_id())
+ if (!m_write_txn)
+ throw0(DB_ERROR_TXN_START((std::string("Attempted to stop write txn when no such txn exists in ")+__FUNCTION__).c_str()));
+ if (m_writer != boost::this_thread::get_id())
+ throw0(DB_ERROR_TXN_START((std::string("Attempted to stop write txn from the wrong thread in ")+__FUNCTION__).c_str()));
{
if (! m_batch_active)
{
@@ -3669,40 +3720,31 @@ void BlockchainLMDB::block_txn_stop()
memset(&m_wcursors, 0, sizeof(m_wcursors));
}
}
- else if (m_tinfo->m_ti_rtxn)
- {
- mdb_txn_reset(m_tinfo->m_ti_rtxn);
- memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
- }
}
-void BlockchainLMDB::block_txn_abort()
+void BlockchainLMDB::block_wtxn_abort()
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
- if (m_write_txn && m_writer == boost::this_thread::get_id())
- {
- if (! m_batch_active)
- {
- delete m_write_txn;
- m_write_txn = nullptr;
- memset(&m_wcursors, 0, sizeof(m_wcursors));
- }
- }
- else if (m_tinfo->m_ti_rtxn)
- {
- mdb_txn_reset(m_tinfo->m_ti_rtxn);
- memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
- }
- else
+ if (!m_write_txn)
+ throw0(DB_ERROR_TXN_START((std::string("Attempted to abort write txn when no such txn exists in ")+__FUNCTION__).c_str()));
+ if (m_writer != boost::this_thread::get_id())
+ throw0(DB_ERROR_TXN_START((std::string("Attempted to abort write txn from the wrong thread in ")+__FUNCTION__).c_str()));
+
+ if (! m_batch_active)
{
- // This would probably mean an earlier exception was caught, but then we
- // proceeded further than we should have.
- throw0(DB_ERROR((std::string("BlockchainLMDB::") + __func__ +
- std::string(": block-level DB transaction abort called when write txn doesn't exist")
- ).c_str()));
+ delete m_write_txn;
+ m_write_txn = nullptr;
+ memset(&m_wcursors, 0, sizeof(m_wcursors));
}
}
+void BlockchainLMDB::block_rtxn_abort() const
+{
+ LOG_PRINT_L3("BlockchainLMDB::" << __func__);
+ mdb_txn_reset(m_tinfo->m_ti_rtxn);
+ memset(&m_tinfo->m_ti_rflags, 0, sizeof(m_tinfo->m_ti_rflags));
+}
+
uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
const std::vector<std::pair<transaction, blobdata>>& txs)
{
@@ -3728,11 +3770,6 @@ uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t
{
throw;
}
- catch (...)
- {
- block_txn_abort();
- throw;
- }
return ++m_height;
}
@@ -3742,16 +3779,16 @@ void BlockchainLMDB::pop_block(block& blk, std::vector<transaction>& txs)
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
- block_txn_start(false);
+ block_wtxn_start();
try
{
BlockchainDB::pop_block(blk, txs);
- block_txn_stop();
+ block_wtxn_stop();
}
catch (...)
{
- block_txn_abort();
+ block_wtxn_abort();
throw;
}
}
diff --git a/src/blockchain_db/lmdb/db_lmdb.h b/src/blockchain_db/lmdb/db_lmdb.h
index f6b00817d..4b46f081e 100644
--- a/src/blockchain_db/lmdb/db_lmdb.h
+++ b/src/blockchain_db/lmdb/db_lmdb.h
@@ -310,11 +310,14 @@ public:
virtual void batch_stop();
virtual void batch_abort();
- virtual void block_txn_start(bool readonly);
- virtual void block_txn_stop();
- virtual void block_txn_abort();
- virtual bool block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const;
+ virtual void block_wtxn_start();
+ virtual void block_wtxn_stop();
+ virtual void block_wtxn_abort();
+ virtual bool block_rtxn_start() const;
virtual void block_rtxn_stop() const;
+ virtual void block_rtxn_abort() const;
+
+ bool block_rtxn_start(MDB_txn **mtxn, mdb_txn_cursors **mcur) const;
virtual void pop_block(block& blk, std::vector<transaction>& txs);
diff --git a/src/blockchain_db/testdb.h b/src/blockchain_db/testdb.h
index 04fad26a4..34e635899 100644
--- a/src/blockchain_db/testdb.h
+++ b/src/blockchain_db/testdb.h
@@ -42,94 +42,98 @@ namespace cryptonote
class BaseTestDB: public cryptonote::BlockchainDB {
public:
BaseTestDB() {}
- virtual void open(const std::string& filename, const int db_flags = 0) { }
- virtual void close() {}
- virtual void sync() {}
- virtual void safesyncmode(const bool onoff) {}
- virtual void reset() {}
- virtual std::vector<std::string> get_filenames() const { return std::vector<std::string>(); }
- virtual bool remove_data_file(const std::string& folder) const { return true; }
- virtual std::string get_db_name() const { return std::string(); }
- virtual bool lock() { return true; }
- virtual void unlock() { }
- virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) { return true; }
- virtual void batch_stop() {}
- virtual void set_batch_transactions(bool) {}
- virtual void block_txn_start(bool readonly=false) {}
- virtual void block_txn_stop() {}
- virtual void block_txn_abort() {}
- virtual void drop_hard_fork_info() {}
- virtual bool block_exists(const crypto::hash& h, uint64_t *height) const { return false; }
- virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); }
- virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const { return cryptonote::blobdata(); }
- virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; }
- virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; }
- virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const { return false; }
- virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const { return false; }
- virtual uint64_t get_block_height(const crypto::hash& h) const { return 0; }
- virtual cryptonote::block_header get_block_header(const crypto::hash& h) const { return cryptonote::block_header(); }
- virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; }
- virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; }
- virtual uint64_t get_top_block_timestamp() const { return 0; }
- virtual size_t get_block_weight(const uint64_t& height) const { return 128; }
- virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const { return {}; }
- virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; }
- virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; }
- virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; }
- virtual uint64_t get_block_long_term_weight(const uint64_t& height) const { return 128; }
- virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const { return {}; }
- virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const { return crypto::hash(); }
- virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<cryptonote::block>(); }
- virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const { return std::vector<crypto::hash>(); }
- virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const { if (block_height) *block_height = 0; return crypto::hash(); }
- virtual cryptonote::block get_top_block() const { return cryptonote::block(); }
- virtual uint64_t height() const { return 1; }
- virtual bool tx_exists(const crypto::hash& h) const { return false; }
- virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const { return false; }
- virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const { return 0; }
- virtual cryptonote::transaction get_tx(const crypto::hash& h) const { return cryptonote::transaction(); }
- virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const { return false; }
- virtual uint64_t get_tx_count() const { return 0; }
- virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const { return std::vector<cryptonote::transaction>(); }
- virtual uint64_t get_tx_block_height(const crypto::hash& h) const { return 0; }
- virtual uint64_t get_num_outputs(const uint64_t& amount) const { return 1; }
- virtual uint64_t get_indexing_base() const { return 0; }
- virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const { return cryptonote::output_data_t(); }
- virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const { return cryptonote::tx_out_index(); }
- virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const { return cryptonote::tx_out_index(); }
- virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const {}
- virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const {}
- virtual bool can_thread_bulk_indices() const { return false; }
- virtual std::vector<uint64_t> get_tx_output_indices(const crypto::hash& h) const { return std::vector<uint64_t>(); }
- virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const { return std::vector<std::vector<uint64_t>>(); }
- virtual bool has_key_image(const crypto::key_image& img) const { return false; }
- virtual void remove_block() { }
- virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<cryptonote::transaction, cryptonote::blobdata>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) {return 0;}
- virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) {}
- virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) {return 0;}
- virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) {}
- virtual void add_spent_key(const crypto::key_image& k_image) {}
- virtual void remove_spent_key(const crypto::key_image& k_image) {}
+ virtual void open(const std::string& filename, const int db_flags = 0) override { }
+ virtual void close() override {}
+ virtual void sync() override {}
+ virtual void safesyncmode(const bool onoff) override {}
+ virtual void reset() override {}
+ virtual std::vector<std::string> get_filenames() const override { return std::vector<std::string>(); }
+ virtual bool remove_data_file(const std::string& folder) const override { return true; }
+ virtual std::string get_db_name() const override { return std::string(); }
+ virtual bool lock() override { return true; }
+ virtual void unlock() override { }
+ virtual bool batch_start(uint64_t batch_num_blocks=0, uint64_t batch_bytes=0) override { return true; }
+ virtual void batch_stop() override {}
+ virtual void batch_abort() override {}
+ virtual void set_batch_transactions(bool) override {}
+ virtual void block_wtxn_start() override {}
+ virtual void block_wtxn_stop() override {}
+ virtual void block_wtxn_abort() override {}
+ virtual bool block_rtxn_start() const override { return true; }
+ virtual void block_rtxn_stop() const override {}
+ virtual void block_rtxn_abort() const override {}
- virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const { return true; }
- virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const { return true; }
- virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const { return true; }
- 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 { return true; }
- virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const { return true; }
- virtual bool is_read_only() const { return false; }
- virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); }
- virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const { return false; }
+ virtual void drop_hard_fork_info() override {}
+ virtual bool block_exists(const crypto::hash& h, uint64_t *height) const override { return false; }
+ virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const override { return cryptonote::t_serializable_object_to_blob(get_block_from_height(height)); }
+ virtual cryptonote::blobdata get_block_blob(const crypto::hash& h) const override { return cryptonote::blobdata(); }
+ virtual bool get_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
+ virtual bool get_pruned_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
+ virtual bool get_prunable_tx_blob(const crypto::hash& h, cryptonote::blobdata &tx) const override { return false; }
+ virtual bool get_prunable_tx_hash(const crypto::hash& tx_hash, crypto::hash &prunable_hash) const override { return false; }
+ virtual uint64_t get_block_height(const crypto::hash& h) const override { return 0; }
+ virtual cryptonote::block_header get_block_header(const crypto::hash& h) const override { return cryptonote::block_header(); }
+ virtual uint64_t get_block_timestamp(const uint64_t& height) const override { return 0; }
+ virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const override { return {}; }
+ virtual uint64_t get_top_block_timestamp() const override { return 0; }
+ virtual size_t get_block_weight(const uint64_t& height) const override { return 128; }
+ virtual std::vector<uint64_t> get_block_weights(uint64_t start_height, size_t count) const override { return {}; }
+ virtual cryptonote::difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const override { return 10; }
+ virtual cryptonote::difficulty_type get_block_difficulty(const uint64_t& height) const override { return 0; }
+ virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const override { return 10000000000; }
+ virtual uint64_t get_block_long_term_weight(const uint64_t& height) const override { return 128; }
+ virtual std::vector<uint64_t> get_long_term_block_weights(uint64_t start_height, size_t count) const override { return {}; }
+ virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const override { return crypto::hash(); }
+ virtual std::vector<cryptonote::block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const override { return std::vector<cryptonote::block>(); }
+ virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) const override { return std::vector<crypto::hash>(); }
+ virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override { if (block_height) *block_height = 0; return crypto::hash(); }
+ virtual cryptonote::block get_top_block() const override { return cryptonote::block(); }
+ virtual uint64_t height() const override { return 1; }
+ virtual bool tx_exists(const crypto::hash& h) const override { return false; }
+ virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const override { return false; }
+ virtual uint64_t get_tx_unlock_time(const crypto::hash& h) const override { return 0; }
+ virtual cryptonote::transaction get_tx(const crypto::hash& h) const override { return cryptonote::transaction(); }
+ virtual bool get_tx(const crypto::hash& h, cryptonote::transaction &tx) const override { return false; }
+ virtual uint64_t get_tx_count() const override { return 0; }
+ virtual std::vector<cryptonote::transaction> get_tx_list(const std::vector<crypto::hash>& hlist) const override { return std::vector<cryptonote::transaction>(); }
+ virtual uint64_t get_tx_block_height(const crypto::hash& h) const override { return 0; }
+ virtual uint64_t get_num_outputs(const uint64_t& amount) const override { return 1; }
+ virtual uint64_t get_indexing_base() const override { return 0; }
+ virtual cryptonote::output_data_t get_output_key(const uint64_t& amount, const uint64_t& index, bool include_commitmemt) const override { return cryptonote::output_data_t(); }
+ virtual cryptonote::tx_out_index get_output_tx_and_index_from_global(const uint64_t& index) const override { return cryptonote::tx_out_index(); }
+ virtual cryptonote::tx_out_index get_output_tx_and_index(const uint64_t& amount, const uint64_t& index) const override { return cryptonote::tx_out_index(); }
+ virtual void get_output_tx_and_index(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<cryptonote::tx_out_index> &indices) const override {}
+ virtual void get_output_key(const epee::span<const uint64_t> &amounts, const std::vector<uint64_t> &offsets, std::vector<cryptonote::output_data_t> &outputs, bool allow_partial = false) const override {}
+ virtual bool can_thread_bulk_indices() const override { return false; }
+ virtual std::vector<std::vector<uint64_t>> get_tx_amount_output_indices(const uint64_t tx_index, size_t n_txes) const override { return std::vector<std::vector<uint64_t>>(); }
+ virtual bool has_key_image(const crypto::key_image& img) const override { return false; }
+ virtual void remove_block() override { }
+ virtual uint64_t add_transaction_data(const crypto::hash& blk_hash, const std::pair<cryptonote::transaction, cryptonote::blobdata>& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash) override {return 0;}
+ virtual void remove_transaction_data(const crypto::hash& tx_hash, const cryptonote::transaction& tx) override {}
+ virtual uint64_t add_output(const crypto::hash& tx_hash, const cryptonote::tx_out& tx_output, const uint64_t& local_index, const uint64_t unlock_time, const rct::key *commitment) override {return 0;}
+ virtual void add_tx_amount_output_indices(const uint64_t tx_index, const std::vector<uint64_t>& amount_output_indices) override {}
+ virtual void add_spent_key(const crypto::key_image& k_image) override {}
+ virtual void remove_spent_key(const crypto::key_image& k_image) override {}
- virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) {}
- virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) {}
- virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const { return 0; }
- virtual bool txpool_has_tx(const crypto::hash &txid) const { return false; }
- virtual void remove_txpool_tx(const crypto::hash& txid) {}
- virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const { return false; }
- virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const { return false; }
- virtual uint64_t get_database_size() const { return 0; }
- virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const { return ""; }
- virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; }
+ virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const override { return true; }
+ virtual bool for_blocks_range(const uint64_t&, const uint64_t&, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const override { return true; }
+ virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const override { return true; }
+ 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 override { return true; }
+ virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const override { return true; }
+ virtual bool is_read_only() const override { return false; }
+ virtual std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> get_output_histogram(const std::vector<uint64_t> &amounts, bool unlocked, uint64_t recent_cutoff, uint64_t min_count) const override { return std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>>(); }
+ virtual bool get_output_distribution(uint64_t amount, uint64_t from_height, uint64_t to_height, std::vector<uint64_t> &distribution, uint64_t &base) const override { return false; }
+
+ virtual void add_txpool_tx(const crypto::hash &txid, const cryptonote::blobdata &blob, const cryptonote::txpool_tx_meta_t& details) override {}
+ virtual void update_txpool_tx(const crypto::hash &txid, const cryptonote::txpool_tx_meta_t& details) override {}
+ virtual uint64_t get_txpool_tx_count(bool include_unrelayed_txes = true) const override { return 0; }
+ virtual bool txpool_has_tx(const crypto::hash &txid) const override { return false; }
+ virtual void remove_txpool_tx(const crypto::hash& txid) override {}
+ virtual bool get_txpool_tx_meta(const crypto::hash& txid, cryptonote::txpool_tx_meta_t &meta) const override { return false; }
+ virtual bool get_txpool_tx_blob(const crypto::hash& txid, cryptonote::blobdata &bd) const override { return false; }
+ virtual uint64_t get_database_size() const override { return 0; }
+ virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid) const override { return ""; }
+ virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const override { return false; }
virtual void add_block( const cryptonote::block& blk
, size_t block_weight
@@ -138,20 +142,20 @@ public:
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, const crypto::hash& blk_hash
- ) { }
- virtual cryptonote::block get_block_from_height(const uint64_t& height) const { return cryptonote::block(); }
- virtual void set_hard_fork_version(uint64_t height, uint8_t version) {}
- virtual uint8_t get_hard_fork_version(uint64_t height) const { return 0; }
- virtual void check_hard_fork_info() {}
+ ) override { }
+ virtual cryptonote::block get_block_from_height(const uint64_t& height) const override { return cryptonote::block(); }
+ virtual void set_hard_fork_version(uint64_t height, uint8_t version) override {}
+ virtual uint8_t get_hard_fork_version(uint64_t height) const override { return 0; }
+ virtual void check_hard_fork_info() override {}
- virtual uint32_t get_blockchain_pruning_seed() const { return 0; }
- virtual bool prune_blockchain(uint32_t pruning_seed = 0) { return true; }
- virtual bool update_pruning() { return true; }
- virtual bool check_pruning() { return true; }
- virtual void prune_outputs(uint64_t amount) {}
+ virtual uint32_t get_blockchain_pruning_seed() const override { return 0; }
+ virtual bool prune_blockchain(uint32_t pruning_seed = 0) override { return true; }
+ virtual bool update_pruning() override { return true; }
+ virtual bool check_pruning() override { return true; }
+ virtual void prune_outputs(uint64_t amount) override {}
- virtual uint64_t get_max_block_size() { return 100000000; }
- virtual void add_max_block_size(uint64_t sz) { }
+ virtual uint64_t get_max_block_size() override { return 100000000; }
+ virtual void add_max_block_size(uint64_t sz) override { }
};
}
diff --git a/src/blockchain_utilities/README.md b/src/blockchain_utilities/README.md
index ad5963f27..1462e3186 100644
--- a/src/blockchain_utilities/README.md
+++ b/src/blockchain_utilities/README.md
@@ -79,7 +79,7 @@ LMDB flags (more than one may be specified):
## Examples:
-```
+```bash
$ monero-blockchain-import --database lmdb#fastest
$ monero-blockchain-import --database lmdb#nosync
diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp
index 8454595ac..cb9154f29 100644
--- a/src/blockchain_utilities/blockchain_import.cpp
+++ b/src/blockchain_utilities/blockchain_import.cpp
@@ -194,7 +194,11 @@ int check_flush(cryptonote::core &core, std::vector<block_complete_entry> &block
core.prevalidate_block_hashes(core.get_blockchain_storage().get_db().height(), hashes);
std::vector<block> pblocks;
- core.prepare_handle_incoming_blocks(blocks, pblocks);
+ if (!core.prepare_handle_incoming_blocks(blocks, pblocks))
+ {
+ MERROR("Failed to prepare to add blocks");
+ return 1;
+ }
if (!pblocks.empty() && pblocks.size() != blocks.size())
{
MERROR("Unexpected parsed blocks size");
diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat
index adc433522..a7d309753 100644
--- a/src/blocks/checkpoints.dat
+++ b/src/blocks/checkpoints.dat
Binary files differ
diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp
index e31b96646..11bbe2e24 100644
--- a/src/checkpoints/checkpoints.cpp
+++ b/src/checkpoints/checkpoints.cpp
@@ -209,6 +209,7 @@ namespace cryptonote
ADD_CHECKPOINT(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241");
ADD_CHECKPOINT(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568");
ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e");
+ ADD_CHECKPOINT(1856000, "9b57f17f29c71a3acd8a7904b93c41fa6eb8d2b7c73936ce4f1702d14880ba29");
return true;
}
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/password.cpp b/src/common/password.cpp
index 03d13db42..33e1f48fd 100644
--- a/src/common/password.cpp
+++ b/src/common/password.cpp
@@ -57,7 +57,7 @@ namespace
DWORD mode_old;
::GetConsoleMode(h_cin, &mode_old);
- DWORD mode_new = mode_old & ~(hide_input ? ENABLE_ECHO_INPUT : 0);
+ DWORD mode_new = mode_old & ~((hide_input ? ENABLE_ECHO_INPUT : 0) | ENABLE_LINE_INPUT);
::SetConsoleMode(h_cin, mode_new);
bool r = true;
@@ -77,10 +77,6 @@ namespace
}
else if (ucs2_ch == L'\r')
{
- continue;
- }
- else if (ucs2_ch == L'\n')
- {
std::cout << std::endl;
break;
}
diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h
index cb5f79da8..dab3e562d 100644
--- a/src/common/rpc_client.h
+++ b/src/common/rpc_client.h
@@ -36,6 +36,7 @@
#include "storages/http_abstract_invoke.h"
#include "net/http_auth.h"
#include "net/http_client.h"
+#include "net/net_ssl.h"
#include "string_tools.h"
namespace tools
@@ -49,11 +50,12 @@ namespace tools
uint32_t ip
, uint16_t port
, boost::optional<epee::net_utils::http::login> user
+ , epee::net_utils::ssl_options_t ssl_options
)
: m_http_client{}
{
m_http_client.set_server(
- epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port), std::move(user)
+ epee::string_tools::get_ip_string_from_int32(ip), std::to_string(port), std::move(user), std::move(ssl_options)
);
}
diff --git a/src/common/util.cpp b/src/common/util.cpp
index 3388974ce..db5aa3052 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -69,6 +69,9 @@ using namespace epee;
#include "net/http_client.h" // epee::net_utils::...
#ifdef WIN32
+#ifndef STRSAFE_NO_DEPRECATE
+#define STRSAFE_NO_DEPRECATE
+#endif
#include <windows.h>
#include <shlobj.h>
#include <strsafe.h>
diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c
index 170911262..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
@@ -116,7 +119,8 @@ void keccak(const uint8_t *in, size_t inlen, uint8_t *md, int mdlen)
local_abort("Bad keccak use");
}
- memcpy(temp, in, inlen);
+ if (inlen > 0)
+ memcpy(temp, in, inlen);
temp[inlen++] = 1;
memset(temp + inlen, 0, rsiz - inlen);
temp[rsiz - 1] |= 0x80;
diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c
index 7802fb67f..0a5860f3b 100644
--- a/src/crypto/tree-hash.c
+++ b/src/crypto/tree-hash.c
@@ -30,6 +30,7 @@
#include <assert.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include "hash-ops.h"
@@ -82,23 +83,24 @@ void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) {
size_t cnt = tree_hash_cnt( count );
- char ints[cnt][HASH_SIZE];
- memset(ints, 0 , sizeof(ints)); // zero out as extra protection for using uninitialized mem
+ char *ints = calloc(cnt, HASH_SIZE); // zero out as extra protection for using uninitialized mem
+ assert(ints);
memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE);
for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) {
- cn_fast_hash(hashes[i], 64, ints[j]);
+ cn_fast_hash(hashes[i], 64, ints + j * HASH_SIZE);
}
assert(i == count);
while (cnt > 2) {
cnt >>= 1;
for (i = 0, j = 0; j < cnt; i += 2, ++j) {
- cn_fast_hash(ints[i], 64, ints[j]);
+ cn_fast_hash(ints + i * HASH_SIZE, 64, ints + j * HASH_SIZE);
}
}
- cn_fast_hash(ints[0], 64, root_hash);
+ cn_fast_hash(ints, 64, root_hash);
+ free(ints);
}
}
diff --git a/src/cryptonote_basic/cryptonote_basic.h b/src/cryptonote_basic/cryptonote_basic.h
index 20d92bdf1..055c4a22b 100644
--- a/src/cryptonote_basic/cryptonote_basic.h
+++ b/src/cryptonote_basic/cryptonote_basic.h
@@ -320,7 +320,7 @@ namespace cryptonote
}
if (!typename Archive<W>::is_saving())
pruned = true;
- return true;
+ return ar.stream().good();
}
private:
diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h
index 3dd98f0c6..79ce610a9 100644
--- a/src/cryptonote_basic/cryptonote_boost_serialization.h
+++ b/src/cryptonote_basic/cryptonote_boost_serialization.h
@@ -365,11 +365,11 @@ namespace boost
else
{
// store high part
- cryptonote::difficulty_type x_ = x >> 64;
+ cryptonote::difficulty_type x_ = (x >> 64) & 0xffffffffffffffff;
uint64_t v = x_.convert_to<uint64_t>();
a & v;
// store low part
- x_ = x << 64 >> 64;
+ x_ = x & 0xffffffffffffffff;
v = x_.convert_to<uint64_t>();
a & v;
}
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 566622c1a..7d7de416d 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -221,8 +221,7 @@ namespace cryptonote
tx.invalidate_hashes();
//TODO: validate tx
- get_transaction_hash(tx, tx_hash);
- return true;
+ return get_transaction_hash(tx, tx_hash);
}
//---------------------------------------------------------------
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash)
@@ -975,6 +974,7 @@ namespace cryptonote
{
crypto::hash h = null_hash;
get_transaction_hash(t, h, NULL);
+ CHECK_AND_ASSERT_THROW_MES(get_transaction_hash(t, h, NULL), "Failed to calculate transaction hash");
return h;
}
//---------------------------------------------------------------
@@ -1327,7 +1327,7 @@ namespace cryptonote
txs_ids.reserve(1 + b.tx_hashes.size());
crypto::hash h = null_hash;
size_t bl_sz = 0;
- get_transaction_hash(b.miner_tx, h, bl_sz);
+ CHECK_AND_ASSERT_THROW_MES(get_transaction_hash(b.miner_tx, h, bl_sz), "Failed to calculate transaction hash");
txs_ids.push_back(h);
for(auto& th: b.tx_hashes)
txs_ids.push_back(th);
diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp
index 89bca2f09..98158a513 100644
--- a/src/cryptonote_basic/hardfork.cpp
+++ b/src/cryptonote_basic/hardfork.cpp
@@ -185,26 +185,8 @@ void HardFork::init()
else
height = 1;
- bool populate = false;
- try
- {
- db.get_hard_fork_version(0);
- }
- catch (...) { populate = true; }
- if (populate) {
- MINFO("The DB has no hard fork info, reparsing from start");
- height = 1;
- }
- MDEBUG("reorganizing from " << height);
- if (populate) {
- reorganize_from_chain_height(height);
- // reorg will not touch the genesis block, use this as a flag for populating done
- db.set_hard_fork_version(0, original_version);
- }
- else {
- rescan_from_chain_height(height);
- }
- MDEBUG("reorganization done");
+ rescan_from_chain_height(height);
+ MDEBUG("init done");
}
uint8_t HardFork::get_block_version(uint64_t height) const
@@ -266,11 +248,9 @@ bool HardFork::reorganize_from_chain_height(uint64_t height)
bool HardFork::rescan_from_block_height(uint64_t height)
{
CRITICAL_REGION_LOCAL(lock);
- db.block_txn_start(true);
- if (height >= db.height()) {
- db.block_txn_stop();
+ db_rtxn_guard rtxn_guard(&db);
+ if (height >= db.height())
return false;
- }
versions.clear();
@@ -293,8 +273,6 @@ bool HardFork::rescan_from_block_height(uint64_t height)
current_fork_index = voted;
}
- db.block_txn_stop();
-
return true;
}
@@ -314,8 +292,7 @@ void HardFork::on_block_popped(uint64_t nblocks)
const uint64_t new_chain_height = db.height();
const uint64_t old_chain_height = new_chain_height + nblocks;
uint8_t version;
- uint64_t height;
- for (height = old_chain_height - 1; height >= new_chain_height; --height)
+ for (uint64_t height = old_chain_height - 1; height >= new_chain_height; --height)
{
version = versions.back();
last_versions[version]--;
@@ -327,7 +304,7 @@ void HardFork::on_block_popped(uint64_t nblocks)
// does not take voting into account
for (current_fork_index = heights.size() - 1; current_fork_index > 0; --current_fork_index)
- if (height >= heights[current_fork_index].height)
+ if (new_chain_height >= heights[current_fork_index].height)
break;
}
diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp
index e6c6bddb6..e594eb049 100644
--- a/src/cryptonote_basic/miner.cpp
+++ b/src/cryptonote_basic/miner.cpp
@@ -123,7 +123,7 @@ namespace cryptonote
m_miner_extra_sleep(BACKGROUND_MINING_DEFAULT_MINER_EXTRA_SLEEP_MILLIS),
m_block_reward(0)
{
-
+ m_attrs.set_stack_size(THREAD_STACK_SIZE);
}
//-----------------------------------------------------------------------------------------------------
miner::~miner()
@@ -360,7 +360,7 @@ namespace cryptonote
return m_threads_total;
}
//-----------------------------------------------------------------------------------------------------
- bool miner::start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background, bool ignore_battery)
+ bool miner::start(const account_public_address& adr, size_t threads_count, bool do_background, bool ignore_battery)
{
m_block_reward = 0;
m_mine_address = adr;
@@ -371,7 +371,6 @@ namespace cryptonote
m_threads_autodetect.push_back({epee::misc_utils::get_ns_count(), m_total_hashes});
m_threads_total = 1;
}
- m_attrs = attrs;
m_starter_nonce = crypto::rand<uint32_t>();
CRITICAL_REGION_LOCAL(m_threads_lock);
if(is_mining())
@@ -395,7 +394,7 @@ namespace cryptonote
for(size_t i = 0; i != m_threads_total; i++)
{
- m_threads.push_back(boost::thread(attrs, boost::bind(&miner::worker_thread, this)));
+ m_threads.push_back(boost::thread(m_attrs, boost::bind(&miner::worker_thread, this)));
}
if (threads_count == 0)
@@ -405,7 +404,7 @@ namespace cryptonote
if( get_is_background_mining_enabled() )
{
- m_background_mining_thread = boost::thread(attrs, boost::bind(&miner::background_worker_thread, this));
+ m_background_mining_thread = boost::thread(m_attrs, boost::bind(&miner::background_worker_thread, this));
LOG_PRINT_L0("Background mining controller thread started" );
}
@@ -487,10 +486,7 @@ namespace cryptonote
{
if(m_do_mining)
{
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
-
- start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled(), get_ignore_battery());
+ start(m_mine_address, m_threads_total, get_is_background_mining_enabled(), get_ignore_battery());
}
}
//-----------------------------------------------------------------------------------------------------
@@ -528,6 +524,7 @@ namespace cryptonote
uint32_t local_template_ver = 0;
block b;
slow_hash_allocate_state();
+ ++m_threads_active;
while(!m_stop)
{
if(m_pausers_count)//anti split workaround
diff --git a/src/cryptonote_basic/miner.h b/src/cryptonote_basic/miner.h
index 285075f51..ac7a0381c 100644
--- a/src/cryptonote_basic/miner.h
+++ b/src/cryptonote_basic/miner.h
@@ -64,7 +64,7 @@ namespace cryptonote
static void init_options(boost::program_options::options_description& desc);
bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height, uint64_t block_reward);
bool on_block_chain_update();
- bool start(const account_public_address& adr, size_t threads_count, const boost::thread::attributes& attrs, bool do_background = false, bool ignore_battery = false);
+ bool start(const account_public_address& adr, size_t threads_count, bool do_background = false, bool ignore_battery = false);
uint64_t get_speed() const;
uint32_t get_threads_count() const;
void send_stop_signal();
diff --git a/src/cryptonote_core/CMakeLists.txt b/src/cryptonote_core/CMakeLists.txt
index fb96de226..2cbe89b01 100644
--- a/src/cryptonote_core/CMakeLists.txt
+++ b/src/cryptonote_core/CMakeLists.txt
@@ -30,6 +30,7 @@ set(cryptonote_core_sources
blockchain.cpp
cryptonote_core.cpp
tx_pool.cpp
+ tx_sanity_check.cpp
cryptonote_tx_utils.cpp)
set(cryptonote_core_headers)
@@ -39,6 +40,7 @@ set(cryptonote_core_private_headers
blockchain.h
cryptonote_core.h
tx_pool.h
+ tx_sanity_check.h
cryptonote_tx_utils.h)
monero_private_headers(cryptonote_core
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 7ef8f8c45..ac2c07913 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -179,9 +179,11 @@ Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_long_term_block_weights_window(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
m_long_term_effective_median_block_weight(0),
m_long_term_block_weights_cache_tip_hash(crypto::null_hash),
+ m_long_term_block_weights_cache_rolling_median(CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE),
m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1),
- m_btc_valid(false)
+ m_btc_valid(false),
+ m_batch_success(true)
{
LOG_PRINT_L3("Blockchain::" << __func__);
}
@@ -428,6 +430,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
block bl;
block_verification_context bvc = boost::value_initialized<block_verification_context>();
generate_genesis_block(bl, get_config(m_nettype).GENESIS_TX, get_config(m_nettype).GENESIS_NONCE);
+ db_wtxn_guard wtxn_guard(m_db);
add_new_block(bl, bvc);
CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain");
}
@@ -443,7 +446,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
m_db->fixup();
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
+
// check how far behind we are
uint64_t top_block_timestamp = m_db->get_top_block_timestamp();
uint64_t timestamp_diff = time(NULL) - top_block_timestamp;
@@ -464,7 +468,8 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
#endif
MINFO("Blockchain initialized. last block: " << m_db->height() - 1 << ", " << epee::misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block());
- m_db->block_txn_stop();
+
+ rtxn_guard.stop();
uint64_t num_popped_blocks = 0;
while (!m_db->is_read_only())
@@ -516,10 +521,16 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
}
if (test_options && test_options->long_term_block_weight_window)
+ {
m_long_term_block_weights_window = test_options->long_term_block_weight_window;
+ m_long_term_block_weights_cache_rolling_median = epee::misc_utils::rolling_median_t<uint64_t>(m_long_term_block_weights_window);
+ }
- if (!update_next_cumulative_weight_limit())
- return false;
+ {
+ db_txn_guard txn_guard(m_db, m_db->is_read_only());
+ if (!update_next_cumulative_weight_limit())
+ return false;
+ }
return true;
}
//------------------------------------------------------------------
@@ -609,17 +620,13 @@ void Blockchain::pop_blocks(uint64_t nblocks)
CRITICAL_REGION_LOCAL(m_tx_pool);
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
- while (!m_db->batch_start())
- {
- m_blockchain_lock.unlock();
- m_tx_pool.unlock();
- epee::misc_utils::sleep_no_w(1000);
- m_tx_pool.lock();
- m_blockchain_lock.lock();
- }
+ bool stop_batch = m_db->batch_start();
try
{
+ const uint64_t blockchain_height = m_db->height();
+ if (blockchain_height > 0)
+ nblocks = std::min(nblocks, blockchain_height - 1);
for (i=0; i < nblocks; ++i)
{
pop_block_from_blockchain();
@@ -627,10 +634,14 @@ void Blockchain::pop_blocks(uint64_t nblocks)
}
catch (const std::exception& e)
{
- LOG_ERROR("Error when popping blocks, only " << i << " blocks are popped: " << e.what());
+ LOG_ERROR("Error when popping blocks after processing " << i << " blocks: " << e.what());
+ if (stop_batch)
+ m_db->batch_abort();
+ return;
}
- m_db->batch_stop();
+ if (stop_batch)
+ m_db->batch_stop();
}
//------------------------------------------------------------------
// This function tells BlockchainDB to remove the top block from the
@@ -725,6 +736,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
m_db->reset();
m_hardfork->init();
+ db_wtxn_guard wtxn_guard(m_db);
block_verification_context bvc = boost::value_initialized<block_verification_context>();
add_new_block(b, bvc);
if (!update_next_cumulative_weight_limit())
@@ -772,7 +784,7 @@ bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const
if(!sz)
return true;
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
bool genesis_included = false;
uint64_t current_back_offset = 1;
while(current_back_offset < sz)
@@ -799,7 +811,6 @@ bool Blockchain::get_short_chain_history(std::list<crypto::hash>& ids) const
{
ids.push_back(m_db->get_block_hash_from_height(0));
}
- m_db->block_txn_stop();
return true;
}
@@ -1277,21 +1288,20 @@ void Blockchain::get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_
weights = m_db->get_block_weights(start_offset, count);
}
//------------------------------------------------------------------
-void Blockchain::get_long_term_block_weights(std::vector<uint64_t>& weights, uint64_t start_height, size_t count) const
+uint64_t Blockchain::get_long_term_block_weight_median(uint64_t start_height, size_t count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
PERF_TIMER(get_long_term_block_weights);
- if (count == 0)
- return;
+ CHECK_AND_ASSERT_THROW_MES(count > 0, "count == 0");
bool cached = false;
uint64_t blockchain_height = m_db->height();
uint64_t tip_height = start_height + count - 1;
crypto::hash tip_hash = crypto::null_hash;
- if (tip_height < blockchain_height && count == m_long_term_block_weights_cache.size())
+ if (tip_height < blockchain_height && count == (size_t)m_long_term_block_weights_cache_rolling_median.size())
{
tip_hash = m_db->get_block_hash_from_height(tip_height);
cached = tip_hash == m_long_term_block_weights_cache_tip_hash;
@@ -1300,32 +1310,30 @@ void Blockchain::get_long_term_block_weights(std::vector<uint64_t>& weights, uin
if (cached)
{
MTRACE("requesting " << count << " from " << start_height << ", cached");
- weights = m_long_term_block_weights_cache;
- return;
+ return m_long_term_block_weights_cache_rolling_median.median();
}
// in the vast majority of uncached cases, most is still cached,
// as we just move the window one block up:
- if (tip_height > 0 && count == m_long_term_block_weights_cache.size() && tip_height < blockchain_height)
+ if (tip_height > 0 && count == (size_t)m_long_term_block_weights_cache_rolling_median.size() && tip_height < blockchain_height)
{
crypto::hash old_tip_hash = m_db->get_block_hash_from_height(tip_height - 1);
if (old_tip_hash == m_long_term_block_weights_cache_tip_hash)
{
- weights = m_long_term_block_weights_cache;
- for (size_t i = 1; i < weights.size(); ++i)
- weights[i - 1] = weights[i];
MTRACE("requesting " << count << " from " << start_height << ", incremental");
- weights.back() = m_db->get_block_long_term_weight(tip_height);
- m_long_term_block_weights_cache = weights;
m_long_term_block_weights_cache_tip_hash = tip_hash;
- return;
+ m_long_term_block_weights_cache_rolling_median.insert(m_db->get_block_long_term_weight(tip_height));
+ return m_long_term_block_weights_cache_rolling_median.median();
}
}
MTRACE("requesting " << count << " from " << start_height << ", uncached");
- weights = m_db->get_long_term_block_weights(start_height, count);
- m_long_term_block_weights_cache = weights;
+ std::vector<uint64_t> weights = m_db->get_long_term_block_weights(start_height, count);
m_long_term_block_weights_cache_tip_hash = tip_hash;
+ m_long_term_block_weights_cache_rolling_median.clear();
+ for (uint64_t w: weights)
+ m_long_term_block_weights_cache_rolling_median.insert(w);
+ return m_long_term_block_weights_cache_rolling_median.median();
}
//------------------------------------------------------------------
uint64_t Blockchain::get_current_cumulative_block_weight_limit() const
@@ -1366,7 +1374,8 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
// just as we compare it, we'll just use a slightly old template, but
// this would be the case anyway if we'd lock, and the change happened
// just after the block template was created
- if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce && m_btc_pool_cookie == m_tx_pool.cookie()) {
+ if (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address)) && m_btc_nonce == ex_nonce
+ && m_btc_pool_cookie == m_tx_pool.cookie() && m_btc.prev_id == get_tail_id()) {
MDEBUG("Using cached template");
m_btc.timestamp = time(NULL); // update timestamp unconditionally
b = m_btc;
@@ -1866,7 +1875,7 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard (m_db);
rsp.current_blockchain_height = get_current_blockchain_height();
std::vector<std::pair<cryptonote::blobdata,block>> blocks;
get_blocks(arg.blocks, blocks, rsp.missed_ids);
@@ -1884,16 +1893,19 @@ 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
// and missed.
rsp.missed_ids.insert(rsp.missed_ids.end(), missed_tx_ids.begin(), missed_tx_ids.end());
- m_db->block_txn_stop();
return false;
}
@@ -1903,7 +1915,6 @@ bool Blockchain::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NO
//get and pack other transactions, if needed
get_transactions_blobs(arg.txs, rsp.txs, rsp.missed_ids);
- m_db->block_txn_stop();
return true;
}
//------------------------------------------------------------------
@@ -2075,14 +2086,13 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
return false;
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
// make sure that the last block in the request's block list matches
// the genesis block
auto gen_hash = m_db->get_block_hash_from_height(0);
if(qblock_ids.back() != gen_hash)
{
MCERROR("net.p2p", "Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block mismatch: " << std::endl << "id: " << qblock_ids.back() << ", " << std::endl << "expected: " << gen_hash << "," << std::endl << " dropping connection");
- m_db->block_txn_abort();
return false;
}
@@ -2100,11 +2110,9 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
catch (const std::exception& e)
{
MWARNING("Non-critical error trying to find block by hash in BlockchainDB, hash: " << *bl_it);
- m_db->block_txn_abort();
return false;
}
}
- m_db->block_txn_stop();
// this should be impossible, as we checked that we share the genesis block,
// but just in case...
@@ -2284,7 +2292,7 @@ bool Blockchain::get_transactions(const t_ids_container& txs_ids, t_tx_container
// Find the split point between us and foreign blockchain and return
// (by reference) the most recent common block hash along with up to
// BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT additional (more recent) hashes.
-bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const
+bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
@@ -2295,11 +2303,15 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
return false;
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
current_height = get_current_blockchain_height();
- const uint32_t pruning_seed = get_blockchain_pruning_seed();
- start_height = tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed);
- uint64_t stop_height = tools::get_next_pruned_block_height(start_height, current_height, pruning_seed);
+ uint64_t stop_height = current_height;
+ if (clip_pruned)
+ {
+ const uint32_t pruning_seed = get_blockchain_pruning_seed();
+ start_height = tools::get_next_unpruned_block_height(start_height, current_height, pruning_seed);
+ stop_height = tools::get_next_pruned_block_height(start_height, current_height, pruning_seed);
+ }
size_t count = 0;
hashes.reserve(std::min((size_t)(stop_height - start_height), (size_t)BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT));
for(size_t i = start_height; i < stop_height && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++)
@@ -2307,7 +2319,6 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
hashes.push_back(m_db->get_block_hash_from_height(i));
}
- m_db->block_txn_stop();
return true;
}
@@ -2316,12 +2327,12 @@ bool Blockchain::find_blockchain_supplement(const std::list<crypto::hash>& qbloc
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height);
+ bool result = find_blockchain_supplement(qblock_ids, resp.m_block_ids, resp.start_height, resp.total_height, true);
if (result)
{
cryptonote::difficulty_type wide_cumulative_difficulty = m_db->get_block_cumulative_difficulty(resp.total_height - 1);
- resp.cumulative_difficulty = (wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>();
- resp.cumulative_difficulty_top64 = (wide_cumulative_difficulty >> 64).convert_to<uint64_t>();
+ resp.cumulative_difficulty = (wide_cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
+ resp.cumulative_difficulty_top64 = ((wide_cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
}
return result;
@@ -2354,7 +2365,7 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
}
}
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
total_height = get_current_blockchain_height();
size_t count = 0, size = 0;
blocks.reserve(std::min(std::min(max_count, (size_t)10000), (size_t)(total_height - start_height)));
@@ -2380,7 +2391,6 @@ bool Blockchain::find_blockchain_supplement(const uint64_t req_start_block, cons
blocks.back().second.push_back(std::make_pair(b.tx_hashes[i], std::move(txs[i])));
}
}
- m_db->block_txn_stop();
return true;
}
//------------------------------------------------------------------
@@ -3535,7 +3545,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
static bool seen_future_version = false;
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
uint64_t blockchain_height;
const crypto::hash top_hash = get_tail_id(blockchain_height);
++blockchain_height; // block height to chain height
@@ -3544,7 +3554,6 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash&
MERROR_VER("Block with id: " << id << std::endl << "has wrong prev_id: " << bl.prev_id << std::endl << "expected: " << top_hash);
bvc.m_verifivation_failed = true;
leave:
- m_db->block_txn_stop();
return false;
}
@@ -3827,7 +3836,7 @@ leave:
if(precomputed)
block_processing_time += m_fake_pow_calc_time;
- m_db->block_txn_stop();
+ rtxn_guard.stop();
TIME_MEASURE_START(addblock);
uint64_t new_height = 0;
if (!bvc.m_verifivation_failed)
@@ -3841,6 +3850,7 @@ leave:
catch (const KEY_IMAGE_EXISTS& e)
{
LOG_ERROR("Error adding block with hash: " << id << " to blockchain, what = " << e.what());
+ m_batch_success = false;
bvc.m_verifivation_failed = true;
return_tx_to_pool(txs);
return false;
@@ -3849,6 +3859,7 @@ leave:
{
//TODO: figure out the best way to deal with this failure
LOG_ERROR("Error adding block with hash: " << id << " to blockchain, what = " << e.what());
+ m_batch_success = false;
bvc.m_verifivation_failed = true;
return_tx_to_pool(txs);
return false;
@@ -3896,6 +3907,10 @@ leave:
//------------------------------------------------------------------
bool Blockchain::prune_blockchain(uint32_t pruning_seed)
{
+ m_tx_pool.lock();
+ epee::misc_utils::auto_scope_leave_caller unlocker = epee::misc_utils::create_scope_leave_handler([&](){m_tx_pool.unlock();});
+ CRITICAL_REGION_LOCAL(m_blockchain_lock);
+
return m_db->prune_blockchain(pruning_seed);
}
//------------------------------------------------------------------
@@ -3928,9 +3943,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT)
return block_weight;
- std::vector<uint64_t> weights;
- get_long_term_block_weights(weights, db_height - nblocks, nblocks);
- uint64_t long_term_median = epee::misc_utils::median(weights);
+ uint64_t long_term_median = get_long_term_block_weight_median(db_height - nblocks, nblocks);
uint64_t 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 = long_term_effective_median_block_weight + long_term_effective_median_block_weight * 2 / 5;
@@ -3945,8 +3958,6 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
LOG_PRINT_L3("Blockchain::" << __func__);
- m_db->block_txn_start(false);
-
// when we reach this, the last hf version is not yet written to the db
const uint64_t db_height = m_db->height();
const uint8_t hf_version = get_current_hard_fork_version();
@@ -3964,7 +3975,6 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
{
const uint64_t block_weight = m_db->get_block_weight(db_height - 1);
- std::vector<uint64_t> weights, new_weights;
uint64_t long_term_median;
if (db_height == 1)
{
@@ -3975,9 +3985,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
if (nblocks == db_height)
--nblocks;
- get_long_term_block_weights(weights, db_height - nblocks - 1, nblocks);
- new_weights = weights;
- long_term_median = epee::misc_utils::median(weights);
+ long_term_median = get_long_term_block_weight_median(db_height - nblocks - 1, nblocks);
}
m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
@@ -3985,14 +3993,19 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
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);
- if (new_weights.empty())
- new_weights.resize(1);
- new_weights[0] = long_term_block_weight;
- long_term_median = epee::misc_utils::median(new_weights);
+ if (db_height == 1)
+ {
+ long_term_median = long_term_block_weight;
+ }
+ else
+ {
+ m_long_term_block_weights_cache_tip_hash = m_db->get_block_hash_from_height(db_height - 1);
+ m_long_term_block_weights_cache_rolling_median.insert(long_term_block_weight);
+ long_term_median = m_long_term_block_weights_cache_rolling_median.median();
+ }
m_long_term_effective_median_block_weight = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5, long_term_median);
- short_term_constraint = m_long_term_effective_median_block_weight + m_long_term_effective_median_block_weight * 2 / 5;
- weights.clear();
+ std::vector<uint64_t> weights;
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
uint64_t short_term_median = epee::misc_utils::median(weights);
@@ -4009,9 +4022,8 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti
if (long_term_effective_median_block_weight)
*long_term_effective_median_block_weight = m_long_term_effective_median_block_weight;
- m_db->add_max_block_size(m_current_block_cumul_weight_limit);
-
- m_db->block_txn_stop();
+ if (!m_db->is_read_only())
+ m_db->add_max_block_size(m_current_block_cumul_weight_limit);
return true;
}
@@ -4022,12 +4034,11 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
crypto::hash id = get_block_hash(bl);
CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process
CRITICAL_REGION_LOCAL1(m_blockchain_lock);
- m_db->block_txn_start(true);
+ db_rtxn_guard rtxn_guard(m_db);
if(have_block(id))
{
LOG_PRINT_L3("block with id = " << id << " already exists");
bvc.m_already_exists = true;
- m_db->block_txn_stop();
m_blocks_txs_check.clear();
return false;
}
@@ -4037,14 +4048,14 @@ bool Blockchain::add_new_block(const block& bl, block_verification_context& bvc)
{
//chain switching or wrong block
bvc.m_added_to_main_chain = false;
- m_db->block_txn_stop();
+ rtxn_guard.stop();
bool r = handle_alternative_block(bl, id, bvc);
m_blocks_txs_check.clear();
return r;
//never relay alternative blocks
}
- m_db->block_txn_stop();
+ rtxn_guard.stop();
return handle_block_to_main_chain(bl, id, bvc);
}
//------------------------------------------------------------------
@@ -4158,7 +4169,10 @@ bool Blockchain::cleanup_handle_incoming_blocks(bool force_sync)
try
{
- m_db->batch_stop();
+ if (m_batch_success)
+ m_db->batch_stop();
+ else
+ m_db->batch_abort();
success = true;
}
catch (const std::exception &e)
@@ -4382,6 +4396,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
m_tx_pool.lock();
m_blockchain_lock.lock();
}
+ m_batch_success = true;
const uint64_t height = m_db->height();
if ((height + blocks_entry.size()) < m_blocks_hash_check.size())
@@ -4834,7 +4849,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
-static const char expected_block_hashes_hash[] = "570ce2357b08fadac6058e34f95c5e08323f9325de260d07b091a281a948a7b0";
+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 3588bbd1b..32ed96b5b 100644
--- a/src/cryptonote_core/blockchain.h
+++ b/src/cryptonote_core/blockchain.h
@@ -37,7 +37,6 @@
#include <boost/multi_index/global_fun.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
-#include <boost/circular_buffer.hpp>
#include <atomic>
#include <functional>
#include <unordered_map>
@@ -46,6 +45,7 @@
#include "span.h"
#include "syncobj.h"
#include "string_tools.h"
+#include "rolling_median.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "common/util.h"
#include "cryptonote_protocol/cryptonote_protocol_defs.h"
@@ -394,10 +394,11 @@ namespace cryptonote
* @param hashes the hashes to be returned, return-by-reference
* @param start_height the start height, return-by-reference
* @param current_height the current blockchain height, return-by-reference
+ * @param clip_pruned whether to constrain results to unpruned data
*
* @return true if a block found in common, else false
*/
- bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height) const;
+ bool find_blockchain_supplement(const std::list<crypto::hash>& qblock_ids, std::vector<crypto::hash>& hashes, uint64_t& start_height, uint64_t& current_height, bool clip_pruned) const;
/**
* @brief get recent block hashes for a foreign chain
@@ -1063,7 +1064,7 @@ namespace cryptonote
uint64_t m_long_term_block_weights_window;
uint64_t m_long_term_effective_median_block_weight;
mutable crypto::hash m_long_term_block_weights_cache_tip_hash;
- mutable std::vector<uint64_t> m_long_term_block_weights_cache;
+ mutable epee::misc_utils::rolling_median_t<uint64_t> m_long_term_block_weights_cache_rolling_median;
epee::critical_section m_difficulty_lock;
crypto::hash m_difficulty_for_next_block_top_hash;
@@ -1101,6 +1102,9 @@ namespace cryptonote
uint64_t m_btc_expected_reward;
bool m_btc_valid;
+
+ bool m_batch_success;
+
std::shared_ptr<tools::Notify> m_block_notify;
std::shared_ptr<tools::Notify> m_reorg_notify;
@@ -1313,15 +1317,16 @@ namespace cryptonote
void get_last_n_blocks_weights(std::vector<uint64_t>& weights, size_t count) const;
/**
- * @brief gets recent block long term weights for median calculation
+ * @brief gets block long term weight median
*
- * get the block long term weights of the last <count> blocks, and return by reference <weights>.
+ * get the block long term weight median of <count> blocks starting at <start_height>
*
- * @param weights return-by-reference the list of weights
* @param start_height the block height of the first block to query
* @param count the number of blocks to get weights for
+ *
+ * @return the long term median block weight
*/
- void get_long_term_block_weights(std::vector<uint64_t>& weights, uint64_t start_height, size_t count) const;
+ uint64_t get_long_term_block_weight_median(uint64_t start_height, size_t count) const;
/**
* @brief checks if a transaction is unlocked (its outputs spendable)
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 91dea4982..13426230e 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -674,9 +674,15 @@ namespace cryptonote
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");
+ CHECK_AND_ASSERT_MES(m_blockchain_storage.prune_blockchain(), false, "Failed to prune blockchain");
+ }
+ else
+ {
+ CHECK_AND_ASSERT_MES(m_blockchain_storage.update_blockchain_pruning(), false, "Failed to update blockchain pruning");
+ }
}
return load_state_data();
@@ -1609,6 +1615,9 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_fork_time()
{
+ if (m_nettype == FAKECHAIN)
+ return true;
+
HardFork::State state = m_blockchain_storage.get_hard_fork_state();
const el::Level level = el::Level::Warning;
switch (state) {
@@ -1824,7 +1833,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::check_block_rate()
{
- if (m_offline || m_target_blockchain_height > get_current_blockchain_height())
+ if (m_offline || m_nettype == FAKECHAIN || m_target_blockchain_height > get_current_blockchain_height())
{
MDEBUG("Not checking block rate, offline or syncing");
return true;
diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp
index c1cbe2acd..49d5a8ccc 100644
--- a/src/cryptonote_core/tx_pool.cpp
+++ b/src/cryptonote_core/tx_pool.cpp
@@ -95,13 +95,17 @@ namespace cryptonote
// the whole prepare/handle/cleanup incoming block sequence.
class LockedTXN {
public:
- LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false) {
+ LockedTXN(Blockchain &b): m_blockchain(b), m_batch(false), m_active(false) {
m_batch = m_blockchain.get_db().batch_start();
+ m_active = true;
}
- ~LockedTXN() { try { if (m_batch) { m_blockchain.get_db().batch_stop(); } } catch (const std::exception &e) { MWARNING("LockedTXN dtor filtering exception: " << e.what()); } }
+ void commit() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_stop(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::commit filtering exception: " << e.what()); } }
+ void abort() { try { if (m_batch && m_active) { m_blockchain.get_db().batch_abort(); m_active = false; } } catch (const std::exception &e) { MWARNING("LockedTXN::abort filtering exception: " << e.what()); } }
+ ~LockedTXN() { abort(); }
private:
Blockchain &m_blockchain;
bool m_batch;
+ bool m_active;
};
}
//---------------------------------------------------------------------------------
@@ -255,6 +259,7 @@ namespace cryptonote
if (!insert_key_images(tx, id, kept_by_block))
return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
+ lock.commit();
}
catch (const std::exception &e)
{
@@ -299,6 +304,7 @@ namespace cryptonote
if (!insert_key_images(tx, id, kept_by_block))
return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
+ lock.commit();
}
catch (const std::exception &e)
{
@@ -398,6 +404,7 @@ namespace cryptonote
return;
}
}
+ lock.commit();
if (changed)
++m_cookie;
if (m_txpool_weight > bytes)
@@ -494,6 +501,7 @@ namespace cryptonote
m_blockchain.remove_txpool_tx(id);
m_txpool_weight -= tx_weight;
remove_transaction_keyimages(tx, id);
+ lock.commit();
}
catch (const std::exception &e)
{
@@ -578,6 +586,7 @@ namespace cryptonote
// ignore error
}
}
+ lock.commit();
++m_cookie;
}
return true;
@@ -641,6 +650,7 @@ namespace cryptonote
// continue
}
}
+ lock.commit();
}
//---------------------------------------------------------------------------------
size_t tx_memory_pool::get_transactions_count(bool include_unrelayed_txes) const
@@ -1119,6 +1129,7 @@ namespace cryptonote
}
}
}
+ lock.commit();
if (changed)
++m_cookie;
}
@@ -1271,6 +1282,7 @@ namespace cryptonote
append_key_images(k_images, tx);
LOG_PRINT_L2(" added, new block weight " << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase));
}
+ lock.commit();
expected_reward = best_coinbase;
LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, weight "
@@ -1336,6 +1348,7 @@ namespace cryptonote
// continue
}
}
+ lock.commit();
}
if (n_removed > 0)
++m_cookie;
@@ -1395,6 +1408,7 @@ namespace cryptonote
// ignore error
}
}
+ lock.commit();
}
m_cookie = 0;
diff --git a/src/cryptonote_core/tx_sanity_check.cpp b/src/cryptonote_core/tx_sanity_check.cpp
new file mode 100644
index 000000000..36be91f2c
--- /dev/null
+++ b/src/cryptonote_core/tx_sanity_check.cpp
@@ -0,0 +1,100 @@
+// 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 <stdint.h>
+#include <vector>
+#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "cryptonote_basic/cryptonote_format_utils.h"
+#include "blockchain.h"
+#include "tx_sanity_check.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "verify"
+
+namespace cryptonote
+{
+
+bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob)
+{
+ cryptonote::transaction tx;
+
+ if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx))
+ {
+ MERROR("Failed to parse transaction");
+ return false;
+ }
+
+ if (cryptonote::is_coinbase(tx))
+ {
+ MERROR("Transaction is coinbase");
+ return false;
+ }
+ std::set<uint64_t> rct_indices;
+ size_t n_indices = 0;
+
+ for (const auto &txin : tx.vin)
+ {
+ if (txin.type() != typeid(cryptonote::txin_to_key))
+ continue;
+ const cryptonote::txin_to_key &in_to_key = boost::get<cryptonote::txin_to_key>(txin);
+ if (in_to_key.amount != 0)
+ continue;
+ const std::vector<uint64_t> absolute = cryptonote::relative_output_offsets_to_absolute(in_to_key.key_offsets);
+ for (uint64_t offset: absolute)
+ rct_indices.insert(offset);
+ n_indices += in_to_key.key_offsets.size();
+ }
+
+ if (n_indices <= 10)
+ {
+ MERROR("n_indices is only " << n_indices);
+ return true;
+ }
+
+ uint64_t n_available = blockchain.get_num_mature_outputs(0);
+ if (n_available < 10000)
+ return true;
+
+ if (rct_indices.size() < n_indices * 8 / 10)
+ {
+ MERROR("unique indices is only " << rct_indices.size() << "/" << n_indices);
+ return false;
+ }
+
+ std::vector<uint64_t> offsets(rct_indices.begin(), rct_indices.end());
+ uint64_t median = epee::misc_utils::median(offsets);
+ if (median < n_available * 6 / 10)
+ {
+ MERROR("median is " << median << "/" << n_available);
+ return false;
+ }
+
+ return true;
+}
+
+}
diff --git a/src/cryptonote_core/tx_sanity_check.h b/src/cryptonote_core/tx_sanity_check.h
new file mode 100644
index 000000000..c12d1b0b1
--- /dev/null
+++ b/src/cryptonote_core/tx_sanity_check.h
@@ -0,0 +1,36 @@
+// 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 "cryptonote_basic/blobdatatype.h"
+
+namespace cryptonote
+{
+ class Blockchain;
+
+ bool tx_sanity_check(Blockchain &blockchain, const cryptonote::blobdata &tx_blob);
+}
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h
index 0927b5d7f..dcc5ec6ed 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h
@@ -52,6 +52,7 @@ PUSH_WARNINGS
DISABLE_VS_WARNINGS(4355)
#define LOCALHOST_INT 2130706433
+#define CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT 500
namespace cryptonote
{
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index b7a50783a..03d04a074 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -341,6 +341,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();
@@ -410,8 +415,8 @@ namespace cryptonote
m_core.get_blockchain_top(hshd.current_height, hshd.top_id);
hshd.top_version = m_core.get_ideal_hard_fork_version(hshd.current_height);
difficulty_type wide_cumulative_difficulty = m_core.get_block_cumulative_difficulty(hshd.current_height);
- hshd.cumulative_difficulty = (wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>();
- hshd.cumulative_difficulty_top64 = (wide_cumulative_difficulty >> 64).convert_to<uint64_t>();
+ hshd.cumulative_difficulty = (wide_cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
+ hshd.cumulative_difficulty_top64 = ((wide_cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
hshd.current_height +=1;
hshd.pruning_seed = m_core.get_blockchain_pruning_seed();
return true;
@@ -809,12 +814,27 @@ namespace cryptonote
NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response;
fluffy_response.b.block = t_serializable_object_to_blob(b);
fluffy_response.current_blockchain_height = arg.current_blockchain_height;
+ std::vector<bool> seen(b.tx_hashes.size(), false);
for(auto& tx_idx: arg.missing_tx_indices)
{
if(tx_idx < b.tx_hashes.size())
{
MDEBUG(" tx " << b.tx_hashes[tx_idx]);
+ if (seen[tx_idx])
+ {
+ LOG_ERROR_CCONTEXT
+ (
+ "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX"
+ << ", request is asking for duplicate tx "
+ << ", tx index = " << tx_idx << ", block tx count " << b.tx_hashes.size()
+ << ", block_height = " << arg.current_blockchain_height
+ << ", dropping connection"
+ );
+ drop_connection(context, true, false);
+ return 1;
+ }
txids.push_back(b.tx_hashes[tx_idx]);
+ seen[tx_idx] = true;
}
else
{
@@ -914,6 +934,17 @@ namespace cryptonote
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)
+ {
+ LOG_ERROR_CCONTEXT(
+ "Requested objects count is too big ("
+ << arg.blocks.size() + arg.txs.size() << ") expected not more then "
+ << CURRENCY_PROTOCOL_MAX_OBJECT_REQUEST_COUNT);
+ drop_connection(context, false, false);
+ return 1;
+ }
+
NOTIFY_RESPONSE_GET_OBJECTS::request rsp;
if(!m_core.handle_get_objects(arg, rsp, context))
{
@@ -2366,6 +2397,8 @@ skip:
{
MINFO("Target height decreasing from " << previous_target << " to " << target);
m_core.set_target_blockchain_height(target);
+ if (target == 0 && context.m_state > cryptonote_connection_context::state_before_handshake && !m_stopping)
+ MCWARNING("global", "monerod is now disconnected from the network");
}
m_block_queue.flush_spans(context.m_connection_id, false);
diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp
index 17b945c9a..ed799096b 100644
--- a/src/daemon/command_parser_executor.cpp
+++ b/src/daemon/command_parser_executor.cpp
@@ -40,10 +40,11 @@ t_command_parser_executor::t_command_parser_executor(
uint32_t ip
, uint16_t port
, const boost::optional<tools::login>& login
+ , const epee::net_utils::ssl_options_t& ssl_options
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server
)
- : m_executor(ip, port, login, is_rpc, rpc_server)
+ : m_executor(ip, port, login, ssl_options, is_rpc, rpc_server)
{}
bool t_command_parser_executor::print_peer_list(const std::vector<std::string>& args)
@@ -595,6 +596,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;
diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h
index 098018642..aeaf4d254 100644
--- a/src/daemon/command_parser_executor.h
+++ b/src/daemon/command_parser_executor.h
@@ -40,6 +40,7 @@
#include "daemon/rpc_command_executor.h"
#include "common/common_fwd.h"
+#include "net/net_fwd.h"
#include "rpc/core_rpc_server.h"
namespace daemonize {
@@ -53,6 +54,7 @@ public:
uint32_t ip
, uint16_t port
, const boost::optional<tools::login>& login
+ , const epee::net_utils::ssl_options_t& ssl_options
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server = NULL
);
@@ -125,6 +127,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 69ad6ff10..f0b4b4ba0 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -43,10 +43,11 @@ t_command_server::t_command_server(
uint32_t ip
, uint16_t port
, const boost::optional<tools::login>& login
+ , const epee::net_utils::ssl_options_t& ssl_options
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server
)
- : m_parser(ip, port, login, is_rpc, rpc_server)
+ : m_parser(ip, port, login, ssl_options, is_rpc, rpc_server)
, m_command_lookup()
, m_is_rpc(is_rpc)
{
@@ -242,10 +243,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/command_server.h b/src/daemon/command_server.h
index c8e77f551..da532223e 100644
--- a/src/daemon/command_server.h
+++ b/src/daemon/command_server.h
@@ -43,6 +43,7 @@ Passing RPC commands:
#include "common/common_fwd.h"
#include "console_handler.h"
#include "daemon/command_parser_executor.h"
+#include "net/net_fwd.h"
namespace daemonize {
@@ -57,6 +58,7 @@ public:
uint32_t ip
, uint16_t port
, const boost::optional<tools::login>& login
+ , const epee::net_utils::ssl_options_t& ssl_options
, bool is_rpc = true
, cryptonote::core_rpc_server* rpc_server = NULL
);
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index 531c080de..5084b6283 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -45,6 +45,7 @@
#include "daemon/command_server.h"
#include "daemon/command_server.h"
#include "daemon/command_line_args.h"
+#include "net/net_ssl.h"
#include "version.h"
using namespace epee;
@@ -163,7 +164,7 @@ bool t_daemon::run(bool interactive)
if (interactive && mp_internals->rpcs.size())
{
// The first three variables are not used when the fourth is false
- rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpcs.front()->get_server()));
+ rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_disabled, false, mp_internals->rpcs.front()->get_server()));
rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
}
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index c3ac24b70..690d4d60e 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -257,7 +257,8 @@ int main(int argc, char const * argv[])
bf::path log_file_path {data_dir / std::string(CRYPTONOTE_NAME ".log")};
if (!command_line::is_arg_defaulted(vm, daemon_args::arg_log_file))
log_file_path = command_line::get_arg(vm, daemon_args::arg_log_file);
- log_file_path = bf::absolute(log_file_path, relative_path_base);
+ if (!log_file_path.has_parent_path())
+ log_file_path = bf::absolute(log_file_path, relative_path_base);
mlog_configure(log_file_path.string(), true, command_line::get_arg(vm, daemon_args::arg_max_log_file_size), command_line::get_arg(vm, daemon_args::arg_max_log_files));
// Set log level
@@ -323,7 +324,11 @@ int main(int argc, char const * argv[])
}
}
- daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, std::move(login)};
+ auto ssl_options = cryptonote::rpc_args::process_ssl(vm, true);
+ if (!ssl_options)
+ return 1;
+
+ daemonize::t_command_server rpc_commands{rpc_ip, rpc_port, std::move(login), std::move(*ssl_options)};
if (rpc_commands.process_command_vec(command))
{
return 0;
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 5901be662..bf4b3b09e 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -127,6 +127,7 @@ t_rpc_command_executor::t_rpc_command_executor(
uint32_t ip
, uint16_t port
, const boost::optional<tools::login>& login
+ , const epee::net_utils::ssl_options_t& ssl_options
, bool is_rpc
, cryptonote::core_rpc_server* rpc_server
)
@@ -137,7 +138,7 @@ t_rpc_command_executor::t_rpc_command_executor(
boost::optional<epee::net_utils::http::login> http_login{};
if (login)
http_login.emplace(login->username, login->password.password());
- m_rpc_client = new tools::t_rpc_client(ip, port, std::move(http_login));
+ m_rpc_client = new tools::t_rpc_client(ip, port, std::move(http_login), ssl_options);
}
else
{
@@ -553,7 +554,7 @@ bool t_rpc_command_executor::mining_status() {
if (!mining_busy && mres.active && mres.speed > 0 && mres.block_target > 0 && mres.difficulty > 0)
{
- double ratio = mres.speed * mres.block_target / mres.difficulty;
+ double ratio = mres.speed * mres.block_target / (double)mres.difficulty;
uint64_t daily = 86400ull / mres.block_target * mres.block_reward * ratio;
uint64_t monthly = 86400ull / mres.block_target * 30.5 * mres.block_reward * ratio;
uint64_t yearly = 86400ull / mres.block_target * 356 * mres.block_reward * ratio;
@@ -722,7 +723,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
tools::msg_writer() << "" << std::endl;
tools::msg_writer()
<< "height: " << header.height << ", timestamp: " << header.timestamp << " (" << tools::get_human_readable_timestamp(header.timestamp) << ")"
- << ", size: " << header.block_size << ", weight: " << header.block_weight << ", transactions: " << header.num_txes << std::endl
+ << ", size: " << header.block_size << ", weight: " << header.block_weight << " (long term " << header.long_term_weight << "), transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
<< "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
@@ -1640,14 +1641,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;
@@ -1655,11 +1656,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);
@@ -1683,7 +1681,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;
@@ -1691,11 +1689,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);
@@ -1719,6 +1714,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;
@@ -2205,7 +2233,7 @@ bool t_rpc_command_executor::prune_blockchain()
}
}
- tools::success_msg_writer() << "Blockchain pruned: seed " << epee::string_tools::to_string_hex(res.pruning_seed);
+ tools::success_msg_writer() << "Blockchain pruned";
return true;
}
diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h
index 3c2686b3f..61c25736d 100644
--- a/src/daemon/rpc_command_executor.h
+++ b/src/daemon/rpc_command_executor.h
@@ -43,6 +43,7 @@
#include "common/common_fwd.h"
#include "common/rpc_client.h"
#include "cryptonote_basic/cryptonote_basic.h"
+#include "net/net_fwd.h"
#include "rpc/core_rpc_server.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
@@ -61,6 +62,7 @@ public:
uint32_t ip
, uint16_t port
, const boost::optional<tools::login>& user
+ , const epee::net_utils::ssl_options_t& ssl_options
, bool is_rpc = true
, cryptonote::core_rpc_server* rpc_server = NULL
);
@@ -135,9 +137,11 @@ public:
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);
diff --git a/src/daemonizer/windows_daemonizer.inl b/src/daemonizer/windows_daemonizer.inl
index 701c098f6..c6cd474fd 100644
--- a/src/daemonizer/windows_daemonizer.inl
+++ b/src/daemonizer/windows_daemonizer.inl
@@ -61,6 +61,10 @@ namespace daemonizer
"run-as-service"
, "Hidden -- true if running as windows service"
};
+ const command_line::arg_descriptor<bool> arg_non_interactive = {
+ "non-interactive"
+ , "Run non-interactive"
+ };
std::string get_argument_string(int argc, char const * argv[])
{
@@ -83,6 +87,7 @@ namespace daemonizer
command_line::add_arg(normal_options, arg_start_service);
command_line::add_arg(normal_options, arg_stop_service);
command_line::add_arg(hidden_options, arg_is_service);
+ command_line::add_arg(hidden_options, arg_non_interactive);
}
inline boost::filesystem::path get_default_data_dir()
@@ -177,7 +182,10 @@ namespace daemonizer
else // interactive
{
//LOG_PRINT_L0("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL);
- return executor.run_interactive(vm);
+ if (command_line::has_arg(vm, arg_non_interactive))
+ return executor.run_non_interactive(vm);
+ else
+ return executor.run_interactive(vm);
}
return false;
diff --git a/src/device/device_ledger.cpp b/src/device/device_ledger.cpp
index 200370564..eba633da8 100644
--- a/src/device/device_ledger.cpp
+++ b/src/device/device_ledger.cpp
@@ -90,6 +90,20 @@ namespace hw {
AKout = keys.AKout;
}
+ ABPkeys &ABPkeys::operator=(const ABPkeys& keys) {
+ if (&keys == this)
+ return *this;
+ Aout = keys.Aout;
+ Bout = keys.Bout;
+ is_subaddress = keys.is_subaddress;
+ is_change_address = keys.is_change_address;
+ additional_key = keys.additional_key;
+ index = keys.index;
+ Pout = keys.Pout;
+ AKout = keys.AKout;
+ return *this;
+ }
+
bool Keymap::find(const rct::key& P, ABPkeys& keys) const {
size_t sz = ABP.size();
for (size_t i=0; i<sz; i++) {
diff --git a/src/device/device_ledger.hpp b/src/device/device_ledger.hpp
index d4d98ce4a..fe9028733 100644
--- a/src/device/device_ledger.hpp
+++ b/src/device/device_ledger.hpp
@@ -77,6 +77,7 @@ namespace hw {
ABPkeys(const rct::key& A, const rct::key& B, const bool is_subaddr, bool is_subaddress, bool is_change_address, size_t index, const rct::key& P,const rct::key& AK);
ABPkeys(const ABPkeys& keys) ;
ABPkeys() {index=0;is_subaddress=false;is_subaddress=false;is_change_address=false;}
+ ABPkeys &operator=(const ABPkeys &keys);
};
class Keymap {
diff --git a/src/device_trezor/device_trezor_base.cpp b/src/device_trezor/device_trezor_base.cpp
index 58abde1d1..b7adf433d 100644
--- a/src/device_trezor/device_trezor_base.cpp
+++ b/src/device_trezor/device_trezor_base.cpp
@@ -115,10 +115,14 @@ namespace trezor {
MDEBUG("Enumerating Trezor devices...");
enumerate(trans);
+ sort_transports_by_env(trans);
- MDEBUG("Enumeration yielded " << trans.size() << " devices");
+ MDEBUG("Enumeration yielded " << trans.size() << " Trezor devices");
for (auto &cur : trans) {
MDEBUG(" device: " << *(cur.get()));
+ }
+
+ for (auto &cur : trans) {
std::string cur_path = cur->get_path();
if (boost::starts_with(cur_path, this->name)) {
MDEBUG("Device Match: " << cur_path);
@@ -412,10 +416,11 @@ namespace trezor {
CHECK_AND_ASSERT_THROW_MES(msg, "Empty message");
MDEBUG("on_button_request, code: " << msg->code());
+ TREZOR_CALLBACK(on_button_request, msg->code());
+
messages::common::ButtonAck ack;
write_raw(&ack);
- TREZOR_CALLBACK(on_button_request, msg->code());
resp = read_raw();
}
diff --git a/src/device_trezor/trezor/transport.cpp b/src/device_trezor/trezor/transport.cpp
index dd9b0b52f..59b281f13 100644
--- a/src/device_trezor/trezor/transport.cpp
+++ b/src/device_trezor/trezor/transport.cpp
@@ -31,11 +31,13 @@
#include <libusb.h>
#endif
+#include <algorithm>
#include <boost/endian/conversion.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/format.hpp>
+#include "common/apply_permutation.h"
#include "transport.hpp"
#include "messages/messages-common.pb.h"
@@ -95,6 +97,47 @@ namespace trezor{
return patch | (((uint64_t)minor) << bits_2) | (((uint64_t)major) << (bits_1 + bits_2));
}
+ typedef struct {
+ uint16_t trezor_type;
+ uint16_t id_vendor;
+ uint16_t id_product;
+ } trezor_usb_desc_t;
+
+ static trezor_usb_desc_t TREZOR_DESC_T1 = {1, 0x534C, 0x0001};
+ static trezor_usb_desc_t TREZOR_DESC_T2 = {2, 0x1209, 0x53C1};
+ static trezor_usb_desc_t TREZOR_DESC_T2_BL = {3, 0x1209, 0x53C0};
+
+ static trezor_usb_desc_t TREZOR_DESCS[] = {
+ TREZOR_DESC_T1,
+ TREZOR_DESC_T2,
+ TREZOR_DESC_T2_BL,
+ };
+
+ static size_t TREZOR_DESCS_LEN = sizeof(TREZOR_DESCS)/sizeof(TREZOR_DESCS[0]);
+
+ static ssize_t get_device_idx(uint16_t id_vendor, uint16_t id_product){
+ for(size_t i = 0; i < TREZOR_DESCS_LEN; ++i){
+ if (TREZOR_DESCS[i].id_vendor == id_vendor && TREZOR_DESCS[i].id_product == id_product){
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ static bool is_device_supported(ssize_t device_idx){
+ CHECK_AND_ASSERT_THROW_MES(device_idx < (ssize_t)TREZOR_DESCS_LEN, "Device desc idx too big");
+ if (device_idx < 0){
+ return false;
+ }
+
+#ifdef TREZOR_1_SUPPORTED
+ return true;
+#else
+ return TREZOR_DESCS[device_idx].trezor_type != 1;
+#endif
+ }
+
//
// Helpers
//
@@ -312,6 +355,24 @@ namespace trezor{
for(rapidjson::Value::ConstValueIterator itr = bridge_res.Begin(); itr != bridge_res.End(); ++itr){
auto element = itr->GetObject();
auto t = std::make_shared<BridgeTransport>(boost::make_optional(json_get_string(element["path"])));
+
+ auto itr_vendor = element.FindMember("vendor");
+ auto itr_product = element.FindMember("product");
+ if (itr_vendor != element.MemberEnd() && itr_product != element.MemberEnd()
+ && itr_vendor->value.IsNumber() && itr_product->value.IsNumber()){
+ try {
+ const auto id_vendor = (uint16_t) itr_vendor->value.GetUint64();
+ const auto id_product = (uint16_t) itr_product->value.GetUint64();
+ const auto device_idx = get_device_idx(id_vendor, id_product);
+ if (!is_device_supported(device_idx)){
+ MDEBUG("Device with idx " << device_idx << " is not supported. Vendor: " << id_vendor << ", product: " << id_product);
+ continue;
+ }
+ } catch(const std::exception &e){
+ MERROR("Could not detect vendor & product: " << e.what());
+ }
+ }
+
t->m_device_info.emplace();
t->m_device_info->CopyFrom(*itr, t->m_device_info->GetAllocator());
res.push_back(t);
@@ -710,24 +771,20 @@ namespace trezor{
#ifdef WITH_DEVICE_TREZOR_WEBUSB
static bool is_trezor1(libusb_device_descriptor * info){
- return info->idVendor == 0x534C && info->idProduct == 0x0001;
+ return info->idVendor == TREZOR_DESC_T1.id_vendor && info->idProduct == TREZOR_DESC_T1.id_product;
}
static bool is_trezor2(libusb_device_descriptor * info){
- return info->idVendor == 0x1209 && info->idProduct == 0x53C1;
+ return info->idVendor == TREZOR_DESC_T2.id_vendor && info->idProduct == TREZOR_DESC_T2.id_product;
}
static bool is_trezor2_bl(libusb_device_descriptor * info){
- return info->idVendor == 0x1209 && info->idProduct == 0x53C0;
+ return info->idVendor == TREZOR_DESC_T2_BL.id_vendor && info->idProduct == TREZOR_DESC_T2_BL.id_product;
}
- static uint8_t get_trezor_dev_mask(libusb_device_descriptor * info){
- uint8_t mask = 0;
+ static ssize_t get_trezor_dev_id(libusb_device_descriptor *info){
CHECK_AND_ASSERT_THROW_MES(info, "Empty device descriptor");
- mask |= is_trezor1(info) ? 1 : 0;
- mask |= is_trezor2(info) ? 2 : 0;
- mask |= is_trezor2_bl(info) ? 4 : 0;
- return mask;
+ return get_device_idx(info->idVendor, info->idProduct);
}
static void set_libusb_log(libusb_context *ctx){
@@ -844,12 +901,12 @@ namespace trezor{
continue;
}
- const auto trezor_mask = get_trezor_dev_mask(&desc);
- if (!trezor_mask){
+ const auto trezor_dev_idx = get_trezor_dev_id(&desc);
+ if (!is_device_supported(trezor_dev_idx)){
continue;
}
- MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct << " mask " << (int)trezor_mask);
+ MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct << " dev_idx " << (int)trezor_dev_idx);
auto t = std::make_shared<WebUsbTransport>(boost::make_optional(&desc));
t->m_bus_id = libusb_get_bus_number(devs[i]);
@@ -909,8 +966,8 @@ namespace trezor{
continue;
}
- const auto trezor_mask = get_trezor_dev_mask(&desc);
- if (!trezor_mask) {
+ const auto trezor_dev_idx = get_trezor_dev_id(&desc);
+ if (!is_device_supported(trezor_dev_idx)){
continue;
}
@@ -921,7 +978,7 @@ namespace trezor{
get_libusb_ports(devs[i], path);
MTRACE("Found Trezor device: " << desc.idVendor << ":" << desc.idProduct
- << ", mask: " << (int)trezor_mask
+ << ", dev_idx: " << (int)trezor_dev_idx
<< ". path: " << get_usb_path(bus_id, path));
if (bus_id == m_bus_id && path == m_port_numbers) {
@@ -1110,6 +1167,39 @@ namespace trezor{
#endif
}
+ void sort_transports_by_env(t_transport_vect & res){
+ const char *env_trezor_path = getenv("TREZOR_PATH");
+ if (!env_trezor_path){
+ return;
+ }
+
+ // Sort transports by the longest matching prefix with TREZOR_PATH
+ std::string trezor_path(env_trezor_path);
+ std::vector<size_t> match_idx(res.size());
+ std::vector<size_t> path_permutation(res.size());
+
+ for(size_t i = 0; i < res.size(); ++i){
+ auto cpath = res[i]->get_path();
+ std::string * s1 = &trezor_path;
+ std::string * s2 = &cpath;
+
+ // first has to be shorter in std::mismatch(). Returns first non-matching iterators.
+ if (s1->size() >= s2->size()){
+ std::swap(s1, s2);
+ }
+
+ const auto mism = std::mismatch(s1->begin(), s1->end(), s2->begin());
+ match_idx[i] = mism.first - s1->begin();
+ path_permutation[i] = i;
+ }
+
+ std::sort(path_permutation.begin(), path_permutation.end(), [&](const size_t i0, const size_t i1) {
+ return match_idx[i0] > match_idx[i1];
+ });
+
+ tools::apply_permutation(path_permutation, res);
+ }
+
std::shared_ptr<Transport> transport(const std::string & path){
if (boost::starts_with(path, BridgeTransport::PATH_PREFIX)){
return std::make_shared<BridgeTransport>(path.substr(strlen(BridgeTransport::PATH_PREFIX)));
diff --git a/src/device_trezor/trezor/transport.hpp b/src/device_trezor/trezor/transport.hpp
index cde862547..affd91553 100644
--- a/src/device_trezor/trezor/transport.hpp
+++ b/src/device_trezor/trezor/transport.hpp
@@ -303,6 +303,11 @@ namespace trezor {
void enumerate(t_transport_vect & res);
/**
+ * Sorts found transports by TREZOR_PATH environment variable.
+ */
+ void sort_transports_by_env(t_transport_vect & res);
+
+ /**
* Transforms path to the transport
*/
std::shared_ptr<Transport> transport(const std::string & path);
diff --git a/src/lmdb/error.cpp b/src/lmdb/error.cpp
index 359677064..91479521e 100644
--- a/src/lmdb/error.cpp
+++ b/src/lmdb/error.cpp
@@ -55,7 +55,7 @@ namespace {
break; // map to nothing generic
case MDB_PAGE_NOTFOUND:
case MDB_CORRUPTED:
- return std::errc::state_not_recoverable;
+ return std::errc::bad_address;
case MDB_PANIC:
case MDB_VERSION_MISMATCH:
case MDB_INVALID:
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h
index 653314b04..7d2599e9a 100644
--- a/src/mnemonics/language_base.h
+++ b/src/mnemonics/language_base.h
@@ -129,7 +129,7 @@ namespace Language
case 1: *wptr++ = cp; break;
case 2: *wptr++ = 0xc0 | (cp >> 6); *wptr++ = 0x80 | (cp & 0x3f); break;
case 3: *wptr++ = 0xe0 | (cp >> 12); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
- case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr += 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
+ case 4: *wptr++ = 0xf0 | (cp >> 18); *wptr++ = 0x80 | ((cp >> 12) & 0x3f); *wptr++ = 0x80 | ((cp >> 6) & 0x3f); *wptr++ = 0x80 | (cp & 0x3f); break;
default: throw std::runtime_error("Invalid UTF-8");
}
*wptr = 0;
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..d93d7d352 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -58,4 +58,27 @@ namespace net
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..9f0d66ea6 100644
--- a/src/net/parse.h
+++ b/src/net/parse.h
@@ -50,5 +50,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.h b/src/p2p/net_node.h
index 42bb3b061..9ee5ce0de 100644
--- a/src/p2p/net_node.h
+++ b/src/p2p/net_node.h
@@ -248,7 +248,11 @@ namespace nodetool
void change_max_in_public_peers(size_t count);
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 +323,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(
@@ -461,8 +465,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;
diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl
index 7d13b3216..ae51a63b2 100644
--- a/src/p2p/net_node.inl
+++ b/src/p2p/net_node.inl
@@ -155,19 +155,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>
@@ -176,8 +212,15 @@ namespace nodetool
if(!addr.is_blockable())
return false;
+ const time_t now = time(nullptr);
+
CRITICAL_REGION_LOCAL(m_blocked_hosts_lock);
- m_blocked_hosts[addr.host_str()] = time(nullptr) + seconds;
+ 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_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
@@ -207,7 +250,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);
@@ -216,6 +259,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())
@@ -937,7 +1032,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)
@@ -1083,6 +1181,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;
}
@@ -1142,7 +1241,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;
}
@@ -1219,19 +1318,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);
@@ -1948,7 +2081,7 @@ namespace nodetool
const epee::net_utils::zone zone_type = context.m_remote_address.get_zone();
network_zone& zone = m_network_zones.at(zone_type);
- zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new);
+ zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true);
m_payload_handler.get_payload_sync_data(rsp.payload_data);
/* Tor/I2P nodes receiving connections via forwarding (from tor/i2p daemon)
@@ -2051,7 +2184,7 @@ namespace nodetool
});
//fill response
- zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new);
+ zone.m_peerlist.get_peerlist_head(rsp.local_peerlist_new, true);
get_local_node_data(rsp.node_data, zone);
m_payload_handler.get_payload_sync_data(rsp.payload_data);
LOG_DEBUG_CC(context, "COMMAND_HANDSHAKE");
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 52814af94..883997fd6 100644
--- a/src/p2p/net_peerlist.h
+++ b/src/p2p/net_peerlist.h
@@ -102,7 +102,7 @@ namespace nodetool
size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();}
size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();}
bool merge_peerlist(const std::vector<peerlist_entry>& outer_bs);
- bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
+ bool get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE);
void get_peerlist(std::vector<peerlist_entry>& pl_gray, std::vector<peerlist_entry>& pl_white);
void get_peerlist(peerlist_types& peers);
bool get_white_peer_by_index(peerlist_entry& p, size_t i);
@@ -263,23 +263,40 @@ namespace nodetool
}
//--------------------------------------------------------------------------------------------------
inline
- bool peerlist_manager::get_peerlist_head(std::vector<peerlist_entry>& bs_head, uint32_t depth)
+ bool peerlist_manager::get_peerlist_head(std::vector<peerlist_entry>& bs_head, bool anonymize, uint32_t depth)
{
-
CRITICAL_REGION_LOCAL(m_peerlist_lock);
peers_indexed::index<by_time>::type& by_time_index=m_peers_white.get<by_time>();
uint32_t cnt = 0;
- bs_head.reserve(depth);
+
+ // picks a random set of peers within the first 120%, rather than a set of the first 100%.
+ // The intent is that if someone asks twice, they can't easily tell:
+ // - this address was not in the first list, but is in the second, so the only way this can be
+ // is if its last_seen was recently reset, so this means the target node recently had a new
+ // connection to that address
+ // - this address was in the first list, and not in the second, which means either the address
+ // was moved to the gray list (if it's not accessibe, which the attacker can check if
+ // the address accepts incoming connections) or it was the oldest to still fit in the 250 items,
+ // so its last_seen is old.
+ const uint32_t pick_depth = anonymize ? depth + depth / 5 : depth;
+ bs_head.reserve(pick_depth);
for(const peers_indexed::value_type& vl: boost::adaptors::reverse(by_time_index))
{
- if(!vl.last_seen)
- continue;
-
- if(cnt++ >= depth)
+ if(cnt++ >= pick_depth)
break;
bs_head.push_back(vl);
}
+
+ if (anonymize)
+ {
+ std::random_shuffle(bs_head.begin(), bs_head.end());
+ if (bs_head.size() > depth)
+ bs_head.resize(depth);
+ for (auto &e: bs_head)
+ e.last_seen = 0;
+ }
+
return true;
}
//--------------------------------------------------------------------------------------------------
@@ -327,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);
@@ -362,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 40ef2ebcd..32f30adca 100644
--- a/src/p2p/net_peerlist_boost_serialization.h
+++ b/src/p2p/net_peerlist_boost_serialization.h
@@ -134,10 +134,11 @@ namespace boost
a & port;
a & length;
- if (length > net::tor_address::buffer_size())
+ const size_t buffer_size = net::tor_address::buffer_size();
+ if (length > buffer_size)
MONERO_THROW(net::error::invalid_tor_address, "Tor address too long");
- char host[net::tor_address::buffer_size()] = {0};
+ char host[buffer_size] = {0};
a.load_binary(host, length);
host[sizeof(host) - 1] = 0;
@@ -155,10 +156,11 @@ namespace boost
a & port;
a & length;
- if (length > net::i2p_address::buffer_size())
+ const size_t buffer_size = net::i2p_address::buffer_size();
+ if (length > buffer_size)
MONERO_THROW(net::error::invalid_i2p_address, "i2p address too long");
- char host[net::i2p_address::buffer_size()] = {0};
+ char host[buffer_size] = {0};
a.load_binary(host, length);
host[sizeof(host) - 1] = 0;
diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h
index 59c6099d5..85774fcd5 100644
--- a/src/p2p/p2p_protocol_defs.h
+++ b/src/p2p/p2p_protocol_defs.h
@@ -81,7 +81,8 @@ namespace nodetool
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(adr)
KV_SERIALIZE(id)
- KV_SERIALIZE(last_seen)
+ if (!is_store || this_ref.last_seen != 0)
+ KV_SERIALIZE_OPT(last_seen, (int64_t)0)
KV_SERIALIZE_OPT(pruning_seed, (uint32_t)0)
KV_SERIALIZE_OPT(rpc_port, (uint16_t)0)
END_KV_SERIALIZE_MAP()
@@ -132,7 +133,7 @@ namespace nodetool
ss << pe.id << "\t" << pe.adr.str()
<< " \trpc port " << (pe.rpc_port > 0 ? std::to_string(pe.rpc_port) : "-")
<< " \tpruning seed " << pe.pruning_seed
- << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen)
+ << " \tlast_seen: " << (pe.last_seen == 0 ? std::string("never") : epee::misc_utils::get_time_interval_string(now_time - pe.last_seen))
<< std::endl;
}
return ss.str();
diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc
index e394ef088..6270d4d14 100644
--- a/src/ringct/bulletproofs.cc
+++ b/src/ringct/bulletproofs.cc
@@ -48,12 +48,12 @@ extern "C"
//#define DEBUG_BP
-#if 1
+#if 0
#define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000)
#define PERF_TIMER_STOP_BP(x) PERF_TIMER_STOP(x)
#else
-#define PERF_TIMER_START_BP(x) ((void*)0)
-#define PERF_TIMER_STOP_BP(x) ((void*)0)
+#define PERF_TIMER_START_BP(x) ((void)0)
+#define PERF_TIMER_STOP_BP(x) ((void)0)
#endif
#define STRAUS_SIZE_LIMIT 232
@@ -439,35 +439,35 @@ static epee::span<const rct::key> slice(const rct::keyV &a, size_t start, size_t
static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1)
{
- rct::keyV data;
- data.reserve(3);
- data.push_back(hash_cache);
- data.push_back(mash0);
- data.push_back(mash1);
- return hash_cache = rct::hash_to_scalar(data);
+ rct::key data[3];
+ data[0] = hash_cache;
+ data[1] = mash0;
+ data[2] = mash1;
+ rct::hash_to_scalar(hash_cache, data, sizeof(data));
+ return hash_cache;
}
static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2)
{
- rct::keyV data;
- data.reserve(4);
- data.push_back(hash_cache);
- data.push_back(mash0);
- data.push_back(mash1);
- data.push_back(mash2);
- return hash_cache = rct::hash_to_scalar(data);
+ rct::key data[4];
+ data[0] = hash_cache;
+ data[1] = mash0;
+ data[2] = mash1;
+ data[3] = mash2;
+ rct::hash_to_scalar(hash_cache, data, sizeof(data));
+ return hash_cache;
}
static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2, const rct::key &mash3)
{
- rct::keyV data;
- data.reserve(5);
- data.push_back(hash_cache);
- data.push_back(mash0);
- data.push_back(mash1);
- data.push_back(mash2);
- data.push_back(mash3);
- return hash_cache = rct::hash_to_scalar(data);
+ rct::key data[5];
+ data[0] = hash_cache;
+ data[1] = mash0;
+ data[2] = mash1;
+ data[3] = mash2;
+ data[4] = mash3;
+ rct::hash_to_scalar(hash_cache, data, sizeof(data));
+ return hash_cache;
}
/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */
@@ -825,6 +825,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
size_t inv_offset = 0;
std::vector<rct::key> to_invert;
to_invert.reserve(11 * sizeof(proofs));
+ size_t max_logM = 0;
for (const Bulletproof *p: proofs)
{
const Bulletproof &proof = *p;
@@ -861,6 +862,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
size_t M;
for (pd.logM = 0; (M = 1<<pd.logM) <= maxM && M < proof.V.size(); ++pd.logM);
CHECK_AND_ASSERT_MES(proof.L.size() == 6+pd.logM, false, "Proof is not the expected size");
+ max_logM = std::max(pd.logM, max_logM);
const size_t rounds = pd.logM+logN;
CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds");
@@ -888,7 +890,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
rct::key tmp;
std::vector<MultiexpData> multiexp_data;
- multiexp_data.reserve(nV + (2 * (10/*logM*/ + logN) + 4) * proofs.size() + 2 * maxMN);
+ multiexp_data.reserve(nV + (2 * (max_logM + logN) + 4) * proofs.size() + 2 * maxMN);
multiexp_data.resize(2 * maxMN);
PERF_TIMER_START_BP(VERIFY_line_24_25_invert);
@@ -901,6 +903,8 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
rct::keyV m_z4(maxMN, rct::zero()), m_z5(maxMN, rct::zero());
rct::key m_y0 = rct::zero(), y1 = rct::zero();
int proof_data_index = 0;
+ rct::keyV w_cache;
+ rct::keyV proof8_V, proof8_L, proof8_R;
for (const Bulletproof *p: proofs)
{
const Bulletproof &proof = *p;
@@ -913,9 +917,9 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
const rct::key weight_z = rct::skGen();
// pre-multiply some points by 8
- rct::keyV proof8_V = proof.V; for (rct::key &k: proof8_V) k = rct::scalarmult8(k);
- rct::keyV proof8_L = proof.L; for (rct::key &k: proof8_L) k = rct::scalarmult8(k);
- rct::keyV proof8_R = proof.R; for (rct::key &k: proof8_R) k = rct::scalarmult8(k);
+ proof8_V.resize(proof.V.size()); for (size_t i = 0; i < proof.V.size(); ++i) proof8_V[i] = rct::scalarmult8(proof.V[i]);
+ proof8_L.resize(proof.L.size()); for (size_t i = 0; i < proof.L.size(); ++i) proof8_L[i] = rct::scalarmult8(proof.L[i]);
+ proof8_R.resize(proof.R.size()); for (size_t i = 0; i < proof.R.size(); ++i) proof8_R[i] = rct::scalarmult8(proof.R[i]);
rct::key proof8_T1 = rct::scalarmult8(proof.T1);
rct::key proof8_T2 = rct::scalarmult8(proof.T2);
rct::key proof8_S = rct::scalarmult8(proof.S);
@@ -976,7 +980,7 @@ bool bulletproof_VERIFY(const std::vector<const Bulletproof*> &proofs)
// precalc
PERF_TIMER_START_BP(VERIFY_line_24_25_precalc);
- rct::keyV w_cache(1<<rounds);
+ w_cache.resize(1<<rounds);
w_cache[0] = winv[0];
w_cache[1] = pd.w[0];
for (size_t j = 1; j < rounds; ++j)
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index e877c13ce..ff2a81d43 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -695,6 +695,7 @@ namespace rct {
CHECK_AND_ASSERT_THROW_MES(mixRing[n].size() == inSk.size(), "Bad mixRing size");
}
CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present");
+ CHECK_AND_ASSERT_THROW_MES(inSk.size() < 2, "genRct is not suitable for 2+ rings");
rctSig rv;
rv.type = RCTTypeFull;
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 50d0f4d91..f8729b872 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -191,6 +191,8 @@ namespace rct {
Bulletproof(const rct::keyV &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t):
V(V), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {}
+ bool operator==(const Bulletproof &other) const { return V == other.V && A == other.A && S == other.S && T1 == other.T1 && T2 == other.T2 && taux == other.taux && mu == other.mu && L == other.L && R == other.R && a == other.a && b == other.b && t == other.t; }
+
BEGIN_SERIALIZE_OBJECT()
// Commitments aren't saved, they're restored via outPk
// FIELD(V)
@@ -250,7 +252,7 @@ namespace rct {
{
FIELD(type)
if (type == RCTTypeNull)
- return true;
+ return ar.stream().good();
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2)
return false;
VARINT_FIELD(txnFee)
@@ -310,7 +312,7 @@ namespace rct {
ar.delimit_array();
}
ar.end_array();
- return true;
+ return ar.stream().good();
}
};
struct rctSigPrunable {
@@ -323,7 +325,7 @@ namespace rct {
bool serialize_rctsig_prunable(Archive<W> &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin)
{
if (type == RCTTypeNull)
- return true;
+ return ar.stream().good();
if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2)
return false;
if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2)
@@ -427,7 +429,7 @@ namespace rct {
}
ar.end_array();
}
- return true;
+ return ar.stream().good();
}
};
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index 39a8b4745..cfa1c49d8 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -28,6 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+#include <boost/preprocessor/stringize.hpp>
#include "include_base_utils.h"
#include "string_tools.h"
using namespace epee;
@@ -41,6 +42,7 @@ using namespace epee;
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
+#include "cryptonote_core/tx_sanity_check.h"
#include "misc_language.h"
#include "net/parse.h"
#include "storages/http_abstract_invoke.h"
@@ -57,6 +59,8 @@ using namespace epee;
#define MAX_RESTRICTED_FAKE_OUTS_COUNT 40
#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 5000
+#define OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION (3 * 86400) // 3 days max, the wallet requests 1.8 days
+
namespace
{
void add_reason(std::string &reasons, const char *reason)
@@ -73,9 +77,9 @@ namespace
void store_difficulty(cryptonote::difficulty_type difficulty, uint64_t &sdiff, std::string &swdiff, uint64_t &stop64)
{
- sdiff = (difficulty << 64 >> 64).convert_to<uint64_t>();
+ sdiff = (difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
swdiff = cryptonote::hex(difficulty);
- stop64 = (difficulty >> 64).convert_to<uint64_t>();
+ stop64 = ((difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
}
}
@@ -88,15 +92,9 @@ namespace cryptonote
command_line::add_arg(desc, arg_rpc_bind_port);
command_line::add_arg(desc, arg_rpc_restricted_bind_port);
command_line::add_arg(desc, arg_restricted_rpc);
- command_line::add_arg(desc, arg_rpc_ssl);
- command_line::add_arg(desc, arg_rpc_ssl_private_key);
- command_line::add_arg(desc, arg_rpc_ssl_certificate);
- command_line::add_arg(desc, arg_rpc_ssl_ca_certificates);
- command_line::add_arg(desc, arg_rpc_ssl_allowed_fingerprints);
- command_line::add_arg(desc, arg_rpc_ssl_allow_any_cert);
command_line::add_arg(desc, arg_bootstrap_daemon_address);
command_line::add_arg(desc, arg_bootstrap_daemon_login);
- cryptonote::rpc_args::init_options(desc);
+ cryptonote::rpc_args::init_options(desc, true);
}
//------------------------------------------------------------------------------------------------------------------------------
core_rpc_server::core_rpc_server(
@@ -116,7 +114,7 @@ namespace cryptonote
m_restricted = restricted;
m_net_server.set_threads_prefix("RPC");
- auto rpc_config = cryptonote::rpc_args::process(vm);
+ auto rpc_config = cryptonote::rpc_args::process(vm, true);
if (!rpc_config)
return false;
@@ -149,38 +147,9 @@ namespace cryptonote
if (rpc_config->login)
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
- epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect;
- if (command_line::get_arg(vm, arg_rpc_ssl_allow_any_cert))
- ssl_options.verification = epee::net_utils::ssl_verification_t::none;
- else
- {
- std::string ssl_ca_path = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates);
- const std::vector<std::string> ssl_allowed_fingerprint_strings = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints);
- std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ ssl_allowed_fingerprint_strings.size() };
- std::transform(ssl_allowed_fingerprint_strings.begin(), ssl_allowed_fingerprint_strings.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
-
- if (!ssl_ca_path.empty() || !ssl_allowed_fingerprints.empty())
- ssl_options = epee::net_utils::ssl_options_t{std::move(ssl_allowed_fingerprints), std::move(ssl_ca_path)};
- }
-
- ssl_options.auth = epee::net_utils::ssl_authentication_t{
- command_line::get_arg(vm, arg_rpc_ssl_private_key), command_line::get_arg(vm, arg_rpc_ssl_certificate)
- };
-
- // user specified CA file or fingeprints implies enabled SSL by default
- if (ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, arg_rpc_ssl))
- {
- const std::string ssl = command_line::get_arg(vm, arg_rpc_ssl);
- if (!epee::net_utils::ssl_support_from_string(ssl_options.support, ssl))
- {
- MFATAL("Invalid RPC SSL support: " << ssl);
- return false;
- }
- }
-
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(ssl_options)
+ 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)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -432,17 +401,12 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_HASHES_FAST>(invoke_http_mode::BIN, "/gethashes.bin", req, res, r))
return r;
- NOTIFY_RESPONSE_CHAIN_ENTRY::request resp;
-
- resp.start_height = req.start_height;
- if(!m_core.find_blockchain_supplement(req.block_ids, resp))
+ res.start_height = req.start_height;
+ if(!m_core.get_blockchain_storage().find_blockchain_supplement(req.block_ids, res.m_block_ids, res.start_height, res.current_height, false))
{
res.status = "Failed";
return false;
}
- res.current_height = resp.total_height;
- res.start_height = resp.start_height;
- res.m_block_ids = std::move(resp.m_block_ids);
res.status = CORE_RPC_STATUS_OK;
return true;
@@ -574,7 +538,7 @@ namespace cryptonote
// try the pool for any missing txes
size_t found_in_pool = 0;
std::unordered_set<crypto::hash> pool_tx_hashes;
- std::unordered_map<crypto::hash, bool> double_spend_seen;
+ std::unordered_map<crypto::hash, tx_info> per_tx_pool_tx_info;
if (!missed_txs.empty())
{
std::vector<tx_info> pool_tx_info;
@@ -629,7 +593,7 @@ namespace cryptonote
{
if (ti.id_hash == hash_string)
{
- double_spend_seen.insert(std::make_pair(h, ti.double_spend_seen));
+ per_tx_pool_tx_info.insert(std::make_pair(h, ti));
break;
}
}
@@ -715,14 +679,17 @@ namespace cryptonote
if (e.in_pool)
{
e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
- if (double_spend_seen.find(tx_hash) != double_spend_seen.end())
+ auto it = per_tx_pool_tx_info.find(tx_hash);
+ if (it != per_tx_pool_tx_info.end())
{
- e.double_spend_seen = double_spend_seen[tx_hash];
+ e.double_spend_seen = it->second.double_spend_seen;
+ e.relayed = it->second.relayed;
}
else
{
- MERROR("Failed to determine double spend status for " << tx_hash);
+ MERROR("Failed to determine pool info for " << tx_hash);
e.double_spend_seen = false;
+ e.relayed = false;
}
}
else
@@ -730,6 +697,7 @@ namespace cryptonote
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height);
e.double_spend_seen = false;
+ e.relayed = false;
}
// fill up old style responses too, in case an old wallet asks
@@ -845,6 +813,15 @@ namespace cryptonote
return true;
}
+ if (req.do_sanity_checks && !cryptonote::tx_sanity_check(m_core.get_blockchain_storage(), tx_blob))
+ {
+ res.status = "Failed";
+ res.reason = "Sanity check failed";
+ 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);
if(!m_core.handle_incoming_tx(tx_blob, tvc, false, false, req.do_not_relay) || tvc.m_verifivation_failed)
@@ -931,16 +908,13 @@ namespace cryptonote
return true;
}
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
-
cryptonote::miner &miner= m_core.get_miner();
if (miner.is_mining())
{
res.status = "Already mining";
return true;
}
- if(!miner.start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
+ if(!miner.start(info.address, static_cast<size_t>(req.threads_count), req.do_background_mining, req.ignore_battery))
{
res.status = "Failed, mining not started";
LOG_PRINT_L0(res.status);
@@ -1296,7 +1270,10 @@ namespace cryptonote
LOG_ERROR("Failed to find tx pub key in blockblob");
return false;
}
- res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
+ if (req.reserve_size)
+ res.reserved_offset += sizeof(tx_pub_key) + 2; //2 bytes: tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte)
+ else
+ res.reserved_offset = 0;
if(res.reserved_offset + req.reserve_size > block_blob.size())
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1394,11 +1371,9 @@ namespace cryptonote
submit_req.push_back(boost::value_initialized<std::string>());
res.height = m_core.get_blockchain_storage().get_current_blockchain_height();
- bool r = CORE_RPC_STATUS_OK;
-
for(size_t i = 0; i < req.amount_of_blocks; i++)
{
- r = on_getblocktemplate(template_req, template_res, error_resp, ctx);
+ bool r = on_getblocktemplate(template_req, template_res, error_resp, ctx);
res.status = template_res.status;
template_req.prev_block.clear();
@@ -1797,20 +1772,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;
@@ -1823,13 +1838,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);
@@ -1910,6 +1941,13 @@ namespace cryptonote
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_OUTPUT_HISTOGRAM>(invoke_http_mode::JON_RPC, "get_output_histogram", req, res, r))
return r;
+ const bool restricted = m_restricted && ctx;
+ if (restricted && req.recent_cutoff > 0 && req.recent_cutoff < (uint64_t)time(NULL) - OUTPUT_HISTOGRAM_RECENT_CUTOFF_RESTRICTION)
+ {
+ res.status = "Recent cutoff is too old";
+ return true;
+ }
+
std::map<uint64_t, std::tuple<uint64_t, uint64_t, uint64_t>> histogram;
try
{
@@ -2089,6 +2127,13 @@ namespace cryptonote
bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res, const connection_context *ctx)
{
PERF_TIMER(on_update);
+
+ if (m_core.offline())
+ {
+ res.status = "Daemon is running offline";
+ return true;
+ }
+
static const char software[] = "monero";
#ifdef BUILD_TAG
static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG);
@@ -2299,7 +2344,7 @@ namespace cryptonote
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (uint64_t amount: req.amounts)
{
- auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative);
+ auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, [this](uint64_t height) { return m_core.get_blockchain_storage().get_db().get_block_hash_from_height(height); }, req.cumulative, m_core.get_current_blockchain_height());
if (!data)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -2342,7 +2387,7 @@ namespace cryptonote
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (uint64_t amount: req.amounts)
{
- auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative);
+ auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, [this](uint64_t height) { return m_core.get_blockchain_storage().get_db().get_block_hash_from_height(height); }, req.cumulative, m_core.get_current_blockchain_height());
if (!data)
{
res.status = "Failed to get output distribution";
@@ -2373,6 +2418,7 @@ namespace cryptonote
return false;
}
res.pruning_seed = m_core.get_blockchain_pruning_seed();
+ res.pruned = res.pruning_seed != 0;
}
catch (const std::exception &e)
{
@@ -2413,40 +2459,6 @@ namespace cryptonote
, false
};
- const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl = {
- "rpc-ssl"
- , "Enable SSL on RPC connections: enabled|disabled|autodetect"
- , "autodetect"
- };
-
- const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_private_key = {
- "rpc-ssl-private-key"
- , "Path to a PEM format private key"
- , ""
- };
-
- const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_certificate = {
- "rpc-ssl-certificate"
- , "Path to a PEM format certificate"
- , ""
- };
-
- const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_ssl_ca_certificates = {
- "rpc-ssl-ca-certificates"
- , "Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."
- };
-
- const command_line::arg_descriptor<std::vector<std::string>> core_rpc_server::arg_rpc_ssl_allowed_fingerprints = {
- "rpc-ssl-allowed-fingerprints"
- , "List of certificate fingerprints to allow"
- };
-
- const command_line::arg_descriptor<bool> core_rpc_server::arg_rpc_ssl_allow_any_cert = {
- "rpc-ssl-allow-any-cert"
- , "Allow any peer certificate"
- , false
- };
-
const command_line::arg_descriptor<std::string> core_rpc_server::arg_bootstrap_daemon_address = {
"bootstrap-daemon-address"
, "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced"
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index a42ca2494..266661fb0 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -40,6 +40,9 @@
#include "p2p/net_node.h"
#include "cryptonote_protocol/cryptonote_protocol_handler.h"
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc"
+
// yes, epee doesn't properly use its full namespace when calling its
// functions from macros. *sigh*
using namespace epee;
@@ -151,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)
@@ -217,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);
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index d2aba8d67..a78faf5aa 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -84,7 +84,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 5
+#define CORE_RPC_VERSION_MINOR 7
#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)
@@ -364,6 +364,7 @@ namespace cryptonote
uint64_t block_height;
uint64_t block_timestamp;
std::vector<uint64_t> output_indices;
+ bool relayed;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash)
@@ -374,9 +375,16 @@ namespace cryptonote
KV_SERIALIZE(as_json)
KV_SERIALIZE(in_pool)
KV_SERIALIZE(double_spend_seen)
- KV_SERIALIZE(block_height)
- KV_SERIALIZE(block_timestamp)
- KV_SERIALIZE(output_indices)
+ if (!this_ref.in_pool)
+ {
+ KV_SERIALIZE(block_height)
+ KV_SERIALIZE(block_timestamp)
+ KV_SERIALIZE(output_indices)
+ }
+ else
+ {
+ KV_SERIALIZE(relayed)
+ }
END_KV_SERIALIZE_MAP()
};
@@ -577,10 +585,12 @@ namespace cryptonote
{
std::string tx_as_hex;
bool do_not_relay;
+ bool do_sanity_checks;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_as_hex)
KV_SERIALIZE_OPT(do_not_relay, false)
+ KV_SERIALIZE_OPT(do_sanity_checks, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -599,6 +609,7 @@ namespace cryptonote
bool overspend;
bool fee_too_low;
bool not_rct;
+ bool sanity_check_failed;
bool untrusted;
BEGIN_KV_SERIALIZE_MAP()
@@ -613,6 +624,7 @@ namespace cryptonote
KV_SERIALIZE(overspend)
KV_SERIALIZE(fee_too_low)
KV_SERIALIZE(not_rct)
+ KV_SERIALIZE(sanity_check_failed)
KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP()
};
@@ -1864,6 +1876,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
@@ -2308,11 +2347,13 @@ namespace cryptonote
struct response_t
{
+ bool pruned;
uint32_t pruning_seed;
std::string status;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
+ KV_SERIALIZE(pruned)
KV_SERIALIZE(pruning_seed)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/rpc/daemon_handler.cpp b/src/rpc/daemon_handler.cpp
index 540afe6b9..612b2cab6 100644
--- a/src/rpc/daemon_handler.cpp
+++ b/src/rpc/daemon_handler.cpp
@@ -141,7 +141,7 @@ namespace rpc
auto& chain = m_core.get_blockchain_storage();
- if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height))
+ if (!chain.find_blockchain_supplement(req.known_hashes, res.hashes, res.start_height, res.current_height, false))
{
res.status = Message::STATUS_FAILED;
res.error_details = "Blockchain::find_blockchain_supplement() returned false";
@@ -408,10 +408,7 @@ namespace rpc
return;
}
- boost::thread::attributes attrs;
- attrs.set_stack_size(THREAD_STACK_SIZE);
-
- if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
+ if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), req.do_background_mining, req.ignore_battery))
{
res.error_details = "Failed, mining not started";
LOG_PRINT_L0(res.error_details);
@@ -437,7 +434,7 @@ namespace rpc
auto& chain = m_core.get_blockchain_storage();
res.info.wide_difficulty = chain.get_difficulty_for_next_block();
- res.info.difficulty = (res.info.wide_difficulty << 64 >> 64).convert_to<uint64_t>();
+ res.info.difficulty = (res.info.wide_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
res.info.target = chain.get_difficulty_target();
@@ -459,7 +456,7 @@ namespace rpc
res.info.testnet = m_core.get_nettype() == TESTNET;
res.info.stagenet = m_core.get_nettype() == STAGENET;
res.info.wide_cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1);
- res.info.cumulative_difficulty = (res.info.wide_cumulative_difficulty << 64 >> 64).convert_to<uint64_t>();
+ res.info.cumulative_difficulty = (res.info.wide_cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.info.start_time = (uint64_t)m_core.get_start_time();
@@ -778,7 +775,7 @@ namespace rpc
const uint64_t req_to_height = req.to_height ? req.to_height : (m_core.get_current_blockchain_height() - 1);
for (std::uint64_t amount : req.amounts)
{
- auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, req.cumulative);
+ auto data = rpc::RpcHandler::get_output_distribution([this](uint64_t amount, uint64_t from, uint64_t to, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &base) { return m_core.get_output_distribution(amount, from, to, start_height, distribution, base); }, amount, req.from_height, req_to_height, [this](uint64_t height) { return m_core.get_blockchain_storage().get_db().get_block_hash_from_height(height); }, req.cumulative, m_core.get_current_blockchain_height());
if (!data)
{
res.distributions.clear();
@@ -829,7 +826,7 @@ namespace rpc
}
header.wide_difficulty = m_core.get_blockchain_storage().block_difficulty(header.height);
- header.difficulty = (header.wide_difficulty << 64 >> 64).convert_to<uint64_t>();
+ header.difficulty = (header.wide_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
return true;
}
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index f2be94f51..4479bd1f1 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -33,28 +33,95 @@
#include <boost/bind.hpp>
#include "common/command_line.h"
#include "common/i18n.h"
+#include "hex.h"
namespace cryptonote
{
+ namespace
+ {
+ boost::optional<epee::net_utils::ssl_options_t> do_process_ssl(const boost::program_options::variables_map& vm, const rpc_args::descriptors& arg, const bool any_cert_option)
+ {
+ bool ssl_required = false;
+ epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
+ if (any_cert_option && command_line::get_arg(vm, arg.rpc_ssl_allow_any_cert))
+ ssl_options.verification = epee::net_utils::ssl_verification_t::none;
+ else
+ {
+ std::string ssl_ca_file = command_line::get_arg(vm, arg.rpc_ssl_ca_certificates);
+ const std::vector<std::string> ssl_allowed_fingerprints = command_line::get_arg(vm, arg.rpc_ssl_allowed_fingerprints);
+
+ std::vector<std::vector<uint8_t>> allowed_fingerprints{ ssl_allowed_fingerprints.size() };
+ std::transform(ssl_allowed_fingerprints.begin(), ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector);
+ for (const auto &fpr: allowed_fingerprints)
+ {
+ if (fpr.size() != SSL_FINGERPRINT_SIZE)
+ {
+ MERROR("SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long.");
+ return boost::none;
+ }
+ }
+
+ if (!allowed_fingerprints.empty() || !ssl_ca_file.empty())
+ {
+ ssl_required = true;
+ ssl_options = epee::net_utils::ssl_options_t{
+ std::move(allowed_fingerprints), std::move(ssl_ca_file)
+ };
+
+ if (command_line::get_arg(vm, arg.rpc_ssl_allow_chained))
+ ssl_options.verification = epee::net_utils::ssl_verification_t::user_ca;
+ }
+ }
+
+ // user specified CA file or fingeprints implies enabled SSL by default
+ if (!ssl_required && !epee::net_utils::ssl_support_from_string(ssl_options.support, command_line::get_arg(vm, arg.rpc_ssl)))
+ {
+ MERROR("Invalid argument for " << std::string(arg.rpc_ssl.name));
+ return boost::none;
+ }
+
+ ssl_options.auth = epee::net_utils::ssl_authentication_t{
+ command_line::get_arg(vm, arg.rpc_ssl_private_key), command_line::get_arg(vm, arg.rpc_ssl_certificate)
+ };
+
+ return {std::move(ssl_options)};
+ }
+ } // anonymous
+
rpc_args::descriptors::descriptors()
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify IP to bind RPC server"), "127.0.0.1"})
, rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
, confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
, rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
+ , rpc_ssl({"rpc-ssl", rpc_args::tr("Enable SSL on RPC connections: enabled|disabled|autodetect"), "autodetect"})
+ , rpc_ssl_private_key({"rpc-ssl-private-key", rpc_args::tr("Path to a PEM format private key"), ""})
+ , rpc_ssl_certificate({"rpc-ssl-certificate", rpc_args::tr("Path to a PEM format certificate"), ""})
+ , rpc_ssl_ca_certificates({"rpc-ssl-ca-certificates", rpc_args::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s)."), ""})
+ , rpc_ssl_allowed_fingerprints({"rpc-ssl-allowed-fingerprints", rpc_args::tr("List of certificate fingerprints to allow")})
+ , rpc_ssl_allow_chained({"rpc-ssl-allow-chained", rpc_args::tr("Allow user (via --rpc-ssl-certificates) chain certificates"), false})
+ , rpc_ssl_allow_any_cert({"rpc-ssl-allow-any-cert", rpc_args::tr("Allow any peer certificate"), false})
{}
const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); }
- void rpc_args::init_options(boost::program_options::options_description& desc)
+ void rpc_args::init_options(boost::program_options::options_description& desc, const bool any_cert_option)
{
const descriptors arg{};
command_line::add_arg(desc, arg.rpc_bind_ip);
command_line::add_arg(desc, arg.rpc_login);
command_line::add_arg(desc, arg.confirm_external_bind);
command_line::add_arg(desc, arg.rpc_access_control_origins);
+ command_line::add_arg(desc, arg.rpc_ssl);
+ command_line::add_arg(desc, arg.rpc_ssl_private_key);
+ command_line::add_arg(desc, arg.rpc_ssl_certificate);
+ command_line::add_arg(desc, arg.rpc_ssl_ca_certificates);
+ command_line::add_arg(desc, arg.rpc_ssl_allowed_fingerprints);
+ command_line::add_arg(desc, arg.rpc_ssl_allow_chained);
+ if (any_cert_option)
+ command_line::add_arg(desc, arg.rpc_ssl_allow_any_cert);
}
- boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm)
+ boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm, const bool any_cert_option)
{
const descriptors arg{};
rpc_args config{};
@@ -118,6 +185,17 @@ namespace cryptonote
config.access_control_origins = std::move(access_control_origins);
}
+ auto ssl_options = do_process_ssl(vm, arg, any_cert_option);
+ if (!ssl_options)
+ return boost::none;
+ config.ssl_options = std::move(*ssl_options);
+
return {std::move(config)};
}
+
+ boost::optional<epee::net_utils::ssl_options_t> rpc_args::process_ssl(const boost::program_options::variables_map& vm, const bool any_cert_option)
+ {
+ const descriptors arg{};
+ return do_process_ssl(vm, arg, any_cert_option);
+ }
}
diff --git a/src/rpc/rpc_args.h b/src/rpc/rpc_args.h
index 216ba3712..619f02b42 100644
--- a/src/rpc/rpc_args.h
+++ b/src/rpc/rpc_args.h
@@ -35,6 +35,7 @@
#include "common/command_line.h"
#include "common/password.h"
+#include "net/net_ssl.h"
namespace cryptonote
{
@@ -54,16 +55,29 @@ namespace cryptonote
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;
+ const command_line::arg_descriptor<std::string> rpc_ssl;
+ const command_line::arg_descriptor<std::string> rpc_ssl_private_key;
+ const command_line::arg_descriptor<std::string> rpc_ssl_certificate;
+ const command_line::arg_descriptor<std::string> rpc_ssl_ca_certificates;
+ const command_line::arg_descriptor<std::vector<std::string>> rpc_ssl_allowed_fingerprints;
+ const command_line::arg_descriptor<bool> rpc_ssl_allow_chained;
+ const command_line::arg_descriptor<bool> rpc_ssl_allow_any_cert;
};
+ // `allow_any_cert` bool toggles `--rpc-ssl-allow-any-cert` configuration
+
static const char* tr(const char* str);
- static void init_options(boost::program_options::options_description& desc);
+ static void init_options(boost::program_options::options_description& desc, const bool any_cert_option = false);
//! \return Arguments specified by user, or `boost::none` if error
- static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm);
+ static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm, const bool any_cert_option = false);
+
+ //! \return SSL arguments specified by user, or `boost::none` if error
+ 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::vector<std::string> access_control_origins;
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
+ epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
};
}
diff --git a/src/rpc/rpc_handler.cpp b/src/rpc/rpc_handler.cpp
index e0a81c70f..af5cb98a3 100644
--- a/src/rpc/rpc_handler.cpp
+++ b/src/rpc/rpc_handler.cpp
@@ -26,26 +26,49 @@ namespace rpc
}
boost::optional<output_distribution_data>
- RpcHandler::get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative)
+ RpcHandler::get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, const std::function<crypto::hash(uint64_t)> &get_hash, bool cumulative, uint64_t blockchain_height)
{
static struct D
{
boost::mutex mutex;
std::vector<std::uint64_t> cached_distribution;
std::uint64_t cached_from, cached_to, cached_start_height, cached_base;
+ crypto::hash cached_m10_hash;
+ crypto::hash cached_top_hash;
bool cached;
- D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached(false) {}
+ D(): cached_from(0), cached_to(0), cached_start_height(0), cached_base(0), cached_m10_hash(crypto::null_hash), cached_top_hash(crypto::null_hash), cached(false) {}
} d;
const boost::unique_lock<boost::mutex> lock(d.mutex);
- if (d.cached && amount == 0 && d.cached_from == from_height && d.cached_to == to_height)
+ crypto::hash top_hash = crypto::null_hash;
+ if (d.cached_to < blockchain_height)
+ top_hash = get_hash(d.cached_to);
+ if (d.cached && amount == 0 && d.cached_from == from_height && d.cached_to == to_height && d.cached_top_hash == top_hash)
return process_distribution(cumulative, d.cached_start_height, d.cached_distribution, d.cached_base);
std::vector<std::uint64_t> distribution;
std::uint64_t start_height, base;
// see if we can extend the cache - a common case
- if (d.cached && amount == 0 && d.cached_from == from_height && to_height > d.cached_to)
+ bool can_extend = d.cached && amount == 0 && d.cached_from == from_height && to_height > d.cached_to && top_hash == d.cached_top_hash;
+ if (!can_extend)
+ {
+ // we kept track of the hash 10 blocks below, if it exists, so if it matches,
+ // we can still pop the last 10 cached slots and try again
+ if (d.cached && amount == 0 && d.cached_from == from_height && d.cached_to - d.cached_from >= 10 && to_height > d.cached_to - 10)
+ {
+ crypto::hash hash10 = get_hash(d.cached_to - 10);
+ if (hash10 == d.cached_m10_hash)
+ {
+ d.cached_to -= 10;
+ d.cached_top_hash = hash10;
+ d.cached_m10_hash = crypto::null_hash;
+ d.cached_distribution.resize(d.cached_distribution.size() - 10);
+ can_extend = true;
+ }
+ }
+ }
+ if (can_extend)
{
std::vector<std::uint64_t> new_distribution;
if (!f(amount, d.cached_to + 1, to_height, start_height, new_distribution, base))
@@ -74,6 +97,8 @@ namespace rpc
{
d.cached_from = from_height;
d.cached_to = to_height;
+ d.cached_top_hash = get_hash(d.cached_to);
+ d.cached_m10_hash = d.cached_to >= 10 ? get_hash(d.cached_to - 10) : crypto::null_hash;
d.cached_distribution = distribution;
d.cached_start_height = start_height;
d.cached_base = base;
diff --git a/src/rpc/rpc_handler.h b/src/rpc/rpc_handler.h
index 2439eaa58..b81983d28 100644
--- a/src/rpc/rpc_handler.h
+++ b/src/rpc/rpc_handler.h
@@ -32,6 +32,7 @@
#include <cstdint>
#include <string>
#include <vector>
+#include "crypto/hash.h"
namespace cryptonote
{
@@ -56,7 +57,7 @@ class RpcHandler
virtual std::string handle(const std::string& request) = 0;
static boost::optional<output_distribution_data>
- get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, bool cumulative);
+ get_output_distribution(const std::function<bool(uint64_t, uint64_t, uint64_t, uint64_t&, std::vector<uint64_t>&, uint64_t&)> &f, uint64_t amount, uint64_t from_height, uint64_t to_height, const std::function<crypto::hash(uint64_t)> &get_hash, bool cumulative, uint64_t blockchain_height);
};
diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h
index a0e4eff9d..9f60cf311 100644
--- a/src/serialization/binary_archive.h
+++ b/src/serialization/binary_archive.h
@@ -146,7 +146,8 @@ struct binary_archive<false> : public binary_archive_base<std::istream, false>
void serialize_uvarint(T &v)
{
typedef std::istreambuf_iterator<char> it;
- tools::read_varint(it(stream_), it(), v); // XXX handle failure
+ if (tools::read_varint(it(stream_), it(), v) < 0)
+ stream_.setstate(std::ios_base::failbit);
}
void begin_array(size_t &s)
diff --git a/src/serialization/difficulty_type.h b/src/serialization/difficulty_type.h
index e32e24b78..c551095f9 100644
--- a/src/serialization/difficulty_type.h
+++ b/src/serialization/difficulty_type.h
@@ -54,8 +54,8 @@ inline bool do_serialize(Archive<true>& ar, cryptonote::difficulty_type &diff)
{
if (!ar.stream().good())
return false;
- const uint64_t hi = (diff >> 64).convert_to<uint64_t>();
- const uint64_t lo = (diff << 64 >> 64).convert_to<uint64_t>();
+ const uint64_t hi = ((diff >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
+ const uint64_t lo = (diff & 0xffffffffffffffff).convert_to<uint64_t>();
ar.serialize_varint(hi);
ar.serialize_varint(lo);
if (!ar.stream().good())
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 007bf265f..553e9951f 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -212,7 +212,7 @@ inline bool do_serialize(Archive &ar, bool &v)
* \brief self-explanatory
*/
#define END_SERIALIZE() \
- return true; \
+ return ar.stream().good(); \
}
/*! \macro VALUE(f)
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index 2e134931f..02a099811 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -118,7 +118,7 @@ typedef cryptonote::simple_wallet sw;
if (!m_long_payment_id_support) { \
fail_msg_writer() << tr("Warning: Long payment IDs are obsolete."); \
fail_msg_writer() << tr("Long payment IDs are not encrypted on the blockchain, and will harm your privacy."); \
- fail_msg_writer() << tr("Use --long-payment-id-support if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \
+ fail_msg_writer() << tr("Use --long-payment-id-support-bad-for-privacy if you really must use one, and warn the recipient they are using an obsolete feature that will disappear in the future."); \
return true; \
} \
} while(0)
@@ -4092,6 +4092,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if (welcome)
message_writer(console_color_yellow, true) << tr("If you are new to Monero, type \"welcome\" for a brief overview.");
+ if (m_long_payment_id_support)
+ {
+ message_writer(console_color_red, false) <<
+ tr("WARNING: obsolete long payment IDs are enabled. Sending transactions with those payment IDs are bad for your privacy.");
+ message_writer(console_color_red, false) <<
+ tr("It is recommended that you do not use them, and ask recipients who ask for one to not endanger your privacy.");
+ }
+
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -4926,7 +4934,7 @@ void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block
m_refresh_progress_reporter.update(height, false);
}
//----------------------------------------------------------------------------------------------------
-void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index)
+void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time)
{
message_writer(console_color_green, false) << "\r" <<
tr("Height ") << height << ", " <<
@@ -4948,6 +4956,8 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid,
(m_long_payment_id_support ? tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead.") : tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete. Support will be withdrawn in the future. Use subaddresses instead."));
}
}
+ if (unlock_time)
+ message_writer() << tr("NOTE: This transaction is locked, see details with: show_transfer ") + epee::string_tools::pod_to_hex(txid);
if (m_auto_refresh_refreshing)
m_cmd_binder.print_prompt();
else
@@ -5571,7 +5581,7 @@ bool simple_wallet::print_ring_members(const std::vector<tools::wallet2::pending
if (j == source.real_output)
highlight_height = heights[j];
}
- std::pair<std::string, std::string> ring_str = show_outputs_line(heights, highlight_height);
+ std::pair<std::string, std::string> ring_str = show_outputs_line(heights, blockchain_height, highlight_height);
ostr << ring_str.first << tr("\n|") << ring_str.second << tr("|\n");
}
// warn if rings contain keys originating from the same tx or temporally very close block heights
@@ -5906,12 +5916,14 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
uint64_t total_fee = 0;
uint64_t dust_not_in_fee = 0;
uint64_t dust_in_fee = 0;
+ uint64_t change = 0;
for (size_t n = 0; n < ptx_vector.size(); ++n)
{
total_fee += ptx_vector[n].fee;
for (auto i: ptx_vector[n].selected_transfers)
total_sent += m_wallet->get_transfer_details(i).amount();
total_sent -= ptx_vector[n].change_dts.amount + ptx_vector[n].fee;
+ change += ptx_vector[n].change_dts.amount;
if (ptx_vector[n].dust_added_to_fee)
dust_in_fee += ptx_vector[n].dust;
@@ -5949,7 +5961,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
if (transfer_type == TransferLocked)
{
float days = locked_blocks / 720.0f;
- prompt << boost::format(tr(".\nThis transaction will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % ((unsigned long long)unlock_block) % days;
+ prompt << boost::format(tr(".\nThis transaction (including %s change) will unlock on block %llu, in approximately %s days (assuming 2 minutes per block)")) % cryptonote::print_money(change) % ((unsigned long long)unlock_block) % days;
}
if (m_wallet->print_ring_members())
{
@@ -7673,6 +7685,8 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
local_args.erase(local_args.begin());
}
+ const uint64_t last_block_height = m_wallet->get_blockchain_current_height();
+
if (in || coinbase) {
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments;
m_wallet->get_payments(payments, min_height, max_height, m_current_subaddress_account, subaddr_indices);
@@ -7687,6 +7701,25 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
const std::string type = pd.m_coinbase ? tr("block") : tr("in");
const bool unlocked = m_wallet->is_transfer_unlocked(pd.m_unlock_time, pd.m_block_height);
+ std::string locked_msg = "unlocked";
+ if (!unlocked)
+ {
+ locked_msg = "locked";
+ const uint64_t unlock_time = pd.m_unlock_time;
+ if (pd.m_unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER)
+ {
+ uint64_t bh = std::max(pd.m_unlock_time, pd.m_block_height + CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE);
+ if (bh >= last_block_height)
+ locked_msg = std::to_string(bh - last_block_height) + " blks";
+ }
+ else
+ {
+ uint64_t current_time = static_cast<uint64_t>(time(NULL));
+ uint64_t threshold = current_time + (m_wallet->use_fork_rules(2, 0) ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1);
+ if (threshold < pd.m_unlock_time)
+ locked_msg = get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold));
+ }
+ }
transfers.push_back({
type,
pd.m_block_height,
@@ -7700,7 +7733,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
{{destination, pd.m_amount}},
{pd.m_subaddr_index.minor},
note,
- (unlocked) ? "unlocked" : "locked"
+ locked_msg
});
}
}
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index 76d446ba5..33b18612c 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -310,7 +310,7 @@ namespace cryptonote
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const cryptonote::block& block);
- virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time);
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index);
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index);
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx);
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 032b873d6..1711db482 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -157,7 +157,7 @@ struct Wallet2CallbackImpl : public tools::i_wallet2_callback
}
}
- virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index)
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time)
{
std::string tx_hash = epee::string_tools::pod_to_hex(txid);
diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp
index f5f3c0e1b..1d5078a11 100644
--- a/src/wallet/node_rpc_proxy.cpp
+++ b/src/wallet/node_rpc_proxy.cpp
@@ -37,9 +37,10 @@ namespace tools
static const std::chrono::seconds rpc_timeout = std::chrono::minutes(3) + std::chrono::seconds(30);
-NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex)
+NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::recursive_mutex &mutex)
: m_http_client(http_client)
, m_daemon_rpc_mutex(mutex)
+ , m_offline(false)
{
invalidate();
}
@@ -61,6 +62,8 @@ void NodeRPCProxy::invalidate()
boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const
{
+ if (m_offline)
+ return boost::optional<std::string>("offline");
if (m_rpc_version == 0)
{
cryptonote::COMMAND_RPC_GET_VERSION::request req_t = AUTO_VAL_INIT(req_t);
@@ -84,6 +87,8 @@ void NodeRPCProxy::set_height(uint64_t h)
boost::optional<std::string> NodeRPCProxy::get_info() const
{
+ if (m_offline)
+ return boost::optional<std::string>("offline");
const time_t now = time(NULL);
if (now >= m_get_info_time + 30) // re-cache every 30 seconds
{
@@ -134,6 +139,8 @@ boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &bloc
boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) const
{
+ if (m_offline)
+ return boost::optional<std::string>("offline");
if (m_earliest_height[version] == 0)
{
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req_t = AUTO_VAL_INIT(req_t);
@@ -161,6 +168,8 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_
if (result)
return result;
+ if (m_offline)
+ return boost::optional<std::string>("offline");
if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks)
{
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
@@ -191,6 +200,8 @@ boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &f
if (result)
return result;
+ if (m_offline)
+ return boost::optional<std::string>("offline");
if (m_dynamic_base_fee_estimate_cached_height != height)
{
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h
index 3630aec08..3b75c8b94 100644
--- a/src/wallet/node_rpc_proxy.h
+++ b/src/wallet/node_rpc_proxy.h
@@ -39,9 +39,10 @@ namespace tools
class NodeRPCProxy
{
public:
- NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::mutex &mutex);
+ NodeRPCProxy(epee::net_utils::http::http_simple_client &http_client, boost::recursive_mutex &mutex);
void invalidate();
+ void set_offline(bool offline) { m_offline = offline; }
boost::optional<std::string> get_rpc_version(uint32_t &version) const;
boost::optional<std::string> get_height(uint64_t &height) const;
@@ -56,7 +57,8 @@ private:
boost::optional<std::string> get_info() const;
epee::net_utils::http::http_simple_client &m_http_client;
- boost::mutex &m_daemon_rpc_mutex;
+ boost::recursive_mutex &m_daemon_rpc_mutex;
+ bool m_offline;
mutable uint64_t m_height;
mutable uint64_t m_earliest_height[256];
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index b288994a5..8f3f30da1 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -29,7 +29,6 @@
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <numeric>
-#include <random>
#include <tuple>
#include <boost/format.hpp>
#include <boost/optional/optional.hpp>
@@ -40,6 +39,7 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/range/adaptor/transformed.hpp>
+#include <boost/preprocessor/stringize.hpp>
#include "include_base_utils.h"
using namespace epee;
@@ -129,7 +129,11 @@ using namespace cryptonote;
#define FIRST_REFRESH_GRANULARITY 1024
-#define GAMMA_PICK_HALF_WINDOW 5
+#define GAMMA_SHAPE 19.28
+#define GAMMA_SCALE (1/1.61)
+
+#define DEFAULT_MIN_OUTPUT_COUNT 5
+#define DEFAULT_MIN_OUTPUT_VALUE (2*COIN)
static const std::string MULTISIG_SIGNATURE_MAGIC = "SigMultisigPkV1";
static const std::string MULTISIG_EXTRA_INFO_MAGIC = "MultisigxV1";
@@ -221,6 +225,8 @@ namespace
add_reason(reason, "fee too low");
if (res.not_rct)
add_reason(reason, "tx is not ringct");
+ if (res.sanity_check_failed)
+ add_reason(reason, "tx sanity check failed");
if (res.not_relayed)
add_reason(reason, "tx was not relayed");
return reason;
@@ -266,6 +272,7 @@ struct options {
const command_line::arg_descriptor<std::string> hw_device_derivation_path = {"hw-device-deriv-path", tools::wallet2::tr("HW device wallet derivation path (e.g., SLIP-10)"), ""};
const command_line::arg_descriptor<std::string> tx_notify = { "tx-notify" , "Run a program for each new incoming transaction, '%s' will be replaced by the transaction hash" , "" };
const command_line::arg_descriptor<bool> no_dns = {"no-dns", tools::wallet2::tr("Do not use DNS"), false};
+ const command_line::arg_descriptor<bool> offline = {"offline", tools::wallet2::tr("Do not connect to a daemon, nor use DNS"), false};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
@@ -337,6 +344,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
{
std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints{ daemon_ssl_allowed_fingerprints.size() };
std::transform(daemon_ssl_allowed_fingerprints.begin(), daemon_ssl_allowed_fingerprints.end(), ssl_allowed_fingerprints.begin(), epee::from_hex::vector);
+ for (const auto &fpr: ssl_allowed_fingerprints)
+ {
+ THROW_WALLET_EXCEPTION_IF(fpr.size() != SSL_FINGERPRINT_SIZE, tools::error::wallet_internal_error,
+ "SHA-256 fingerprint should be " BOOST_PP_STRINGIZE(SSL_FINGERPRINT_SIZE) " bytes long.");
+ }
ssl_options = epee::net_utils::ssl_options_t{
std::move(ssl_allowed_fingerprints), std::move(daemon_ssl_ca_file)
@@ -387,8 +399,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
{
const boost::string_ref real_daemon = boost::string_ref{daemon_address}.substr(0, daemon_address.rfind(':'));
+ /* If SSL or proxy is enabled, then a specific cert, CA or fingerprint must
+ be specified. This is specific to the wallet. */
const bool verification_required =
- ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || use_proxy;
+ ssl_options.verification != epee::net_utils::ssl_verification_t::none &&
+ (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || use_proxy);
THROW_WALLET_EXCEPTION_IF(
verification_required && !ssl_options.has_strong_verification(real_daemon),
@@ -456,6 +471,9 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
if (command_line::get_arg(vm, opts.no_dns))
wallet->enable_dns(false);
+ if (command_line::get_arg(vm, opts.offline))
+ wallet->set_offline();
+
try
{
if (!command_line::is_arg_defaulted(vm, opts.tx_notify))
@@ -792,7 +810,7 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// pseudoOuts
size += 32 * n_inputs;
// ecdhInfo
- size += 2 * 32 * n_outputs;
+ size += 8 * n_outputs;
// outPk - only commitment is saved
size += 32 * n_outputs;
// txnFee
@@ -954,6 +972,44 @@ const size_t MAX_SPLIT_ATTEMPTS = 30;
constexpr const std::chrono::seconds wallet2::rpc_timeout;
const char* wallet2::tr(const char* str) { return i18n_translate(str, "tools::wallet2"); }
+gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets, double shape, double scale):
+ rct_offsets(rct_offsets)
+{
+ gamma = std::gamma_distribution<double>(shape, scale);
+ THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, error::wallet_internal_error, "Bad offset calculation");
+ const size_t blocks_in_a_year = 86400 * 365 / DIFFICULTY_TARGET_V2;
+ const size_t blocks_to_consider = std::min<size_t>(rct_offsets.size(), blocks_in_a_year);
+ const size_t outputs_to_consider = rct_offsets.back() - (blocks_to_consider < rct_offsets.size() ? rct_offsets[rct_offsets.size() - blocks_to_consider - 1] : 0);
+ begin = rct_offsets.data();
+ end = rct_offsets.data() + rct_offsets.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
+ num_rct_outputs = *(end - 1);
+ THROW_WALLET_EXCEPTION_IF(num_rct_outputs == 0, error::wallet_internal_error, "No rct outputs");
+ average_output_time = DIFFICULTY_TARGET_V2 * blocks_to_consider / outputs_to_consider; // this assumes constant target over the whole rct range
+};
+
+gamma_picker::gamma_picker(const std::vector<uint64_t> &rct_offsets): gamma_picker(rct_offsets, GAMMA_SHAPE, GAMMA_SCALE) {}
+
+uint64_t gamma_picker::pick()
+{
+ double x = gamma(engine);
+ x = exp(x);
+ uint64_t output_index = x / average_output_time;
+ if (output_index >= num_rct_outputs)
+ return std::numeric_limits<uint64_t>::max(); // bad pick
+ output_index = num_rct_outputs - 1 - output_index;
+
+ const uint64_t *it = std::lower_bound(begin, end, output_index);
+ THROW_WALLET_EXCEPTION_IF(it == end, error::wallet_internal_error, "output_index not found");
+ uint64_t index = std::distance(begin, it);
+
+ const uint64_t first_rct = index == 0 ? 0 : rct_offsets[index - 1];
+ const uint64_t n_rct = rct_offsets[index] - first_rct;
+ if (n_rct == 0)
+ return std::numeric_limits<uint64_t>::max(); // bad pick
+ MTRACE("Picking 1/" << n_rct << " in block " << index);
+ return first_rct + crypto::rand_idx(n_rct);
+};
+
wallet_keys_unlocker::wallet_keys_unlocker(wallet2 &w, const boost::optional<tools::password_container> &password):
w(w),
locked(password != boost::none)
@@ -1083,7 +1139,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_unattended(unattended),
m_devices_registered(false),
m_device_last_key_image_sync(0),
- m_use_dns(true)
+ m_use_dns(true),
+ m_offline(false)
{
}
@@ -1139,6 +1196,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.hw_device_derivation_path);
command_line::add_arg(desc_params, opts.tx_notify);
command_line::add_arg(desc_params, opts.no_dns);
+ command_line::add_arg(desc_params, opts.offline);
}
std::pair<std::unique_ptr<wallet2>, tools::password_container> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -1184,6 +1242,8 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
//----------------------------------------------------------------------------------------------------
bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
{
+ boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
+
if(m_http_client.is_connected())
m_http_client.disconnect();
m_daemon_address = std::move(daemon_address);
@@ -1959,7 +2019,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
}
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
+ m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time);
}
total_received_1 += amount;
notify = true;
@@ -2029,7 +2089,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid);
if (0 != m_callback)
- m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index);
+ m_callback->on_money_received(height, txid, tx, td.m_amount, td.m_subaddr_index, td.m_tx.unlock_time);
}
total_received_1 += extra_amount;
notify = true;
@@ -2392,7 +2452,7 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height,
req.start_height = start_height;
req.no_miner_tx = m_refresh_type == RefreshNoCoinbase;
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_bin("/getblocks.bin", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_bin("/getblocks.bin", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin");
@@ -2414,7 +2474,7 @@ void wallet2::pull_hashes(uint64_t start_height, uint64_t &blocks_start_height,
req.start_height = start_height;
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_bin("/gethashes.bin", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_bin("/gethashes.bin", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gethashes.bin");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gethashes.bin");
@@ -2691,7 +2751,7 @@ void wallet2::update_pool_state(bool refreshed)
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::request req;
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_HASHES_BIN::response res;
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/get_transaction_pool_hashes.bin", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_json("/get_transaction_pool_hashes.bin", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_transaction_pool_hashes.bin");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_transaction_pool_hashes.bin");
@@ -2838,7 +2898,7 @@ void wallet2::update_pool_state(bool refreshed)
req.decode_as_json = false;
req.prune = true;
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
MDEBUG("Got " << r << " and " << res.status);
if (r && res.status == CORE_RPC_STATUS_OK)
@@ -2999,6 +3059,13 @@ std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> wallet2::create
//----------------------------------------------------------------------------------------------------
void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blocks_fetched, bool& received_money, bool check_pool)
{
+ if (m_offline)
+ {
+ blocks_fetched = 0;
+ received_money = 0;
+ return;
+ }
+
if(m_light_wallet) {
// MyMonero get_address_info needs to be called occasionally to trigger wallet sync.
@@ -3076,6 +3143,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
}
});
+ auto scope_exit_handler_hwdev = epee::misc_utils::create_scope_leave_handler([&](){hwdev.computing_key_images(false);});
bool first = true;
while(m_run.load(std::memory_order_relaxed))
{
@@ -3201,7 +3269,6 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo
LOG_PRINT_L1("Failed to check pending transactions");
}
- hwdev.computing_key_images(false);
m_first_refresh_done = true;
LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all()));
@@ -3258,7 +3325,7 @@ bool wallet2::get_rct_distribution(uint64_t &start_height, std::vector<uint64_t>
req.binary = true;
req.compress = true;
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_bin("/get_output_distribution.bin", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_bin("/get_output_distribution.bin", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
if (!r)
{
@@ -5088,7 +5155,14 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
{
THROW_WALLET_EXCEPTION_IF(!m_is_initialized, error::wallet_not_initialized);
- boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex);
+ if (m_offline)
+ {
+ if (version)
+ *version = 0;
+ if (ssl)
+ *ssl = false;
+ return false;
+ }
// TODO: Add light wallet version check.
if(m_light_wallet) {
@@ -5099,20 +5173,23 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
return m_light_wallet_connected;
}
- if(!m_http_client.is_connected(ssl))
{
- m_node_rpc_proxy.invalidate();
- if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
- return false;
+ boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
if(!m_http_client.is_connected(ssl))
- return false;
+ {
+ m_node_rpc_proxy.invalidate();
+ if (!m_http_client.connect(std::chrono::milliseconds(timeout)))
+ return false;
+ if(!m_http_client.is_connected(ssl))
+ return false;
+ }
}
if (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 = net_utils::invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t, m_http_client);
+ bool r = invoke_http_json_rpc("/json_rpc", "get_version", req_t, resp_t);
if(!r) {
*version = 0;
return false;
@@ -5126,6 +5203,18 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout)
return true;
}
//----------------------------------------------------------------------------------------------------
+void wallet2::set_offline(bool offline)
+{
+ m_offline = offline;
+ m_http_client.set_auto_connect(!offline);
+ if (offline)
+ {
+ boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
+ if(m_http_client.is_connected())
+ m_http_client.disconnect();
+ }
+}
+//----------------------------------------------------------------------------------------------------
bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const
{
hw::device &hwdev = m_account.get_device();
@@ -5304,7 +5393,7 @@ void wallet2::trim_hashchain()
cryptonote::COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response res = AUTO_VAL_INIT(res);
m_daemon_rpc_mutex.lock();
req.height = m_blockchain.size() - 1;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_json_rpc("/json_rpc", "getblockheaderbyheight", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
if (r && res.status == CORE_RPC_STATUS_OK)
{
@@ -5660,7 +5749,7 @@ void wallet2::rescan_spent()
for (size_t n = start_offset; n < start_offset + n_outputs; ++n)
req.key_images.push_back(string_tools::pod_to_hex(m_transfers[n].m_key_image));
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
@@ -5988,7 +6077,7 @@ void wallet2::commit_tx(pending_tx& ptx)
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
oreq.tx = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/submit_raw_tx", oreq, ores, m_http_client, rpc_timeout, "POST");
+ bool r = invoke_http_json("/submit_raw_tx", oreq, ores, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "submit_raw_tx");
// MyMonero and OpenMonero use different status strings
@@ -6000,9 +6089,10 @@ void wallet2::commit_tx(pending_tx& ptx)
COMMAND_RPC_SEND_RAW_TX::request req;
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx));
req.do_not_relay = false;
+ req.do_sanity_checks = true;
COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp;
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/sendrawtransaction", req, daemon_send_resp, m_http_client, rpc_timeout);
+ bool r = invoke_http_json("/sendrawtransaction", req, daemon_send_resp, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction");
THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction");
@@ -6955,7 +7045,7 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
m_daemon_rpc_mutex.lock();
getbh_req.start_height = m_blockchain.size() - N;
getbh_req.end_height = m_blockchain.size() - 1;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, m_http_client, rpc_timeout);
+ bool r = invoke_http_json_rpc("/json_rpc", "getblockheadersrange", getbh_req, getbh_res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblockheadersrange");
THROW_WALLET_EXCEPTION_IF(getbh_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblockheadersrange");
@@ -7126,7 +7216,7 @@ bool wallet2::unset_ring(const crypto::hash &txid)
req.decode_as_json = false;
req.prune = true;
m_daemon_rpc_mutex.lock();
- bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to get transaction from daemon");
if (res.txs.empty())
@@ -7180,8 +7270,8 @@ bool wallet2::find_and_save_rings(bool force)
req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txs_hashes[s]));
bool r;
{
- const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
- r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
}
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
@@ -7322,7 +7412,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
oreq.count = light_wallet_requested_outputs_count;
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/get_random_outs", oreq, ores, m_http_client, rpc_timeout, "POST");
+ bool r = invoke_http_json("/get_random_outs", oreq, ores, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs");
THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
@@ -7462,7 +7552,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
req_t.unlocked = true;
req_t.recent_cutoff = time(NULL) - RECENT_OUTPUT_ZONE;
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -7485,7 +7575,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
req_t.cumulative = true;
req_t.binary = true;
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, m_http_client, rpc_timeout * 1000);
+ bool r = invoke_http_json_rpc("/json_rpc", "get_output_distribution", req_t, resp_t, rpc_timeout * 1000);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "transfer_selected");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_distribution");
@@ -7524,61 +7614,9 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
- struct gamma_engine
- {
- typedef uint64_t result_type;
- static constexpr result_type min() { return 0; }
- static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
- result_type operator()() { return crypto::rand<result_type>(); }
- } engine;
- static const double shape = 19.28/*16.94*/;
- //static const double shape = m_testnet ? 17.02 : 17.28;
- static const double scale = 1/1.61;
- std::gamma_distribution<double> gamma(shape, scale);
- THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, error::wallet_internal_error, "Bad offset calculation");
- uint64_t last_usable_block = rct_offsets.size() - 1;
- auto pick_gamma = [&]()
- {
- double x = gamma(engine);
- x = exp(x);
- uint64_t block_offset = x / DIFFICULTY_TARGET_V2; // this assumes constant target over the whole rct range
- if (block_offset > last_usable_block - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE)
- return std::numeric_limits<uint64_t>::max(); // bad pick
- block_offset = last_usable_block - block_offset;
- THROW_WALLET_EXCEPTION_IF(block_offset > last_usable_block, error::wallet_internal_error, "Bad offset calculation");
- THROW_WALLET_EXCEPTION_IF(block_offset > 0 && rct_offsets[block_offset] < rct_offsets[block_offset - 1],
- error::get_output_distribution, "Decreasing offsets in rct distribution: " +
- std::to_string(block_offset - 1) + ": " + std::to_string(rct_offsets[block_offset - 1]) + ", " +
- std::to_string(block_offset) + ": " + std::to_string(rct_offsets[block_offset]));
- uint64_t first_block_offset = block_offset, last_block_offset = block_offset;
- for (size_t half_window = 0; half_window <= GAMMA_PICK_HALF_WINDOW; ++half_window)
- {
- // end when we have a non empty block
- uint64_t cum0 = first_block_offset > 0 ? rct_offsets[first_block_offset] - rct_offsets[first_block_offset - 1] : rct_offsets[0];
- if (cum0 > 1)
- break;
- uint64_t cum1 = last_block_offset > 0 ? rct_offsets[last_block_offset] - rct_offsets[last_block_offset - 1] : rct_offsets[0];
- if (cum1 > 1)
- break;
- if (first_block_offset == 0 && last_block_offset >= last_usable_block)
- break;
- // expand up to bounds
- if (first_block_offset > 0)
- --first_block_offset;
- else
- return std::numeric_limits<uint64_t>::max(); // bad pick
- if (last_block_offset < last_usable_block - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE)
- ++last_block_offset;
- else
- return std::numeric_limits<uint64_t>::max(); // bad pick
- }
- const uint64_t first_rct = first_block_offset == 0 ? 0 : rct_offsets[first_block_offset - 1];
- const uint64_t n_rct = rct_offsets[last_block_offset] - first_rct;
- if (n_rct == 0)
- return rct_offsets[block_offset] ? rct_offsets[block_offset] - 1 : 0;
- MDEBUG("Picking 1/" << n_rct << " in " << (last_block_offset - first_block_offset + 1) << " blocks centered around " << block_offset + rct_start_height);
- return first_rct + crypto::rand_idx(n_rct);
- };
+ std::unique_ptr<gamma_picker> gamma;
+ if (has_rct_distribution)
+ gamma.reset(new gamma_picker(rct_offsets));
size_t num_selected_transfers = 0;
for(size_t idx: selected_transfers)
@@ -7726,7 +7764,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
}
- if (num_outs <= requested_outputs_count && !existing_ring_found)
+ if (num_outs <= requested_outputs_count)
{
for (uint64_t i = 0; i < num_outs; i++)
req.outputs.push_back({amount, i});
@@ -7752,6 +7790,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// while we still need more mixins
uint64_t num_usable_outs = num_outs;
bool allow_blackballed = false;
+ MDEBUG("Starting gamma picking with " << num_outs << ", num_usable_outs " << num_usable_outs
+ << ", requested_outputs_count " << requested_outputs_count);
while (num_found < requested_outputs_count)
{
// if we've gone through every possible output, we've gotten all we can
@@ -7776,20 +7816,21 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
const char *type = "";
if (amount == 0 && has_rct_distribution)
{
+ THROW_WALLET_EXCEPTION_IF(!gamma, error::wallet_internal_error, "No gamma picker");
// gamma distribution
if (num_found -1 < recent_outputs_count + pre_fork_outputs_count)
{
- do i = pick_gamma(); while (i >= segregation_limit[amount].first);
+ do i = gamma->pick(); while (i >= segregation_limit[amount].first);
type = "pre-fork gamma";
}
else if (num_found -1 < recent_outputs_count + pre_fork_outputs_count + post_fork_outputs_count)
{
- do i = pick_gamma(); while (i < segregation_limit[amount].first || i >= num_outs);
+ do i = gamma->pick(); while (i < segregation_limit[amount].first || i >= num_outs);
type = "post-fork gamma";
}
else
{
- do i = pick_gamma(); while (i >= num_outs);
+ do i = gamma->pick(); while (i >= num_outs);
type = "gamma";
}
}
@@ -7850,6 +7891,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
picks[type].insert(i);
req.outputs.push_back({amount, i});
++num_found;
+ MDEBUG("picked " << i << ", " << num_found << " now picked");
}
for (const auto &pick: picks)
@@ -7884,7 +7926,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// get the keys for those
req.get_txid = false;
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, daemon_resp, m_http_client, rpc_timeout);
+ bool r = invoke_http_bin("/get_outs.bin", req, daemon_resp, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
@@ -8596,7 +8638,7 @@ bool wallet2::light_wallet_login(bool &new_address)
// Always create account if it doesn't exist.
request.create_account = true;
m_daemon_rpc_mutex.lock();
- bool connected = epee::net_utils::invoke_http_json("/login", request, response, m_http_client, rpc_timeout, "POST");
+ bool connected = invoke_http_json("/login", request, response, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
// MyMonero doesn't send any status message. OpenMonero does.
m_light_wallet_connected = connected && (response.status.empty() || response.status == "success");
@@ -8621,7 +8663,7 @@ bool wallet2::light_wallet_import_wallet_request(tools::COMMAND_RPC_IMPORT_WALLE
oreq.address = get_account().get_public_address_str(m_nettype);
oreq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/import_wallet_request", oreq, response, m_http_client, rpc_timeout, "POST");
+ bool r = invoke_http_json("/import_wallet_request", oreq, response, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "import_wallet_request");
@@ -8647,7 +8689,7 @@ void wallet2::light_wallet_get_unspent_outs()
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/get_unspent_outs", oreq, ores, m_http_client, rpc_timeout, "POST");
+ bool r = invoke_http_json("/get_unspent_outs", oreq, ores, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_unspent_outs");
THROW_WALLET_EXCEPTION_IF(ores.status == "error", error::wallet_internal_error, ores.reason);
@@ -8792,7 +8834,7 @@ bool wallet2::light_wallet_get_address_info(tools::COMMAND_RPC_GET_ADDRESS_INFO:
request.address = get_account().get_public_address_str(m_nettype);
request.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/get_address_info", request, response, m_http_client, rpc_timeout, "POST");
+ bool r = invoke_http_json("/get_address_info", request, response, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_info");
// TODO: Validate result
@@ -8809,7 +8851,7 @@ void wallet2::light_wallet_get_address_txs()
ireq.address = get_account().get_public_address_str(m_nettype);
ireq.view_key = string_tools::pod_to_hex(get_account().get_keys().m_view_secret_key);
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/get_address_txs", ireq, ires, m_http_client, rpc_timeout, "POST");
+ bool r = invoke_http_json("/get_address_txs", ireq, ires, rpc_timeout, "POST");
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_address_txs");
//OpenMonero sends status=success, Mymonero doesn't.
@@ -9348,9 +9390,16 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
idx = pop_best_value(indices, tx.selected_transfers, true);
// we might not want to add it if it's a large output and we don't have many left
- if (m_transfers[idx].amount() >= m_min_output_value) {
- if (get_count_above(m_transfers, *unused_transfers_indices, m_min_output_value) < m_min_output_count) {
- LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_money(m_min_output_value) << ", not adding");
+ uint64_t min_output_value = m_min_output_value;
+ uint32_t min_output_count = m_min_output_count;
+ if (min_output_value == 0 && min_output_count == 0)
+ {
+ min_output_value = DEFAULT_MIN_OUTPUT_VALUE;
+ min_output_count = DEFAULT_MIN_OUTPUT_COUNT;
+ }
+ if (m_transfers[idx].amount() >= min_output_value) {
+ if (get_count_above(m_transfers, *unused_transfers_indices, min_output_value) < min_output_count) {
+ LOG_PRINT_L2("Second output was not strictly needed, and we're running out of outputs above " << print_money(min_output_value) << ", not adding");
break;
}
}
@@ -9509,7 +9558,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
tx.ptx = test_ptx;
tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
- tx.needed_fee = needed_fee;
+ tx.needed_fee = test_ptx.fee;
accumulated_fee += test_ptx.fee;
accumulated_change += test_ptx.change_dts.amount;
adding_fee = false;
@@ -9907,7 +9956,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.ptx = test_ptx;
tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
- tx.needed_fee = needed_fee;
+ tx.needed_fee = test_ptx.fee;
accumulated_fee += test_ptx.fee;
accumulated_change += test_ptx.change_dts.amount;
if (!unused_transfers_indices.empty() || !unused_dust_indices.empty())
@@ -10113,7 +10162,7 @@ std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t co
req_t.max_count = 0;
req_t.unlocked = unlocked;
req_t.recent_cutoff = 0;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_outputs_from_histogram");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -10151,7 +10200,7 @@ uint64_t wallet2::get_num_rct_outputs()
req_t.max_count = 0;
req_t.unlocked = true;
req_t.recent_cutoff = 0;
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, m_http_client, rpc_timeout);
+ bool r = invoke_http_json_rpc("/json_rpc", "get_output_histogram", req_t, resp_t, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_num_rct_outputs");
THROW_WALLET_EXCEPTION_IF(resp_t.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
@@ -10276,7 +10325,7 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s
req.decode_as_json = false;
req.prune = true;
m_daemon_rpc_mutex.lock();
- bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon");
@@ -10319,8 +10368,8 @@ void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
{
- const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
- r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
}
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
@@ -10369,8 +10418,8 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
{
- const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
- r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
}
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
@@ -10431,8 +10480,8 @@ std::string wallet2::get_spend_proof(const crypto::hash &txid, const std::string
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
bool r;
{
- const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
- r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
}
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
@@ -10487,8 +10536,8 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
bool r;
{
- const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
- r = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ r = invoke_http_json("/gettransactions", req, res, rpc_timeout);
}
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
@@ -10560,8 +10609,8 @@ bool wallet2::check_spend_proof(const crypto::hash &txid, const std::string &mes
COMMAND_RPC_GET_OUTPUTS_BIN::response res = AUTO_VAL_INIT(res);
bool r;
{
- const boost::lock_guard<boost::mutex> lock{m_daemon_rpc_mutex};
- r = epee::net_utils::invoke_http_bin("/get_outs.bin", req, res, m_http_client, rpc_timeout);
+ const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
+ r = invoke_http_bin("/get_outs.bin", req, res, rpc_timeout);
}
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_outs.bin");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_outs.bin");
@@ -10659,7 +10708,7 @@ void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_de
req.decode_as_json = false;
req.prune = true;
m_daemon_rpc_mutex.lock();
- bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon");
@@ -10708,7 +10757,7 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac
req.decode_as_json = false;
req.prune = true;
m_daemon_rpc_mutex.lock();
- bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon");
@@ -10863,7 +10912,7 @@ bool wallet2::check_tx_proof(const crypto::hash &txid, const cryptonote::account
req.decode_as_json = false;
req.prune = true;
m_daemon_rpc_mutex.lock();
- bool ok = net_utils::invoke_http_json("/gettransactions", req, res, m_http_client);
+ bool ok = invoke_http_json("/gettransactions", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1),
error::wallet_internal_error, "Failed to get transaction from daemon");
@@ -11155,7 +11204,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
gettx_req.decode_as_json = false;
gettx_req.prune = true;
m_daemon_rpc_mutex.lock();
- bool ok = net_utils::invoke_http_json("/gettransactions", gettx_req, gettx_res, m_http_client);
+ bool ok = invoke_http_json("/gettransactions", gettx_req, gettx_res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!ok || gettx_res.txs.size() != proofs.size(),
error::wallet_internal_error, "Failed to get transaction from daemon");
@@ -11166,7 +11215,7 @@ bool wallet2::check_reserve_proof(const cryptonote::account_public_address &addr
for (size_t i = 0; i < proofs.size(); ++i)
kispent_req.key_images.push_back(epee::string_tools::pod_to_hex(proofs[i].key_image));
m_daemon_rpc_mutex.lock();
- ok = epee::net_utils::invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, m_http_client, rpc_timeout);
+ ok = invoke_http_json("/is_key_image_spent", kispent_req, kispent_res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!ok || kispent_res.spent_status.size() != proofs.size(),
error::wallet_internal_error, "Failed to get key image spent status from daemon");
@@ -11718,7 +11767,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
{
PERF_TIMER(import_key_images_RPC);
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/is_key_image_spent", req, daemon_resp, m_http_client, rpc_timeout);
+ bool r = invoke_http_json("/is_key_image_spent", req, daemon_resp, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent");
THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent");
@@ -11804,7 +11853,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
PERF_TIMER_START(import_key_images_E);
m_daemon_rpc_mutex.lock();
- bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout);
+ bool r = invoke_http_json("/gettransactions", gettxs_req, gettxs_res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
@@ -12032,14 +12081,15 @@ void wallet2::import_blockchain(const std::tuple<size_t, crypto::hash, std::vect
m_last_block_reward = cryptonote::get_outs_money_amount(genesis.miner_tx);
}
//----------------------------------------------------------------------------------------------------
-std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs() const
+std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export_outputs(bool all) const
{
PERF_TIMER(export_outputs);
std::vector<tools::wallet2::transfer_details> outs;
size_t offset = 0;
- while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
- ++offset;
+ if (!all)
+ while (offset < m_transfers.size() && (m_transfers[offset].m_key_image_known && !m_transfers[offset].m_key_image_request))
+ ++offset;
outs.reserve(m_transfers.size() - offset);
for (size_t n = offset; n < m_transfers.size(); ++n)
@@ -12052,13 +12102,13 @@ std::pair<size_t, std::vector<tools::wallet2::transfer_details>> wallet2::export
return std::make_pair(offset, outs);
}
//----------------------------------------------------------------------------------------------------
-std::string wallet2::export_outputs_to_str() const
+std::string wallet2::export_outputs_to_str(bool all) const
{
PERF_TIMER(export_outputs_to_str);
std::stringstream oss;
boost::archive::portable_binary_oarchive ar(oss);
- const auto& outputs = export_outputs();
+ const auto& outputs = export_outputs(all);
ar << outputs;
std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC));
@@ -12582,8 +12632,7 @@ std::string wallet2::make_uri(const std::string &address, const std::string &pay
if (!payment_id.empty())
{
crypto::hash pid32;
- crypto::hash8 pid8;
- if (!wallet2::parse_long_payment_id(payment_id, pid32) && !wallet2::parse_short_payment_id(payment_id, pid8))
+ if (!wallet2::parse_long_payment_id(payment_id, pid32))
{
error = "Invalid payment id";
return std::string();
@@ -12677,8 +12726,7 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
return false;
}
crypto::hash hash;
- crypto::hash8 hash8;
- if (!wallet2::parse_long_payment_id(kv[1], hash) && !wallet2::parse_short_payment_id(kv[1], hash8))
+ if (!wallet2::parse_long_payment_id(kv[1], hash))
{
error = "Invalid payment id: " + kv[1];
return false;
@@ -12739,7 +12787,7 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui
height_mid,
height_max
};
- bool r = net_utils::invoke_http_bin("/getblocks_by_height.bin", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_bin("/getblocks_by_height.bin", req, res, rpc_timeout);
if (!r || res.status != CORE_RPC_STATUS_OK)
{
std::ostringstream oss;
@@ -12809,7 +12857,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request req = AUTO_VAL_INIT(req);
cryptonote::COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response res = AUTO_VAL_INIT(res);
m_daemon_rpc_mutex.lock();
- bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, m_http_client, rpc_timeout);
+ bool r = invoke_http_json_rpc("/json_rpc", "get_txpool_backlog", req, res, rpc_timeout);
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "Failed to connect to daemon");
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
@@ -12879,7 +12927,7 @@ uint64_t wallet2::get_segregation_fork_height() const
if (m_segregation_height > 0)
return m_segregation_height;
- if (m_use_dns)
+ if (m_use_dns && !m_offline)
{
// All four MoneroPulse domains have DNSSEC on and valid
static const std::vector<std::string> dns_urls = {
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 39380c9df..921c150cb 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -39,6 +39,7 @@
#include <boost/serialization/deque.hpp>
#include <boost/thread/lock_guard.hpp>
#include <atomic>
+#include <random>
#include "include_base_utils.h"
#include "cryptonote_basic/account.h"
@@ -76,6 +77,30 @@ namespace tools
class wallet2;
class Notify;
+ class gamma_picker
+ {
+ public:
+ uint64_t pick();
+ gamma_picker(const std::vector<uint64_t> &rct_offsets);
+ gamma_picker(const std::vector<uint64_t> &rct_offsets, double shape, double scale);
+
+ private:
+ struct gamma_engine
+ {
+ typedef uint64_t result_type;
+ static constexpr result_type min() { return 0; }
+ static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
+ result_type operator()() { return crypto::rand<result_type>(); }
+ } engine;
+
+private:
+ std::gamma_distribution<double> gamma;
+ const std::vector<uint64_t> &rct_offsets;
+ const uint64_t *begin, *end;
+ uint64_t num_rct_outputs;
+ double average_output_time;
+ };
+
class wallet_keys_unlocker
{
public:
@@ -93,7 +118,7 @@ namespace tools
public:
// Full wallet callbacks
virtual void on_new_block(uint64_t height, const cryptonote::block& block) {}
- virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
+ virtual void on_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index, uint64_t unlock_time) {}
virtual void on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) {}
virtual void on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) {}
@@ -1139,8 +1164,8 @@ namespace tools
bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const;
// Import/Export wallet data
- std::pair<size_t, std::vector<tools::wallet2::transfer_details>> export_outputs() const;
- std::string export_outputs_to_str() const;
+ std::pair<size_t, std::vector<tools::wallet2::transfer_details>> export_outputs(bool all = false) const;
+ std::string export_outputs_to_str(bool all = false) const;
size_t import_outputs(const std::pair<size_t, std::vector<tools::wallet2::transfer_details>> &outputs);
size_t import_outputs_from_str(const std::string &outputs_st);
payment_container export_payments() const;
@@ -1232,19 +1257,22 @@ namespace tools
template<class t_request, class t_response>
inline bool invoke_http_json(const boost::string_ref uri, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET")
{
- boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex);
+ if (m_offline) return false;
+ boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
return epee::net_utils::invoke_http_json(uri, req, res, m_http_client, timeout, http_method);
}
template<class t_request, class t_response>
inline bool invoke_http_bin(const boost::string_ref uri, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET")
{
- boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex);
+ if (m_offline) return false;
+ boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
return epee::net_utils::invoke_http_bin(uri, req, res, m_http_client, timeout, http_method);
}
template<class t_request, class t_response>
inline bool invoke_http_json_rpc(const boost::string_ref uri, const std::string& method_name, const t_request& req, t_response& res, std::chrono::milliseconds timeout = std::chrono::seconds(15), const boost::string_ref http_method = "GET", const std::string& req_id = "0")
{
- boost::lock_guard<boost::mutex> lock(m_daemon_rpc_mutex);
+ if (m_offline) return false;
+ boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
return epee::net_utils::invoke_http_json_rpc(uri, method_name, req, res, m_http_client, timeout, http_method, req_id);
}
@@ -1291,6 +1319,7 @@ namespace tools
uint64_t hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const;
void finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash);
void enable_dns(bool enable) { m_use_dns = enable; }
+ void set_offline(bool offline = true);
private:
/*!
@@ -1422,7 +1451,7 @@ namespace tools
std::atomic<bool> m_run;
- boost::mutex m_daemon_rpc_mutex;
+ boost::recursive_mutex m_daemon_rpc_mutex;
bool m_trusted_daemon;
i_wallet2_callback* m_callback;
@@ -1474,6 +1503,7 @@ namespace tools
std::string m_device_derivation_path;
uint64_t m_device_last_key_image_sync;
bool m_use_dns;
+ bool m_offline;
// 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 71c64d3c1..0e2a34470 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -31,6 +31,7 @@
#include <boost/asio/ip/address.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/preprocessor/stringize.hpp>
#include <cstdint>
#include "include_base_utils.h"
using namespace epee;
@@ -65,11 +66,6 @@ namespace
const command_line::arg_descriptor<bool> arg_restricted = {"restricted-rpc", "Restricts to view-only commands", false};
const command_line::arg_descriptor<std::string> arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"};
const command_line::arg_descriptor<bool> arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false};
- const command_line::arg_descriptor<std::string> arg_rpc_ssl = {"rpc-ssl", tools::wallet2::tr("Enable SSL on wallet RPC connections: enabled|disabled|autodetect"), "autodetect"};
- const command_line::arg_descriptor<std::string> arg_rpc_ssl_private_key = {"rpc-ssl-private-key", tools::wallet2::tr("Path to a PEM format private key"), ""};
- const command_line::arg_descriptor<std::string> arg_rpc_ssl_certificate = {"rpc-ssl-certificate", tools::wallet2::tr("Path to a PEM format certificate"), ""};
- const command_line::arg_descriptor<std::string> arg_rpc_ssl_ca_certificates = {"rpc-ssl-ca-certificates", tools::wallet2::tr("Path to file containing concatenated PEM format certificate(s) to replace system CA(s).")};
- const command_line::arg_descriptor<std::vector<std::string>> arg_rpc_ssl_allowed_fingerprints = {"rpc-ssl-allowed-fingerprints", tools::wallet2::tr("List of certificate fingerprints to allow")};
constexpr const char default_rpc_username[] = "monero";
@@ -86,12 +82,10 @@ namespace
void set_confirmations(tools::wallet_rpc::transfer_entry &entry, uint64_t blockchain_height, uint64_t block_reward)
{
if (entry.height >= blockchain_height || (entry.height == 0 && (!strcmp(entry.type.c_str(), "pending") || !strcmp(entry.type.c_str(), "pool"))))
- {
entry.confirmations = 0;
- entry.suggested_confirmations_threshold = 0;
- return;
- }
- entry.confirmations = blockchain_height - entry.height;
+ else
+ entry.confirmations = blockchain_height - entry.height;
+
if (block_reward == 0)
entry.suggested_confirmations_threshold = 0;
else
@@ -245,37 +239,6 @@ namespace tools
assert(bool(http_login));
} // end auth enabled
- auto rpc_ssl_private_key = command_line::get_arg(vm, arg_rpc_ssl_private_key);
- auto rpc_ssl_certificate = command_line::get_arg(vm, arg_rpc_ssl_certificate);
- auto rpc_ssl_ca_file = command_line::get_arg(vm, arg_rpc_ssl_ca_certificates);
- auto rpc_ssl_allowed_fingerprints = command_line::get_arg(vm, arg_rpc_ssl_allowed_fingerprints);
- auto rpc_ssl = command_line::get_arg(vm, arg_rpc_ssl);
- epee::net_utils::ssl_options_t rpc_ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_enabled;
-
- if (!rpc_ssl_ca_file.empty() || !rpc_ssl_allowed_fingerprints.empty())
- {
- std::vector<std::vector<uint8_t>> allowed_fingerprints{ rpc_ssl_allowed_fingerprints.size() };
- std::transform(rpc_ssl_allowed_fingerprints.begin(), rpc_ssl_allowed_fingerprints.end(), allowed_fingerprints.begin(), epee::from_hex::vector);
-
- rpc_ssl_options = epee::net_utils::ssl_options_t{
- std::move(allowed_fingerprints), std::move(rpc_ssl_ca_file)
- };
- }
-
- // user specified CA file or fingeprints implies enabled SSL by default
- if (rpc_ssl_options.verification != epee::net_utils::ssl_verification_t::user_certificates || !command_line::is_arg_defaulted(vm, arg_rpc_ssl))
- {
- if (!epee::net_utils::ssl_support_from_string(rpc_ssl_options.support, rpc_ssl))
- {
- MERROR("Invalid argument for " << std::string(arg_rpc_ssl.name));
- return false;
- }
- }
-
- rpc_ssl_options.auth = epee::net_utils::ssl_authentication_t{
- std::move(rpc_ssl_private_key), std::move(rpc_ssl_certificate)
- };
-
m_auto_refresh_period = DEFAULT_AUTO_REFRESH_PERIOD;
m_last_auto_refresh_time = boost::posix_time::min_date_time;
@@ -285,7 +248,7 @@ namespace tools
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),
- std::move(rpc_ssl_options)
+ std::move(rpc_config->ssl_options)
);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -387,7 +350,7 @@ namespace tools
entry.destinations.push_back(wallet_rpc::transfer_destination());
wallet_rpc::transfer_destination &td = entry.destinations.back();
td.amount = d.amount;
- td.address = get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr);
+ td.address = d.original.empty() ? get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) : d.original;
}
entry.type = "out";
@@ -412,6 +375,14 @@ namespace tools
entry.amount = pd.m_amount_in - pd.m_change - entry.fee;
entry.unlock_time = pd.m_tx.unlock_time;
entry.note = m_wallet->get_tx_note(txid);
+
+ for (const auto &d: pd.m_dests) {
+ entry.destinations.push_back(wallet_rpc::transfer_destination());
+ wallet_rpc::transfer_destination &td = entry.destinations.back();
+ td.amount = d.amount;
+ td.address = d.original.empty() ? get_account_address_as_str(m_wallet->nettype(), d.is_subaddress, d.addr) : d.original;
+ }
+
entry.type = is_failed ? "failed" : "pending";
entry.subaddr_index = { pd.m_subaddr_account, 0 };
for (uint32_t i: pd.m_subaddr_indices)
@@ -1774,6 +1745,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
{
@@ -1842,11 +1818,7 @@ namespace tools
{
if (req.account_index != td.m_subaddr_index.major || (!req.subaddr_indices.empty() && req.subaddr_indices.count(td.m_subaddr_index.minor) == 0))
continue;
- if (!transfers_found)
- {
- transfers_found = true;
- }
- auto txBlob = t_serializable_object_to_blob(td.m_tx);
+ transfers_found = true;
wallet_rpc::transfer_details rpc_transfers;
rpc_transfers.amount = td.amount();
rpc_transfers.spent = td.m_spent;
@@ -1854,6 +1826,9 @@ namespace tools
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.subaddr_index = {td.m_subaddr_index.major, td.m_subaddr_index.minor};
rpc_transfers.key_image = td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
+ rpc_transfers.block_height = td.m_block_height;
+ rpc_transfers.frozen = td.m_frozen;
+ rpc_transfers.unlocked = m_wallet->is_transfer_unlocked(td);
res.transfers.push_back(rpc_transfers);
}
}
@@ -2581,7 +2556,7 @@ namespace tools
try
{
- res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str());
+ res.outputs_data_hex = epee::string_tools::buff_to_hex_nodelimer(m_wallet->export_outputs_to_str(req.all));
}
catch (const std::exception &e)
{
@@ -2808,20 +2783,20 @@ namespace tools
}
crypto::hash long_payment_id;
- crypto::hash8 short_payment_id;
if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
{
if (!wallet2::parse_short_payment_id(req.payment_id, info.payment_id))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
- er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 16 or 64 character string";
+ er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string";
return false;
}
else
{
- memcpy(payment_id.data, info.payment_id.data, 8);
- memset(payment_id.data + 8, 0, 24);
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
+ er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
+ return false;
}
}
}
@@ -3111,6 +3086,18 @@ namespace tools
er.message = "Invalid filename";
return false;
}
+ if (m_wallet && req.autosave_current)
+ {
+ try
+ {
+ m_wallet->store();
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ }
std::string wallet_file = m_wallet_dir + "/" + req.filename;
{
po::options_description desc("dummy");
@@ -3141,18 +3128,7 @@ namespace tools
}
if (m_wallet)
- {
- try
- {
- m_wallet->store();
- }
- catch (const std::exception& e)
- {
- handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
- return false;
- }
delete m_wallet;
- }
m_wallet = wal.release();
return true;
}
@@ -3161,14 +3137,17 @@ namespace tools
{
if (!m_wallet) return not_open(er);
- try
+ if (req.autosave_current)
{
- m_wallet->store();
- }
- catch (const std::exception& e)
- {
- handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
- return false;
+ try
+ {
+ m_wallet->store();
+ }
+ catch (const std::exception& e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
}
delete m_wallet;
m_wallet = NULL;
@@ -3385,6 +3364,20 @@ namespace tools
return false;
}
+ if (m_wallet && req.autosave_current)
+ {
+ try
+ {
+ if (!wallet_file.empty())
+ m_wallet->store();
+ }
+ catch (const std::exception &e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ }
+
try
{
if (!req.spendkey.empty())
@@ -3433,19 +3426,7 @@ namespace tools
}
if (m_wallet)
- {
- try
- {
- if (!wallet_file.empty())
- m_wallet->store();
- }
- catch (const std::exception &e)
- {
- handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
- return false;
- }
delete m_wallet;
- }
m_wallet = wal.release();
res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
return true;
@@ -3511,6 +3492,18 @@ namespace tools
return false;
}
}
+ if (m_wallet && req.autosave_current)
+ {
+ try
+ {
+ m_wallet->store();
+ }
+ catch (const std::exception &e)
+ {
+ handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+ return false;
+ }
+ }
// process seed_offset if given
{
@@ -3621,18 +3614,7 @@ namespace tools
}
if (m_wallet)
- {
- try
- {
- m_wallet->store();
- }
- catch (const std::exception &e)
- {
- handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
- return false;
- }
delete m_wallet;
- }
m_wallet = wal.release();
res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
res.info = "Wallet has been restored successfully.";
@@ -4065,9 +4047,10 @@ namespace tools
{ cryptonote::TESTNET, "testnet" },
{ cryptonote::STAGENET, "stagenet" },
};
+ if (!req.any_net_type && !m_wallet) return not_open(er);
for (const auto &net_type: net_types)
{
- if (!req.any_net_type && net_type.type != m_wallet->nettype())
+ if (!req.any_net_type && (!m_wallet || net_type.type != m_wallet->nettype()))
continue;
if (req.allow_openalias)
{
@@ -4145,10 +4128,15 @@ namespace tools
std::move(req.ssl_private_key_path), std::move(req.ssl_certificate_path)
};
- if (ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled && !ssl_options.has_strong_verification(boost::string_ref{}))
+ const bool verification_required =
+ ssl_options.verification != epee::net_utils::ssl_verification_t::none &&
+ ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled;
+
+ if (verification_required && !ssl_options.has_strong_verification(boost::string_ref{}))
{
er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
er.message = "SSL is enabled but no user certificate or fingerprints were provided";
+ return false;
}
if (!m_wallet->set_daemon(req.address, boost::none, req.trusted, std::move(ssl_options)))
@@ -4160,6 +4148,39 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
+ if (req.level < 0 || req.level > 4)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_INVALID_LOG_LEVEL;
+ er.message = "Error: log level not valid";
+ return false;
+ }
+ mlog_set_log_level(req.level);
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
+ bool wallet_rpc_server::on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+ {
+ if (m_restricted)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_DENIED;
+ er.message = "Command unavailable in restricted mode.";
+ return false;
+ }
+
+ mlog_set_log(req.categories.c_str());
+ res.categories = mlog_get_categories();
+ return true;
+ }
+ //------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
res.version = WALLET_RPC_VERSION;
@@ -4356,11 +4377,6 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_from_json);
command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password);
- command_line::add_arg(desc_params, arg_rpc_ssl);
- command_line::add_arg(desc_params, arg_rpc_ssl_private_key);
- command_line::add_arg(desc_params, arg_rpc_ssl_certificate);
- command_line::add_arg(desc_params, arg_rpc_ssl_ca_certificates);
- command_line::add_arg(desc_params, arg_rpc_ssl_allowed_fingerprints);
daemonizer::init_options(hidden_options, desc_params);
desc_params.add(hidden_options);
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index 7d2272dd0..a327ed908 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -151,6 +151,8 @@ namespace tools
MAP_JON_RPC_WE("submit_multisig", on_submit_multisig, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG)
MAP_JON_RPC_WE("validate_address", on_validate_address, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS)
MAP_JON_RPC_WE("set_daemon", on_set_daemon, wallet_rpc::COMMAND_RPC_SET_DAEMON)
+ MAP_JON_RPC_WE("set_log_level", on_set_log_level, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL)
+ MAP_JON_RPC_WE("set_log_categories", on_set_log_categories, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES)
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
END_JSON_RPC_MAP()
END_URI_MAP2()
@@ -234,6 +236,8 @@ namespace tools
bool on_submit_multisig(const wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_SUBMIT_MULTISIG::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_validate_address(const wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_VALIDATE_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_set_daemon(const wallet_rpc::COMMAND_RPC_SET_DAEMON::request& req, wallet_rpc::COMMAND_RPC_SET_DAEMON::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_set_log_level(const wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_LEVEL::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+ bool on_set_log_categories(const wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::request& req, wallet_rpc::COMMAND_RPC_SET_LOG_CATEGORIES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
//json rpc v2
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index bb360ae01..4504ac752 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -47,7 +47,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
-#define WALLET_RPC_VERSION_MINOR 9
+#define WALLET_RPC_VERSION_MINOR 13
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@@ -982,6 +982,9 @@ namespace wallet_rpc
std::string tx_hash;
cryptonote::subaddress_index subaddr_index;
std::string key_image;
+ uint64_t block_height;
+ bool frozen;
+ bool unlocked;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
@@ -990,6 +993,9 @@ namespace wallet_rpc
KV_SERIALIZE(tx_hash)
KV_SERIALIZE(subaddr_index)
KV_SERIALIZE(key_image)
+ KV_SERIALIZE(block_height)
+ KV_SERIALIZE(frozen)
+ KV_SERIALIZE(unlocked)
END_KV_SERIALIZE_MAP()
};
@@ -1626,7 +1632,10 @@ namespace wallet_rpc
{
struct request_t
{
+ bool all;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(all)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -2043,10 +2052,12 @@ namespace wallet_rpc
{
std::string filename;
std::string password;
+ bool autosave_current;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(filename)
KV_SERIALIZE(password)
+ KV_SERIALIZE_OPT(autosave_current, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -2063,7 +2074,10 @@ namespace wallet_rpc
{
struct request_t
{
+ bool autosave_current;
+
BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE_OPT(autosave_current, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -2108,6 +2122,7 @@ namespace wallet_rpc
std::string spendkey;
std::string viewkey;
std::string password;
+ bool autosave_current;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
@@ -2116,6 +2131,7 @@ namespace wallet_rpc
KV_SERIALIZE(spendkey)
KV_SERIALIZE(viewkey)
KV_SERIALIZE(password)
+ KV_SERIALIZE_OPT(autosave_current, true)
END_KV_SERIALIZE_MAP()
};
@@ -2141,6 +2157,7 @@ namespace wallet_rpc
std::string seed_offset;
std::string password;
std::string language;
+ bool autosave_current;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_OPT(restore_height, (uint64_t)0)
@@ -2149,6 +2166,7 @@ namespace wallet_rpc
KV_SERIALIZE(seed_offset)
KV_SERIALIZE(password)
KV_SERIALIZE(language)
+ KV_SERIALIZE_OPT(autosave_current, true)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -2477,5 +2495,48 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
+ struct COMMAND_RPC_SET_LOG_LEVEL
+ {
+ struct request_t
+ {
+ int8_t level;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(level)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
+ struct COMMAND_RPC_SET_LOG_CATEGORIES
+ {
+ struct request_t
+ {
+ std::string categories;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(categories)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<request_t> request;
+
+ struct response_t
+ {
+ std::string categories;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(categories)
+ END_KV_SERIALIZE_MAP()
+ };
+ typedef epee::misc_utils::struct_init<response_t> response;
+ };
+
}
}
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index 440a58a47..011d146d4 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -74,3 +74,4 @@
#define WALLET_RPC_ERROR_CODE_SIGNED_SUBMISSION -41
#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