diff options
Diffstat (limited to 'src')
38 files changed, 701 insertions, 379 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d83242a3c..e473c2984 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,10 @@ if(NOT IOS) add_subdirectory(blockchain_utilities) endif() +if(CMAKE_BUILD_TYPE STREQUAL Debug) + add_subdirectory(debug_utilities) +endif() + if(PER_BLOCK_CHECKPOINT) add_subdirectory(blocks) endif() diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 628c037b3..ffdaad4af 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -67,11 +67,6 @@ monero_private_headers(blockchain_export ${blockchain_export_private_headers}) -set(cn_deserialize_sources - cn_deserialize.cpp - ) - - monero_add_executable(blockchain_import ${blockchain_import_sources} ${blockchain_import_private_headers} @@ -122,21 +117,3 @@ set_property(TARGET blockchain_export PROPERTY OUTPUT_NAME "monero-blockchain-export") -monero_add_executable(cn_deserialize - ${cn_deserialize_sources} - ${cn_deserialize_private_headers}) - -target_link_libraries(cn_deserialize - LINK_PRIVATE - cryptonote_core - blockchain_db - p2p - epee - ${CMAKE_THREAD_LIBS_INIT}) - -add_dependencies(cn_deserialize - version) -set_property(TARGET cn_deserialize - PROPERTY - OUTPUT_NAME "monero-utils-deserialize") - diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h index 56662ff24..bc8e05800 100644 --- a/src/common/perf_timer.h +++ b/src/common/perf_timer.h @@ -46,9 +46,9 @@ extern __thread std::vector<PerformanceTimer*> *performance_timers; class PerformanceTimer { public: - PerformanceTimer(const std::string &s, el::Level l = el::Level::Debug): name(s), level(l), started(false) + PerformanceTimer(const std::string &s, uint64_t unit, el::Level l = el::Level::Debug): name(s), unit(unit), level(l), started(false) { - ticks = epee::misc_utils::get_tick_count(); + ticks = epee::misc_utils::get_ns_count(); if (!performance_timers) { MLOG(level, "PERF ----------"); @@ -69,9 +69,9 @@ public: ~PerformanceTimer() { performance_timers->pop_back(); - ticks = epee::misc_utils::get_tick_count() - ticks; + ticks = epee::misc_utils::get_ns_count() - ticks; char s[12]; - snprintf(s, sizeof(s), "%8llu ", (unsigned long long)ticks); + snprintf(s, sizeof(s), "%8llu ", (unsigned long long)ticks / (1000000000 / unit)); MLOG(level, "PERF " << s << std::string(performance_timers->size() * 2, ' ') << " " << name); if (performance_timers->empty()) { @@ -82,6 +82,7 @@ public: private: std::string name; + uint64_t unit; el::Level level; uint64_t ticks; bool started; @@ -89,7 +90,9 @@ private: void set_performance_timer_log_level(el::Level level); -#define PERF_TIMER(name) tools::PerformanceTimer pt_##name(#name, tools::performance_timer_log_level) -#define PERF_TIMER_L(name, l) tools::PerformanceTimer pt_##name(#name, l) +#define PERF_TIMER_UNIT(name, unit) tools::PerformanceTimer pt_##name(#name, unit, tools::performance_timer_log_level) +#define PERF_TIMER_UNIT_L(name, unit, l) tools::PerformanceTimer pt_##name(#name, unit, l) +#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000) +#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l) } diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index e31f8f0b2..8fc98d2b0 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -31,6 +31,14 @@ #include "misc_log_ex.h" #include <iostream> +#ifdef HAVE_READLINE + #include "readline_buffer.h" + #define PAUSE_READLINE() \ + rdln::suspend_readline pause_readline; +#else + #define PAUSE_READLINE() +#endif + namespace tools { @@ -99,6 +107,7 @@ public: } else { + PAUSE_READLINE(); set_console_color(m_color, m_bright); std::cout << m_oss.str(); epee::reset_console_color(); @@ -108,9 +117,9 @@ public: } }; -inline scoped_message_writer success_msg_writer() +inline scoped_message_writer success_msg_writer(bool color = true) { - return scoped_message_writer(epee::console_color_green, false, std::string(), el::Level::Info); + return scoped_message_writer(color ? epee::console_color_green : epee::console_color_default, false, std::string(), el::Level::Info); } inline scoped_message_writer msg_writer(epee::console_colors color = epee::console_color_default) diff --git a/src/cryptonote_basic/connection_context.h b/src/cryptonote_basic/connection_context.h index ff56b73d7..06f66120b 100644 --- a/src/cryptonote_basic/connection_context.h +++ b/src/cryptonote_basic/connection_context.h @@ -39,7 +39,8 @@ namespace cryptonote struct cryptonote_connection_context: public epee::net_utils::connection_context_base { - cryptonote_connection_context(): m_state(state_befor_handshake), m_remote_blockchain_height(0), m_last_response_height(0) {} + cryptonote_connection_context(): m_state(state_befor_handshake), m_remote_blockchain_height(0), m_last_response_height(0), + m_last_known_hash(cryptonote::null_hash) {} enum state { @@ -56,6 +57,7 @@ namespace cryptonote uint64_t m_last_response_height; boost::posix_time::ptime m_last_request_time; epee::copyable_atomic m_callback_request_count; //in debug purpose: problem with double callback rise + crypto::hash m_last_known_hash; //size_t m_score; TODO: add score calculations }; diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 9838fcb47..7a2259b32 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -69,7 +69,7 @@ namespace cryptonote { namespace { - std::string return_first_address(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid) + inline std::string return_first_address(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid) { if (addresses.empty()) return {}; diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index aed6cb289..aeb1c030d 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -42,9 +42,9 @@ namespace cryptonote /** * @brief checks if a hash fits the given difficulty * - * The hash passes if (hash * difficulty) < 2^192. + * The hash passes if (hash * difficulty) < 2^256. * Phrased differently, if (hash * difficulty) fits without overflow into - * the least significant 192 bits of the 256 bit multiplication result. + * the least significant 256 bits of the 320 bit multiplication result. * * @param hash the hash to check * @param difficulty the difficulty to check against diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index ef4b1e6be..3c5811d61 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -382,7 +382,7 @@ namespace cryptonote boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled()); + start(m_mine_address, m_threads_total, attrs, get_is_background_mining_enabled(), get_ignore_battery()); } } //----------------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 8dbf1b53b..fd45c0822 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -89,7 +89,7 @@ #define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing -#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading +#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading #define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block #define CRYPTONOTE_MEMPOOL_TX_LIVETIME 86400 //seconds, one day diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 404321e0f..61ddff3d0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -125,8 +125,8 @@ static const uint64_t testnet_hard_fork_version_1_till = 624633; //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : - m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_is_in_checkpoint_zone(false), - m_is_blockchain_storing(false), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false) + m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), + m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_blocks_per_sync(1), m_db_sync_mode(db_async), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_cancel(false) { LOG_PRINT_L3("Blockchain::" << __func__); } @@ -1353,7 +1353,6 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id } // Check the block's hash against the difficulty target for its alt chain - m_is_in_checkpoint_zone = false; difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); crypto::hash proof_of_work = null_hash; @@ -1428,7 +1427,9 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id { //block orphaned bvc.m_marked_as_orphaned = true; - MERROR_VER("Block recognized as orphaned and rejected, id = " << id); + MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height + << ", parent in alt " << (it_prev != m_alternative_chains.end()) << ", parent in main " << parent_in_main + << " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")"); } return true; @@ -2499,7 +2500,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, assert(it != m_check_txin_table.end()); } - uint64_t t_t1 = 0; std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size()); std::vector < uint64_t > results; results.resize(tx.vin.size(), 0); @@ -2633,7 +2633,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, if (failed) { - MERROR_VER("Failed to check ring signatures!, t_loop: " << t_t1); + MERROR_VER("Failed to check ring signatures!"); return false; } } @@ -2782,12 +2782,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, //------------------------------------------------------------------ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const crypto::key_image &key_image, const std::vector<rct::ctkey> &pubkeys, const std::vector<crypto::signature>& sig, uint64_t &result) { - if (m_is_in_checkpoint_zone) - { - result = true; - return; - } - std::vector<const crypto::public_key *> p_output_keys; for (auto &key : pubkeys) { @@ -3883,17 +3877,17 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e offset_map[in_to_key.amount].push_back(offset); } - - // sort and remove duplicate absolute_offsets in offset_map - for (auto &offsets : offset_map) - { - std::sort(offsets.second.begin(), offsets.second.end()); - auto last = std::unique(offsets.second.begin(), offsets.second.end()); - offsets.second.erase(last, offsets.second.end()); - } } } + // sort and remove duplicate absolute_offsets in offset_map + for (auto &offsets : offset_map) + { + std::sort(offsets.second.begin(), offsets.second.end()); + auto last = std::unique(offsets.second.begin(), offsets.second.end()); + offsets.second.erase(last, offsets.second.end()); + } + // [output] stores all transactions for each tx_out_index::hash found std::vector<std::unordered_map<crypto::hash, cryptonote::transaction>> transactions(amounts.size()); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index aa61dc034..7137f50a7 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -585,7 +585,7 @@ namespace cryptonote * * @return true if Blockchain is having the chain stored currently, else false */ - bool is_storing_blockchain()const{return m_is_blockchain_storing;} + bool is_storing_blockchain()const{return false;} /** * @brief gets the difficulty of the block with a given height @@ -943,8 +943,6 @@ namespace cryptonote checkpoints m_checkpoints; - std::atomic<bool> m_is_in_checkpoint_zone; - std::atomic<bool> m_is_blockchain_storing; bool m_enforce_dns_checkpoints; HardFork *m_hardfork; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 13e5badd1..62c536ab8 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1232,6 +1232,16 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + uint8_t core::get_ideal_hard_fork_version(uint64_t height) const + { + return get_blockchain_storage().get_ideal_hard_fork_version(height); + } + //----------------------------------------------------------------------------------------------- + uint8_t core::get_hard_fork_version(uint64_t height) const + { + return get_blockchain_storage().get_hard_fork_version(height); + } + //----------------------------------------------------------------------------------------------- bool core::check_updates() { static const char software[] = "monero"; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 171c3cb98..63d3cd16d 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -621,6 +621,20 @@ namespace cryptonote uint64_t get_target_blockchain_height() const; /** + * @brief return the ideal hard fork version for a given block height + * + * @return what it says above + */ + uint8_t get_ideal_hard_fork_version(uint64_t height) const; + + /** + * @brief return the hard fork version for a given block height + * + * @return what it says above + */ + uint8_t get_hard_fork_version(uint64_t height) const; + + /** * @brief gets start_time * */ diff --git a/src/cryptonote_protocol/block_queue.cpp b/src/cryptonote_protocol/block_queue.cpp index 583c3abf4..4f760582b 100644 --- a/src/cryptonote_protocol/block_queue.cpp +++ b/src/cryptonote_protocol/block_queue.cpp @@ -118,25 +118,6 @@ void block_queue::remove_spans(const boost::uuids::uuid &connection_id, uint64_t } } -void block_queue::mark_last_block(uint64_t last_block_height) -{ - boost::unique_lock<boost::recursive_mutex> lock(mutex); - if (!blocks.empty() && is_blockchain_placeholder(*blocks.begin())) - blocks.erase(*blocks.begin()); - for (block_map::iterator i = blocks.begin(); i != blocks.end(); ) - { - block_map::iterator j = i++; - if (j->start_block_height + j->nblocks - 1 <= last_block_height) - { - blocks.erase(j); - } - } - - // mark the current state of the db (for a fresh db, it's just the genesis block) - add_blocks(0, last_block_height + 1, boost::uuids::nil_uuid()); - MDEBUG("last blocked marked at " << last_block_height); -} - uint64_t block_queue::get_max_block_height() const { boost::unique_lock<boost::recursive_mutex> lock(mutex); @@ -171,7 +152,19 @@ std::string block_queue::get_overview() const return s; } -std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time) +bool block_queue::requested(const crypto::hash &hash) const +{ + boost::unique_lock<boost::recursive_mutex> lock(mutex); + for (const auto &span: blocks) + { + for (const auto &h: span.hashes) + if (h == hash) + return true; + } + return false; +} + +std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list<crypto::hash> &block_hashes, boost::posix_time::ptime time) { boost::unique_lock<boost::recursive_mutex> lock(mutex); @@ -181,71 +174,27 @@ std::pair<uint64_t, uint64_t> block_queue::reserve_span(uint64_t first_block_hei return std::make_pair(0, 0); } - uint64_t max_block_height = get_max_block_height(); - if (last_block_height > max_block_height) - max_block_height = last_block_height; - if (max_block_height == 0) + uint64_t span_start_height = last_block_height - block_hashes.size() + 1; + std::list<crypto::hash>::const_iterator i = block_hashes.begin(); + while (i != block_hashes.end() && requested(*i)) { - MDEBUG("reserve_span: max_block_height is 0"); - return std::make_pair(first_block_height, std::min(last_block_height - first_block_height + 1, max_blocks)); - } - - uint64_t base = 0, last_placeholder_block = 0; - bool has_placeholder = false; - block_map::const_iterator i = blocks.begin(); - if (i != blocks.end() && is_blockchain_placeholder(*i)) - { - base = i->start_block_height + i->nblocks; - last_placeholder_block = base - 1; - has_placeholder = true; ++i; - for (block_map::const_iterator j = i; j != blocks.end(); ++j) - { - if (j->start_block_height < base) - base = j->start_block_height; - } + ++span_start_height; } - if (base > first_block_height) - base = first_block_height; - - CHECK_AND_ASSERT_MES (base <= max_block_height + 1, std::make_pair(0, 0), "Blockchain placeholder larger than max block height"); - std::vector<uint8_t> bitmap(max_block_height + 1 - base, 0); - MDEBUG("base " << base << ", last_placeholder_block " << (has_placeholder ? std::to_string(last_placeholder_block) : "none") << ", first_block_height " << first_block_height); - if (has_placeholder && last_placeholder_block >= base) - memset(bitmap.data(), 1, last_placeholder_block + 1 - base); - while (i != blocks.end()) + uint64_t span_length = 0; + std::list<crypto::hash> hashes; + while (i != block_hashes.end() && span_length < max_blocks) { - CHECK_AND_ASSERT_MES (i->start_block_height >= base, std::make_pair(0, 0), "Span starts before blochckain placeholder"); - memset(bitmap.data() + i->start_block_height - base, 1, i->nblocks); + hashes.push_back(*i); ++i; + ++span_length; } - - const uint8_t *ptr = (const uint8_t*)memchr(bitmap.data() + first_block_height - base, 0, bitmap.size() - (first_block_height - base)); - if (!ptr) - { - MDEBUG("reserve_span: 0 not found in bitmap: " << first_block_height << " " << bitmap.size()); - print(); - return std::make_pair(0, 0); - } - uint64_t start_block_height = ptr - bitmap.data() + base; - if (start_block_height > last_block_height) - { - MDEBUG("reserve_span: start_block_height > last_block_height: " << start_block_height << " < " << last_block_height); - return std::make_pair(0, 0); - } - if (start_block_height + max_blocks - 1 < first_block_height) - { - MDEBUG("reserve_span: start_block_height + max_blocks - 1 < first_block_height: " << start_block_height << " + " << max_blocks << " - 1 < " << first_block_height); + if (span_length == 0) return std::make_pair(0, 0); - } - - uint64_t nblocks = 1; - while (start_block_height + nblocks <= last_block_height && nblocks < max_blocks && bitmap[start_block_height + nblocks - base] == 0) - ++nblocks; - - MDEBUG("Reserving span " << start_block_height << " - " << (start_block_height + nblocks - 1) << " for " << connection_id); - add_blocks(start_block_height, nblocks, connection_id, time); - return std::make_pair(start_block_height, nblocks); + MDEBUG("Reserving span " << span_start_height << " - " << (span_start_height + span_length - 1) << " for " << connection_id); + add_blocks(span_start_height, span_length, connection_id, time); + set_span_hashes(span_start_height, connection_id, hashes); + return std::make_pair(span_start_height, span_length); } bool block_queue::is_blockchain_placeholder(const span &span) const @@ -366,13 +315,15 @@ size_t block_queue::get_num_filled_spans() const return size; } -crypto::hash block_queue::get_last_known_hash() const +crypto::hash block_queue::get_last_known_hash(const boost::uuids::uuid &connection_id) const { boost::unique_lock<boost::recursive_mutex> lock(mutex); crypto::hash hash = cryptonote::null_hash; uint64_t highest_height = 0; for (const auto &span: blocks) { + if (span.connection_id != connection_id) + continue; uint64_t h = span.start_block_height + span.nblocks - 1; if (h > highest_height && span.hashes.size() == span.nblocks) { diff --git a/src/cryptonote_protocol/block_queue.h b/src/cryptonote_protocol/block_queue.h index c75ebc0b9..9a211ac47 100644 --- a/src/cryptonote_protocol/block_queue.h +++ b/src/cryptonote_protocol/block_queue.h @@ -73,11 +73,10 @@ namespace cryptonote void flush_stale_spans(const std::set<boost::uuids::uuid> &live_connections); void remove_span(uint64_t start_block_height); void remove_spans(const boost::uuids::uuid &connection_id, uint64_t start_block_height); - void mark_last_block(uint64_t last_block_height); uint64_t get_max_block_height() const; void print() const; std::string get_overview() const; - std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); + std::pair<uint64_t, uint64_t> reserve_span(uint64_t first_block_height, uint64_t last_block_height, uint64_t max_blocks, const boost::uuids::uuid &connection_id, const std::list<crypto::hash> &block_hashes, boost::posix_time::ptime time = boost::posix_time::microsec_clock::universal_time()); bool is_blockchain_placeholder(const span &span) const; std::pair<uint64_t, uint64_t> get_start_gap_span() const; std::pair<uint64_t, uint64_t> get_next_span_if_scheduled(std::list<crypto::hash> &hashes, boost::uuids::uuid &connection_id, boost::posix_time::ptime &time) const; @@ -86,9 +85,10 @@ namespace cryptonote size_t get_data_size() const; size_t get_num_filled_spans_prefix() const; size_t get_num_filled_spans() const; - crypto::hash get_last_known_hash() const; + crypto::hash get_last_known_hash(const boost::uuids::uuid &connection_id) const; float get_speed(const boost::uuids::uuid &connection_id) const; bool foreach(std::function<bool(const span&)> f, bool include_blockchain_placeholder = false) const; + bool requested(const crypto::hash &hash) const; private: block_map blocks; diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index 37b503436..6e6c83f04 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -76,6 +76,8 @@ namespace cryptonote boost::uuids::uuid connection_id; + uint64_t height; + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(incoming) KV_SERIALIZE(localhost) @@ -97,6 +99,7 @@ namespace cryptonote KV_SERIALIZE(current_upload) KV_SERIALIZE(support_flags) KV_SERIALIZE_VAL_POD_AS_BLOB(connection_id) + KV_SERIALIZE(height) END_KV_SERIALIZE_MAP() }; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index dcee03f66..f30aebf3a 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -130,6 +130,8 @@ namespace cryptonote size_t get_synchronizing_connections_count(); bool on_connection_synchronized(); bool should_download_next_span(cryptonote_connection_context& context) const; + void drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans); + t_core& m_core; nodetool::p2p_endpoint_stub<connection_context> m_p2p_stub; diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 3e3bb83d0..5b3b059a4 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -209,10 +209,10 @@ namespace cryptonote cnx.support_flags = support_flags; cnx.recv_count = cntxt.m_recv_cnt; - cnx.recv_idle_time = timestamp - cntxt.m_last_recv; + cnx.recv_idle_time = timestamp - std::max(cntxt.m_started, cntxt.m_last_recv); cnx.send_count = cntxt.m_send_cnt; - cnx.send_idle_time = timestamp - cntxt.m_last_send; + cnx.send_idle_time = timestamp - std::max(cntxt.m_started, cntxt.m_last_send); cnx.state = get_protocol_state_string(cntxt.m_state); @@ -239,6 +239,8 @@ namespace cryptonote cnx.connection_id = cntxt.m_connection_id; + cnx.height = cntxt.m_remote_blockchain_height; + connections.push_back(cnx); return true; @@ -257,13 +259,15 @@ namespace cryptonote return true; // from v6, if the peer advertises a top block version, reject if it's not what it should be (will only work if no voting) - const uint8_t version = m_core.get_blockchain_storage().get_ideal_hard_fork_version(hshd.current_height - 1); + const uint8_t version = m_core.get_ideal_hard_fork_version(hshd.current_height - 1); if (version >= 6 && version != hshd.top_version) { - LOG_DEBUG_CC(context, "Ignoring due to wrong top version (" << hshd.top_version << ", expected " << version); + LOG_DEBUG_CC(context, "Ignoring due to wrong top version " << (unsigned)hshd.top_version << ", expected " << (unsigned)version); return false; } + context.m_remote_blockchain_height = hshd.current_height; + uint64_t target = m_core.get_target_blockchain_height(); if (target == 0) target = m_core.get_current_blockchain_height(); @@ -293,7 +297,6 @@ namespace cryptonote } LOG_PRINT_L1("Remote blockchain height: " << hshd.current_height << ", id: " << hshd.top_id); context.m_state = cryptonote_connection_context::state_synchronizing; - context.m_remote_blockchain_height = hshd.current_height; //let the socket to send response to handshake, but request callback, to let send request data after response LOG_PRINT_CCONTEXT_L2("requesting callback"); ++context.m_callback_request_count; @@ -305,7 +308,7 @@ namespace cryptonote bool t_cryptonote_protocol_handler<t_core>::get_payload_sync_data(CORE_SYNC_DATA& hshd) { m_core.get_blockchain_top(hshd.current_height, hshd.top_id); - hshd.top_version = m_core.get_blockchain_storage().get_hard_fork_version(hshd.current_height); + hshd.top_version = m_core.get_hard_fork_version(hshd.current_height); hshd.current_height +=1; return true; } @@ -341,10 +344,9 @@ namespace cryptonote if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); - m_p2p->drop_connection(context); + drop_connection(context, false, false); m_core.cleanup_handle_incoming_blocks(); m_core.resume_mine(); - m_block_queue.flush_spans(context.m_connection_id); return 1; } } @@ -356,9 +358,7 @@ namespace cryptonote if(bvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, true, false); return 1; } if(bvc.m_added_to_main_chain) @@ -411,8 +411,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -445,8 +444,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -460,8 +458,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -485,8 +482,7 @@ namespace cryptonote << "dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -503,8 +499,7 @@ namespace cryptonote if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true, false) || tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -526,8 +521,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -547,8 +541,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); m_core.resume_mine(); return 1; } @@ -625,9 +618,7 @@ namespace cryptonote if( bvc.m_verifivation_failed ) { LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, true, false); return 1; } if( bvc.m_added_to_main_chain ) @@ -660,8 +651,7 @@ namespace cryptonote ); m_core.resume_mine(); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -681,8 +671,7 @@ namespace cryptonote if (!m_core.get_block_by_hash(arg.block_hash, b)) { LOG_ERROR_CCONTEXT("failed to find block: " << arg.block_hash << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -709,8 +698,7 @@ namespace cryptonote << ", dropping connection" ); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } } @@ -721,16 +709,14 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " << "failed to get requested transactions"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if (!missed.empty() || txs.size() != txids.size()) { LOG_ERROR_CCONTEXT("Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX, " << missed.size() << " requested transactions not found" << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -773,8 +759,7 @@ namespace cryptonote if(tvc.m_verifivation_failed) { LOG_PRINT_CCONTEXT_L1("Tx verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if(tvc.m_should_be_relayed) @@ -800,8 +785,7 @@ namespace cryptonote if(!m_core.handle_get_objects(arg, rsp, context)) { LOG_ERROR_CCONTEXT("failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() @@ -868,8 +852,7 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -891,16 +874,14 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: " << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if (b.miner_tx.vin.size() != 1 || b.miner_tx.vin.front().type() != typeid(txin_gen)) { LOG_ERROR_CCONTEXT("sent wrong block: block: miner tx does not have exactly one txin_gen input" << epee::string_tools::buff_to_hex_nodelimer(block_entry.block) << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if (start_height == std::numeric_limits<uint64_t>::max()) @@ -912,16 +893,14 @@ namespace cryptonote { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) << " wasn't requested, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } if(b.tx_hashes.size() != block_entry.txs.size()) { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) << ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -933,8 +912,7 @@ namespace cryptonote { MERROR("returned not all requested objects (context.m_requested_objects.size()=" << context.m_requested_objects.size() << "), dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -947,7 +925,8 @@ namespace cryptonote } { - MLOG_YELLOW(el::Level::Debug, "Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size()); + MLOG_YELLOW(el::Level::Debug, context << " Got NEW BLOCKS inside of " << __FUNCTION__ << ": size: " << arg.blocks.size() + << ", blocks: " << start_height << " - " << (start_height + arg.blocks.size() - 1)); // add that new span to the block queue const boost::posix_time::time_duration dt = now - context.m_last_request_time; @@ -955,6 +934,8 @@ namespace cryptonote MDEBUG(context << " adding span: " << arg.blocks.size() << " at height " << start_height << ", " << dt.total_microseconds()/1e6 << " seconds, " << (rate/1e3) << " kB/s, size now " << (m_block_queue.get_data_size() + blocks_size) / 1048576.f << " MB"); m_block_queue.add_blocks(start_height, arg.blocks, context.m_connection_id, rate, blocks_size); + context.m_last_known_hash = cryptonote::get_blob_hash(arg.blocks.back().block); + if (m_core.get_test_drop_download() && m_core.get_test_drop_download_height()) { // DISCARD BLOCKS for testing // We try to lock the sync lock. If we can, it means no other thread is @@ -978,7 +959,6 @@ namespace cryptonote uint64_t start_height; std::list<cryptonote::block_complete_entry> blocks; boost::uuids::uuid span_connection_id; - m_block_queue.mark_last_block(previous_height - 1); if (!m_block_queue.get_next_span(start_height, blocks, span_connection_id)) { MDEBUG(context << " no next span found, going back to download"); @@ -986,9 +966,41 @@ namespace cryptonote } MDEBUG(context << " next span in the queue has blocks " << start_height << "-" << (start_height + blocks.size() - 1) << ", we need " << previous_height); - if (previous_height < start_height || previous_height >= start_height + blocks.size()) + + + block new_block; + if (!parse_and_validate_block_from_blob(blocks.front().block, new_block)) { - MDEBUG(context << " this span is not what we need, going back to download"); + MERROR("Failed to parse block, but it should already have been parsed"); + break; + } + bool parent_known = m_core.have_block(new_block.prev_id); + if (!parent_known) + { + // it could be: + // - later in the current chain + // - later in an alt chain + // - orphan + // if it was requested, then it'll be resolved later, otherwise it's an orphan + bool parent_requested = false; + m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ + if (context.m_requested_objects.find(new_block.prev_id) != context.m_requested_objects.end()) + { + parent_requested = true; + return false; + } + return true; + }); + if (!parent_requested) + { + LOG_ERROR_CCONTEXT("Got block with unknown parent which was not requested - dropping connection"); + // in case the peer had dropped beforehand, remove the span anyway so other threads can wake up and get it + m_block_queue.remove_spans(span_connection_id, start_height); + return 1; + } + + // parent was requested, so we wait for it to be retrieved + MINFO(context << " parent was requested, we'll get back to it"); break; } @@ -1019,8 +1031,7 @@ namespace cryptonote if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, tx_id = " << epee::string_tools::pod_to_hex(get_blob_hash(*it)) << ", dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id, true); + drop_connection(context, false, true); return true; })) LOG_ERROR_CCONTEXT("span connection id not found"); @@ -1045,9 +1056,7 @@ namespace cryptonote { if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ LOG_PRINT_CCONTEXT_L1("Block verification failed, dropping connection"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id, true); + drop_connection(context, true, true); return true; })) LOG_ERROR_CCONTEXT("span connection id not found"); @@ -1061,9 +1070,7 @@ namespace cryptonote { if (!m_p2p->for_connection(span_connection_id, [&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t f)->bool{ LOG_PRINT_CCONTEXT_L1("Block received at sync phase was marked as orphaned, dropping connection"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id, true); + drop_connection(context, true, true); return true; })) LOG_ERROR_CCONTEXT("span connection id not found"); @@ -1083,7 +1090,7 @@ namespace cryptonote m_core.cleanup_handle_incoming_blocks(); - m_block_queue.mark_last_block(m_core.get_current_blockchain_height() - 1); + m_block_queue.remove_spans(span_connection_id, start_height); if (m_core.get_current_blockchain_height() > previous_height) { @@ -1113,8 +1120,7 @@ skip: if (!request_missing_objects(context, true, force_next_span)) { LOG_ERROR_CCONTEXT("Failed to request missing objects, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } return 1; @@ -1134,8 +1140,7 @@ skip: if(!m_core.find_blockchain_supplement(arg.block_ids, r)) { LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN."); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size()); @@ -1201,8 +1206,6 @@ skip: template<class t_core> bool t_cryptonote_protocol_handler<t_core>::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks, bool force_next_span) { - m_block_queue.mark_last_block(m_core.get_current_blockchain_height() - 1); - // flush stale spans std::set<boost::uuids::uuid> live_connections; m_p2p->for_each_connection([&](cryptonote_connection_context& context, nodetool::peerid_type peer_id, uint32_t support_flags)->bool{ @@ -1308,8 +1311,13 @@ skip: context.m_last_response_height = 0; goto skip; } + // take out blocks we already have + while (!context.m_needed_objects.empty() && m_core.have_block(context.m_needed_objects.front())) + { + context.m_needed_objects.pop_front(); + } const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1; - span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id); + span = m_block_queue.reserve_span(first_block_height, context.m_last_response_height, count_limit, context.m_connection_id, context.m_needed_objects); MDEBUG(context << " span from " << first_block_height << ": " << span.first << "/" << span.second); } if (span.second == 0 && !force_next_span) @@ -1360,8 +1368,6 @@ skip: auto j = it++; context.m_needed_objects.erase(j); } - - m_block_queue.set_span_hashes(span.first, context.m_connection_id, hashes); } context.m_last_request_time = boost::posix_time::microsec_clock::universal_time(); @@ -1384,10 +1390,9 @@ skip: if (!start_from_current_chain) { - // we'll want to start off from where we are on the download side, which may not be added yet - crypto::hash last_known_hash = m_block_queue.get_last_known_hash(); - if (last_known_hash != cryptonote::null_hash && r.block_ids.front() != last_known_hash) - r.block_ids.push_front(last_known_hash); + // we'll want to start off from where we are on that peer, which may not be added yet + if (context.m_last_known_hash != cryptonote::null_hash && r.block_ids.front() != context.m_last_known_hash) + r.block_ids.push_front(context.m_last_known_hash); } handler_request_blocks_history( r.block_ids ); // change the limit(?), sleep(?) @@ -1464,9 +1469,7 @@ skip: if(!arg.m_block_ids.size()) { LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); - m_p2p->drop_connection(context); - m_p2p->add_host_fail(context.m_remote_address); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, true, false); return 1; } @@ -1477,8 +1480,7 @@ skip: LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with m_total_height=" << arg.total_height << ", m_start_height=" << arg.start_height << ", m_block_ids.size()=" << arg.m_block_ids.size()); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } @@ -1490,8 +1492,7 @@ skip: if (!request_missing_objects(context, false)) { LOG_ERROR_CCONTEXT("Failed to request missing objects, dropping connection"); - m_p2p->drop_connection(context); - m_block_queue.flush_spans(context.m_connection_id); + drop_connection(context, false, false); return 1; } return 1; @@ -1547,6 +1548,29 @@ skip: m_core.on_transaction_relayed(*tx_blob_it); return relay_post_notify<NOTIFY_NEW_TRANSACTIONS>(arg, exclude_context); } + //------------------------------------------------------------------------------------------------------------------------ + template<class t_core> + void t_cryptonote_protocol_handler<t_core>::drop_connection(cryptonote_connection_context &context, bool add_fail, bool flush_all_spans) + { + if (add_fail) + m_p2p->add_host_fail(context.m_remote_address); + m_p2p->drop_connection(context); + + uint64_t target = 0; + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) { + if (cntxt.m_state >= cryptonote_connection_context::state_synchronizing && cntxt.m_connection_id != context.m_connection_id) + target = std::max(target, cntxt.m_remote_blockchain_height); + return true; + }); + const uint64_t previous_target = m_core.get_target_blockchain_height(); + if (target < previous_target) + { + MINFO("Target height decreasing from " << previous_target << " to " << target); + m_core.set_target_blockchain_height(target); + } + + m_block_queue.flush_spans(context.m_connection_id, flush_all_spans); + } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 34d87d282..995fee484 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1742,7 +1742,7 @@ bool t_rpc_command_executor::sync_info() for (const auto &s: res.spans) if (s.rate > 0.0f && s.connection_id == p.info.connection_id) nblocks += s.nblocks, size += s.size; - tools::success_msg_writer() << address << " " << p.info.peer_id << " " << p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued"; + tools::success_msg_writer() << address << " " << p.info.peer_id << " " << p.info.height << " " << p.info.current_download << " kB/s, " << nblocks << " blocks / " << size/1e6 << " MB queued"; } uint64_t total_size = 0; diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt new file mode 100644 index 000000000..99198dc57 --- /dev/null +++ b/src/debug_utilities/CMakeLists.txt @@ -0,0 +1,73 @@ +# Copyright (c) 2014-2017, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(cn_deserialize_sources + cn_deserialize.cpp + ) + +monero_add_executable(cn_deserialize + ${cn_deserialize_sources} + ${cn_deserialize_private_headers}) + +target_link_libraries(cn_deserialize + LINK_PRIVATE + cryptonote_core + blockchain_db + p2p + epee + ${CMAKE_THREAD_LIBS_INIT}) + +add_dependencies(cn_deserialize + version) +set_property(TARGET cn_deserialize + PROPERTY + OUTPUT_NAME "monero-utils-deserialize") + + +set(object_sizes_sources + object_sizes.cpp + ) + +monero_add_executable(object_sizes + ${object_sizes_sources} + ${object_sizes_private_headers}) + +target_link_libraries(object_sizes + LINK_PRIVATE + cryptonote_core + blockchain_db + p2p + epee + ${CMAKE_THREAD_LIBS_INIT}) + +add_dependencies(object_sizes + version) +set_property(TARGET object_sizes + PROPERTY + OUTPUT_NAME "monero-utils-object-sizes") + diff --git a/src/blockchain_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index b178e4e03..a1b569554 100644 --- a/src/blockchain_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -29,12 +29,11 @@ #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/tx_extra.h" #include "cryptonote_core/blockchain.h" -#include "blockchain_utilities.h" #include "common/command_line.h" #include "version.h" #undef MONERO_DEFAULT_LOG_CATEGORY -#define MONERO_DEFAULT_LOG_CATEGORY "bcutil" +#define MONERO_DEFAULT_LOG_CATEGORY "debugtools.deserialize" namespace po = boost::program_options; using namespace epee; diff --git a/src/debug_utilities/object_sizes.cpp b/src/debug_utilities/object_sizes.cpp new file mode 100644 index 000000000..47ba5cf6c --- /dev/null +++ b/src/debug_utilities/object_sizes.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2017, 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 <map> +#include "cryptonote_basic/cryptonote_basic.h" +#include "cryptonote_basic/tx_extra.h" +#include "cryptonote_core/blockchain.h" +#include "p2p/p2p_protocol_defs.h" +#include "p2p/connection_basic.hpp" +#include "p2p/net_peerlist.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "blockchain_db/lmdb/db_lmdb.h" +#include "wallet/wallet2.h" +#include "wallet/api/wallet.h" +#include "wallet/api/transaction_info.h" +#include "wallet/api/transaction_history.h" +#include "wallet/api/unsigned_transaction.h" +#include "wallet/api/pending_transaction.h" + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "debugtools.objectsizes" + +class size_logger +{ +public: + ~size_logger() + { + for (const auto i: types) + std::cout << std::to_string(i.first) << "\t" << i.second << std::endl; + } + void add(const char *type, size_t size) { types.insert(std::make_pair(size, type)); } +private: + std::multimap<size_t, const std::string> types; +}; +#define SL(type) sl.add(#type, sizeof(type)) + +int main(int argc, char* argv[]) +{ + size_logger sl; + + tools::sanitize_locale(); + + mlog_configure("", true); + + SL(boost::thread); + SL(boost::asio::io_service); + SL(boost::asio::io_service::work); + SL(boost::asio::deadline_timer); + + SL(cryptonote::DB_ERROR); + SL(cryptonote::mdb_txn_safe); + SL(cryptonote::mdb_threadinfo); + + SL(cryptonote::block_header); + SL(cryptonote::block); + SL(cryptonote::transaction_prefix); + SL(cryptonote::transaction); + + SL(cryptonote::txpool_tx_meta_t); + + SL(epee::net_utils::network_address_base); + SL(epee::net_utils::ipv4_network_address); + SL(epee::net_utils::network_address); + SL(epee::net_utils::connection_context_base); + SL(epee::net_utils::connection_basic); + + SL(nodetool::peerlist_entry); + SL(nodetool::anchor_peerlist_entry); + SL(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>>); + SL(nodetool::p2p_connection_context_t<cryptonote::t_cryptonote_protocol_handler<cryptonote::core>::connection_context>); + SL(nodetool::network_address_old); + + SL(tools::wallet2::transfer_details); + SL(tools::wallet2::payment_details); + SL(tools::wallet2::unconfirmed_transfer_details); + SL(tools::wallet2::confirmed_transfer_details); + SL(tools::wallet2::tx_construction_data); + SL(tools::wallet2::pending_tx); + SL(tools::wallet2::unsigned_tx_set); + SL(tools::wallet2::signed_tx_set); + + SL(Monero::WalletImpl); + SL(Monero::AddressBookRow); + SL(Monero::TransactionInfoImpl); + SL(Monero::TransactionHistoryImpl); + SL(Monero::PendingTransactionImpl); + SL(Monero::UnsignedTransactionImpl); + + return 0; +} diff --git a/src/p2p/connection_basic.cpp b/src/p2p/connection_basic.cpp index d4fbc79e1..b95f36b99 100644 --- a/src/p2p/connection_basic.cpp +++ b/src/p2p/connection_basic.cpp @@ -244,8 +244,7 @@ void connection_basic::sleep_before_packet(size_t packet_size, int phase, int q delay *= 0.50; if (delay > 0) { long int ms = (long int)(delay * 1000); - MDEBUG("Sleeping in " << __FUNCTION__ << " for " << ms << " ms before packet_size="<<packet_size); // debug sleep - _dbg1("sleep in sleep_before_packet"); + MTRACE("Sleeping in " << __FUNCTION__ << " for " << ms << " ms before packet_size="<<packet_size); // debug sleep boost::this_thread::sleep(boost::posix_time::milliseconds( ms ) ); } } while(delay > 0); @@ -264,13 +263,13 @@ void connection_basic::set_start_time() { void connection_basic::do_send_handler_write(const void* ptr , size_t cb ) { sleep_before_packet(cb,1,-1); - MDEBUG("handler_write (direct) - before ASIO write, for packet="<<cb<<" B (after sleep)"); + MTRACE("handler_write (direct) - before ASIO write, for packet="<<cb<<" B (after sleep)"); set_start_time(); } void connection_basic::do_send_handler_write_from_queue( const boost::system::error_code& e, size_t cb, int q_len ) { sleep_before_packet(cb,2,q_len); - MDEBUG("handler_write (after write, from queue="<<q_len<<") - before ASIO write, for packet="<<cb<<" B (after sleep)"); + MTRACE("handler_write (after write, from queue="<<q_len<<") - before ASIO write, for packet="<<cb<<" B (after sleep)"); set_start_time(); } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 4e1e60d27..22ae5ec26 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -226,6 +226,7 @@ namespace nodetool bool is_addr_recently_failed(const epee::net_utils::network_address& addr); bool is_priority_node(const epee::net_utils::network_address& na); std::set<std::string> get_seed_nodes(bool testnet) const; + bool connect_to_seed(); template <class Container> bool connect_to_peerlist(const Container& peers); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 58a7f3563..e179fc14f 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -159,7 +159,7 @@ namespace nodetool } catch (const std::exception &e) { - LOG_ERROR("Failed to load p2p config file, falling back to default config"); + MWARNING("Failed to load p2p config file, falling back to default config"); m_peerlist = peerlist_manager(); // it was probably half clobbered by the failed load make_default_config(); } @@ -396,7 +396,7 @@ namespace nodetool } else { - MERROR("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec)); + MWARNING("IPv6 unsupported, skip '" << host << "' -> " << endpoint.address().to_v6().to_string(ec)); throw std::runtime_error("IPv6 unsupported"); } } @@ -756,19 +756,19 @@ namespace nodetool if(code < 0) { - LOG_ERROR_CC(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); return; } if(rsp.node_data.network_id != m_network_id) { - LOG_ERROR_CC(context, "COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE Failed, wrong network! (" << epee::string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); return; } if(!handle_remote_peerlist(rsp.local_peerlist_new, rsp.node_data.local_time, context)) { - LOG_ERROR_CC(context, "COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); add_host_fail(context.m_remote_address); return; } @@ -777,7 +777,7 @@ namespace nodetool { if(!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, true)) { - LOG_ERROR_CC(context, "COMMAND_HANDSHAKE invoked, but process_payload_sync_data returned false, dropping connection."); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE invoked, but process_payload_sync_data returned false, dropping connection."); hsh_result = false; return; } @@ -805,7 +805,7 @@ namespace nodetool if(!hsh_result) { - LOG_ERROR_CC(context_, "COMMAND_HANDSHAKE Failed"); + LOG_WARNING_CC(context_, "COMMAND_HANDSHAKE Failed"); m_net_server.get_config_object().close(context_.m_connection_id); } else @@ -831,7 +831,7 @@ namespace nodetool context.m_in_timedsync = false; if(code < 0) { - LOG_ERROR_CC(context, "COMMAND_TIMED_SYNC invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); + LOG_WARNING_CC(context, "COMMAND_TIMED_SYNC invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); return; } @@ -848,7 +848,7 @@ namespace nodetool if(!r) { - LOG_ERROR_CC(context_, "COMMAND_TIMED_SYNC Failed"); + LOG_WARNING_CC(context_, "COMMAND_TIMED_SYNC Failed"); return false; } return true; @@ -1126,6 +1126,8 @@ namespace nodetool size_t random_index; if (use_white_list) { + local_peers_count = m_peerlist.get_white_peers_count(); + max_random_index = std::min<uint64_t>(local_peers_count -1, 20); random_index = get_random_index_with_fixed_probability(max_random_index); } else { random_index = crypto::rand<size_t>() % m_peerlist.get_gray_peers_count(); @@ -1171,14 +1173,11 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::connections_maker() + bool node_server<t_payload_net_handler>::connect_to_seed() { - if (!connect_to_peerlist(m_exclusive_peers)) return false; - - if (!m_exclusive_peers.empty()) return true; + if (m_seed_nodes.empty()) + return true; - if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) - { size_t try_count = 0; size_t current_index = crypto::rand<size_t>()%m_seed_nodes.size(); bool fallback_nodes_added = false; @@ -1211,6 +1210,21 @@ namespace nodetool if(++current_index >= m_seed_nodes.size()) current_index = 0; } + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::connections_maker() + { + if (!connect_to_peerlist(m_exclusive_peers)) return false; + + if (!m_exclusive_peers.empty()) return true; + + size_t start_conn_count = get_outgoing_connections_count(); + if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) + { + if (!connect_to_seed()) + return false; } if (!connect_to_peerlist(m_priority_peers)) return false; @@ -1242,6 +1256,13 @@ namespace nodetool } } + if (start_conn_count == get_outgoing_connections_count() && start_conn_count < m_config.m_net_config.connections_count) + { + MINFO("Failed to connect to any, trying seeds"); + if (!connect_to_seed()) + return false; + } + return true; } //----------------------------------------------------------------------------------- @@ -1383,17 +1404,17 @@ namespace nodetool uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; if(time_delata > 24*60*60 ) { - LOG_ERROR("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time); + MWARNING("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time); return false; } if(m_last_stat_request_time >= tr.time ) { - LOG_ERROR("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time); + MWARNING("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time); return false; } if(m_config.m_peer_id != tr.peer_id) { - LOG_ERROR("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id<< ")"); + MWARNING("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id<< ")"); return false; } crypto::public_key pk = AUTO_VAL_INIT(pk); @@ -1401,7 +1422,7 @@ namespace nodetool crypto::hash h = get_proof_of_trust_hash(tr); if(!crypto::check_signature(h, pk, tr.sign)) { - LOG_ERROR("check_trust failed: sign check failed"); + MWARNING("check_trust failed: sign check failed"); return false; } //update last request time @@ -1562,13 +1583,13 @@ namespace nodetool { if(code <= 0) { - LOG_ERROR_CC(ping_context, "Failed to invoke COMMAND_PING to " << address.str() << "(" << code << ", " << epee::levin::get_err_descr(code) << ")"); + LOG_WARNING_CC(ping_context, "Failed to invoke COMMAND_PING to " << address.str() << "(" << code << ", " << epee::levin::get_err_descr(code) << ")"); return; } if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) { - LOG_ERROR_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << rsp.peer_id); + LOG_WARNING_CC(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << address.str() << ", hsh_peer_id=" << pr_ << ", rsp.peer_id=" << rsp.peer_id); m_net_server.get_config_object().close(ping_context.m_connection_id); return; } @@ -1578,7 +1599,7 @@ namespace nodetool if(!inv_call_res) { - LOG_ERROR_CC(ping_context, "back ping invoke failed to " << address.str()); + LOG_WARNING_CC(ping_context, "back ping invoke failed to " << address.str()); m_net_server.get_config_object().close(ping_context.m_connection_id); return false; } @@ -1586,7 +1607,7 @@ namespace nodetool }); if(!r) { - LOG_ERROR_CC(context, "Failed to call connect_async, network error."); + LOG_WARNING_CC(context, "Failed to call connect_async, network error."); } return r; } @@ -1605,7 +1626,7 @@ namespace nodetool { if(code < 0) { - LOG_ERROR_CC(context_, "COMMAND_REQUEST_SUPPORT_FLAGS invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); + LOG_WARNING_CC(context_, "COMMAND_REQUEST_SUPPORT_FLAGS invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")"); return; } @@ -1622,7 +1643,7 @@ namespace nodetool { if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) { - LOG_ERROR_CC(context, "Failed to process_payload_sync_data(), dropping connection"); + LOG_WARNING_CC(context, "Failed to process_payload_sync_data(), dropping connection"); drop_connection(context); return 1; } @@ -1649,7 +1670,7 @@ namespace nodetool if(!context.m_is_income) { - LOG_ERROR_CC(context, "COMMAND_HANDSHAKE came not from incoming connection"); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came not from incoming connection"); drop_connection(context); add_host_fail(context.m_remote_address); return 1; @@ -1657,14 +1678,14 @@ namespace nodetool if(context.peer_id) { - LOG_ERROR_CC(context, "COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"); drop_connection(context); return 1; } if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { - LOG_ERROR_CC(context, "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); drop_connection(context); return 1; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 596c298d7..4d8673f8e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -48,6 +48,7 @@ #include "common/util.h" #include "common/dns_utils.h" #include "common/base58.h" +#include "common/scoped_message_writer.h" #include "p2p/net_node.h" #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "simplewallet.h" @@ -62,14 +63,6 @@ #include "wallet/wallet_args.h" #include <stdexcept> -#ifdef HAVE_READLINE - #include "readline_buffer.h" - #define PAUSE_READLINE() \ - rdln::suspend_readline pause_readline; -#else - #define PAUSE_READLINE() -#endif - using namespace std; using namespace epee; using namespace cryptonote; @@ -147,84 +140,19 @@ namespace return err; } - class message_writer + tools::scoped_message_writer success_msg_writer(bool color = false) { - public: - message_writer(console_colors color = console_color_default, bool bright = false, - std::string&& prefix = std::string(), el::Level log_level = el::Level::Info) - : m_flush(true) - , m_color(color) - , m_bright(bright) - , m_log_level(log_level) - { - m_oss << prefix; - } - - message_writer(message_writer&& rhs) - : m_flush(std::move(rhs.m_flush)) -#if defined(_MSC_VER) - , m_oss(std::move(rhs.m_oss)) -#else - // GCC bug: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 - , m_oss(rhs.m_oss.str(), ios_base::out | ios_base::ate) -#endif - , m_color(std::move(rhs.m_color)) - , m_log_level(std::move(rhs.m_log_level)) - { - rhs.m_flush = false; - } - - template<typename T> - std::ostream& operator<<(const T& val) - { - m_oss << val; - return m_oss; - } - - ~message_writer() - { - if (m_flush) - { - m_flush = false; - - MCLOG(m_log_level, "global", m_oss.str()); - - if (console_color_default == m_color) - { - std::cout << m_oss.str(); - } - else - { - PAUSE_READLINE(); - set_console_color(m_color, m_bright); - std::cout << m_oss.str(); - reset_console_color(); - } - std::cout << std::endl; - } - } - - private: - message_writer(message_writer& rhs); - message_writer& operator=(message_writer& rhs); - message_writer& operator=(message_writer&& rhs); - - private: - bool m_flush; - std::stringstream m_oss; - console_colors m_color; - bool m_bright; - el::Level m_log_level; - }; + return tools::scoped_message_writer(color ? console_color_green : console_color_default, false, std::string(), el::Level::Info); + } - message_writer success_msg_writer(bool color = false) + tools::scoped_message_writer message_writer(epee::console_colors color = epee::console_color_default, bool bright = false) { - return message_writer(color ? console_color_green : console_color_default, false, std::string(), el::Level::Info); + return tools::scoped_message_writer(color, bright); } - message_writer fail_msg_writer() + tools::scoped_message_writer fail_msg_writer() { - return message_writer(console_color_red, true, sw::tr("Error: "), el::Level::Error); + return tools::scoped_message_writer(console_color_red, true, sw::tr("Error: "), el::Level::Error); } bool is_it_true(const std::string& s) @@ -919,14 +847,19 @@ bool simple_wallet::ask_wallet_create_if_needed() } else if(!wallet_file_exists && !keys_file_exists) //No wallet, no keys { - message_writer() << tr(m_restoring ? "Confirm wallet name: " : "No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path; - confirm_creation = command_line::input_line(tr("(Y/Yes/N/No): ")); - if(std::cin.eof()) + bool ok = true; + if (!m_restoring) { - LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()"); - return false; + message_writer() << tr("No wallet found with that name. Confirm creation of new wallet named: ") << wallet_path; + confirm_creation = command_line::input_line(tr("(Y/Yes/N/No): ")); + if(std::cin.eof()) + { + LOG_ERROR("Unexpected std::cin.eof() - Exited simple_wallet::ask_wallet_create_if_needed()"); + return false; + } + ok = command_line::is_yes(confirm_creation); } - if(command_line::is_yes(confirm_creation)) + if (ok) { success_msg_writer() << tr("Generating new wallet..."); m_generate_new = wallet_path; @@ -1093,7 +1026,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse spend secret key - std::string spendkey_string = command_line::input_line("Spend key: "); + std::string spendkey_string = command_line::input_line("Secret spend key: "); if (std::cin.eof()) return false; if (spendkey_string.empty()) { @@ -1109,7 +1042,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) crypto::secret_key spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); // parse view secret key - std::string viewkey_string = command_line::input_line("View key: "); + std::string viewkey_string = command_line::input_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) { @@ -3867,6 +3800,22 @@ static std::string get_human_readable_timestamp(uint64_t ts) return std::string(buffer); } //---------------------------------------------------------------------------------------------------- +static std::string get_human_readable_timespan(std::chrono::seconds seconds) +{ + uint64_t ts = seconds.count(); + if (ts < 60) + return std::to_string(ts) + tr(" seconds"); + if (ts < 3600) + return std::to_string((uint64_t)(ts / 60)) + tr(" minutes"); + if (ts < 3600 * 24) + return std::to_string((uint64_t)(ts / 3600)) + tr(" hours"); + if (ts < 3600 * 24 * 30.5) + return std::to_string((uint64_t)(ts / (3600 * 24))) + tr(" days"); + if (ts < 3600 * 24 * 365.25) + return std::to_string((uint64_t)(ts / (3600 * 24 * 365.25))) + tr(" months"); + return tr("a long time"); +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::show_transfers(const std::vector<std::string> &args_) { std::vector<std::string> local_args = args_; @@ -4175,6 +4124,19 @@ void simple_wallet::wallet_idle_thread() } } //---------------------------------------------------------------------------------------------------- +std::string simple_wallet::get_prompt() const +{ + std::string addr_start = m_wallet->get_account().get_public_address_str(m_wallet->testnet()).substr(0, 6); + std::string prompt = std::string("[") + tr("wallet") + " " + addr_start; + uint32_t version; + if (!m_wallet->check_connection(&version)) + prompt += tr(" (no daemon)"); + else if (!m_wallet->is_synced()) + prompt += tr(" (out of sync)"); + prompt += "]: "; + return prompt; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::run() { // check and display warning, but go on anyway @@ -4185,9 +4147,8 @@ bool simple_wallet::run() m_auto_refresh_enabled = m_wallet->auto_refresh(); m_idle_thread = boost::thread([&]{wallet_idle_thread();}); - std::string addr_start = m_wallet->get_account().get_public_address_str(m_wallet->testnet()).substr(0, 6); message_writer(console_color_green, false) << "Background refresh thread started"; - return m_cmd_binder.run_handling(std::string("[") + tr("wallet") + " " + addr_start + "]: ", ""); + return m_cmd_binder.run_handling([this](){return get_prompt();}, ""); } //---------------------------------------------------------------------------------------------------- void simple_wallet::stop() @@ -4509,6 +4470,12 @@ bool simple_wallet::export_key_images(const std::vector<std::string> &args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::import_key_images(const std::vector<std::string> &args) { + if (!m_trusted_daemon) + { + fail_msg_writer() << tr("this command requires a trusted daemon. Enable with --trusted-daemon"); + return true; + } + if (args.size() != 1) { fail_msg_writer() << tr("usage: import_key_images <filename>"); @@ -4673,6 +4640,8 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) } crypto::hash txid = *reinterpret_cast<const crypto::hash*>(txid_data.data()); + const uint64_t last_block_height = m_wallet->get_blockchain_current_height(); + std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; m_wallet->get_payments(payments, 0); for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { @@ -4687,6 +4656,23 @@ bool simple_wallet::show_transfer(const std::vector<std::string> &args) success_msg_writer() << "Timestamp: " << get_human_readable_timestamp(pd.m_timestamp); success_msg_writer() << "Amount: " << print_money(pd.m_amount); success_msg_writer() << "Payment ID: " << payment_id; + 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) + success_msg_writer() << "Locked: " << (bh - last_block_height) << " blocks to unlock"; + else + success_msg_writer() << std::to_string(last_block_height - bh) << " confirmations"; + } + 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) + success_msg_writer() << "unlocked for " << get_human_readable_timespan(std::chrono::seconds(threshold - pd.m_unlock_time)); + else + success_msg_writer() << "locked for " << get_human_readable_timespan(std::chrono::seconds(pd.m_unlock_time - threshold)); + } success_msg_writer() << "Note: " << m_wallet->get_tx_note(txid); return true; } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 1fb85a3c7..cca5d4928 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -182,6 +182,7 @@ namespace cryptonote bool accept_loaded_tx(const tools::wallet2::unsigned_tx_set &txs); bool accept_loaded_tx(const tools::wallet2::signed_tx_set &txs); bool print_ring_members(const std::vector<tools::wallet2::pending_tx>& ptx_vector, std::ostream& ostr); + std::string get_prompt() const; /*! * \brief Prints the seed with a nice message diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index a2bb44cfe..c0974f880 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -35,6 +35,7 @@ #include "transaction_history.h" #include "address_book.h" #include "common_defines.h" +#include "common/util.h" #include "mnemonics/electrum-words.h" #include <boost/format.hpp> @@ -1411,6 +1412,11 @@ bool WalletImpl::parse_uri(const std::string &uri, std::string &address, std::st return m_wallet->parse_uri(uri, address, payment_id, amount, tx_description, recipient_name, unknown_parameters, error); } +std::string WalletImpl::getDefaultDataDir() const +{ + return tools::get_default_data_dir(); +} + bool WalletImpl::rescanSpent() { clearStatus(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 07b0f063b..8190c7873 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -129,6 +129,7 @@ public: virtual void startRefresh(); virtual void pauseRefresh(); virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error); + virtual std::string getDefaultDataDir() const; private: void clearStatus() const; diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index b2f947972..a23533530 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -125,6 +125,10 @@ bool WalletManagerImpl::walletExists(const std::string &path) return false; } +bool WalletManagerImpl::verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const +{ + return tools::wallet2::verify_password(keys_file_name, password, watch_only); +} std::vector<std::string> WalletManagerImpl::findWallets(const std::string &path) { diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index 033e8108f..aa6ea439e 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -50,6 +50,7 @@ public: const std::string &spendKeyString = ""); virtual bool closeWallet(Wallet *wallet); bool walletExists(const std::string &path); + bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const; std::vector<std::string> findWallets(const std::string &path); std::string errorString() const; void setDaemonAddress(const std::string &address); diff --git a/src/wallet/node_rpc_proxy.cpp b/src/wallet/node_rpc_proxy.cpp index 1143a89d7..9f30e4ac5 100644 --- a/src/wallet/node_rpc_proxy.cpp +++ b/src/wallet/node_rpc_proxy.cpp @@ -48,6 +48,8 @@ NodeRPCProxy::NodeRPCProxy(epee::net_utils::http::http_simple_client &http_clien , m_dynamic_per_kb_fee_estimate_cached_height(0) , m_dynamic_per_kb_fee_estimate_grace_blocks(0) , m_rpc_version(0) + , m_target_height(0) + , m_target_height_time(0) {} void NodeRPCProxy::invalidate() @@ -60,9 +62,11 @@ void NodeRPCProxy::invalidate() m_dynamic_per_kb_fee_estimate_cached_height = 0; m_dynamic_per_kb_fee_estimate_grace_blocks = 0; m_rpc_version = 0; + m_target_height = 0; + m_target_height_time = 0; } -boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) +boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version) const { const time_t now = time(NULL); if (m_rpc_version == 0) @@ -84,7 +88,7 @@ boost::optional<std::string> NodeRPCProxy::get_rpc_version(uint32_t &rpc_version return boost::optional<std::string>(); } -boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) +boost::optional<std::string> NodeRPCProxy::get_height(uint64_t &height) const { const time_t now = time(NULL); if (m_height == 0 || now >= m_height_time + 30) // re-cache every 30 seconds @@ -110,7 +114,32 @@ void NodeRPCProxy::set_height(uint64_t h) m_height = h; } -boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) +boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) const +{ + const time_t now = time(NULL); + if (m_height == 0 || now >= m_height_time + 30) // re-cache every 30 seconds + { + epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_INFO::request> req_t = AUTO_VAL_INIT(req_t); + epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_INFO::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); + + req_t.jsonrpc = "2.0"; + req_t.id = epee::serialization::storage_entry(0); + req_t.method = "get_info"; + m_daemon_rpc_mutex.lock(); + bool r = net_utils::invoke_http_json("/json_rpc", req_t, resp_t, m_http_client, rpc_timeout); + m_daemon_rpc_mutex.unlock(); + + CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, resp_t.result.status, "Failed to connect to daemon"); + CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, resp_t.result.status, "Failed to get target blockchain height"); + m_target_height = resp_t.result.target_height; + m_target_height_time = now; + } + height = m_target_height; + return boost::optional<std::string>(); +} + +boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, uint64_t &earliest_height) const { if (m_earliest_height[version] == 0) { @@ -134,7 +163,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version, return boost::optional<std::string>(); } -boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) +boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const { uint64_t height; diff --git a/src/wallet/node_rpc_proxy.h b/src/wallet/node_rpc_proxy.h index 02d1d8d93..fb288189a 100644 --- a/src/wallet/node_rpc_proxy.h +++ b/src/wallet/node_rpc_proxy.h @@ -43,23 +43,26 @@ public: void invalidate(); - boost::optional<std::string> get_rpc_version(uint32_t &version); - boost::optional<std::string> get_height(uint64_t &height); + boost::optional<std::string> get_rpc_version(uint32_t &version) const; + boost::optional<std::string> get_height(uint64_t &height) const; void set_height(uint64_t h); - boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height); - boost::optional<std::string> get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee); + boost::optional<std::string> get_target_height(uint64_t &height) const; + boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const; + boost::optional<std::string> get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const; private: epee::net_utils::http::http_simple_client &m_http_client; boost::mutex &m_daemon_rpc_mutex; - uint64_t m_height; - time_t m_height_time; - uint64_t m_earliest_height[256]; - uint64_t m_dynamic_per_kb_fee_estimate; - uint64_t m_dynamic_per_kb_fee_estimate_cached_height; - uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; - uint32_t m_rpc_version; + mutable uint64_t m_height; + mutable time_t m_height_time; + mutable uint64_t m_earliest_height[256]; + mutable uint64_t m_dynamic_per_kb_fee_estimate; + mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height; + mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; + mutable uint32_t m_rpc_version; + mutable uint64_t m_target_height; + mutable time_t m_target_height_time; }; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e569197b8..b63e07b2d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1097,6 +1097,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans } entry.first->second.m_block_height = height; entry.first->second.m_timestamp = ts; + entry.first->second.m_unlock_time = tx.unlock_time; } //---------------------------------------------------------------------------------------------------- void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices) @@ -1878,6 +1879,10 @@ bool wallet2::clear() m_payments.clear(); m_tx_keys.clear(); m_confirmed_txs.clear(); + m_unconfirmed_payments.clear(); + m_scanned_pool_txs[0].clear(); + m_scanned_pool_txs[1].clear(); + m_address_book.clear(); m_local_bc_height = 1; return true; } @@ -1958,6 +1963,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p value2.SetInt(m_merge_destinations ? 1 :0); json.AddMember("merge_destinations", value2, json.GetAllocator()); + value2.SetInt(m_testnet ? 1 :0); + json.AddMember("testnet", value2, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -2099,6 +2107,11 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_min_output_value = field_min_output_value; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, merge_destinations, int, Int, false, false); m_merge_destinations = field_merge_destinations; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, testnet, int, Int, false, m_testnet); + // Wallet is being opened with testnet flag, but is saved as a mainnet wallet + THROW_WALLET_EXCEPTION_IF(m_testnet && !field_testnet, error::wallet_internal_error, "Mainnet wallet can not be opened as testnet wallet"); + // Wallet is being opened without testnet flag but is saved as a testnet wallet. + THROW_WALLET_EXCEPTION_IF(!m_testnet && field_testnet, error::wallet_internal_error, "Testnet wallet can not be opened as mainnet wallet"); } const cryptonote::account_keys& keys = m_account.get_keys(); @@ -2113,6 +2126,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa /*! * \brief verify password for default wallet keys file. * \param password Password to verify + * \return true if password is correct * * for verification only * should not mutate state, unlike load_keys() @@ -2121,7 +2135,23 @@ bool wallet2::load_keys(const std::string& keys_file_name, const std::string& pa */ bool wallet2::verify_password(const std::string& password) const { - const std::string keys_file_name = m_keys_file; + return verify_password(m_keys_file, password, m_watch_only); +} + +/*! + * \brief verify password for specified wallet keys file. + * \param keys_file_name Keys file to verify password for + * \param password Password to verify + * \param watch_only If set = only verify view keys, otherwise also spend keys + * \return true if password is correct + * + * for verification only + * should not mutate state, unlike load_keys() + * can be used prior to rewriting wallet keys file, to ensure user has entered the correct password + * + */ +bool wallet2::verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only) +{ wallet2::keys_file_data keys_file_data; std::string buf; bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); @@ -2154,7 +2184,7 @@ bool wallet2::verify_password(const std::string& password) const const cryptonote::account_keys& keys = account_data_check.get_keys(); r = r && verify_keys(keys.m_view_secret_key, keys.m_account_address.m_view_public_key); - if(!m_watch_only) + if(!watch_only) r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); return r; } @@ -2192,9 +2222,26 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const std::stri // try asking the daemon first if(m_refresh_from_block_height == 0 && !recover){ std::string err; - uint64_t height = get_daemon_blockchain_height(err); - if (err.empty()) - m_refresh_from_block_height = height - blocks_per_month; + uint64_t height = 0; + + // we get the max of approximated height and known height + // approximated height is the least of daemon target height + // (the max of what the other daemons are claiming is their + // height) and the theoretical height based on the local + // clock. This will be wrong only if both the local clock + // is bad *and* a peer daemon claims a highest height than + // the real chain. + // known height is the height the local daemon is currently + // synced to, it will be lower than the real chain height if + // the daemon is currently syncing. + height = get_approximate_blockchain_height(); + uint64_t target_height = get_daemon_blockchain_target_height(err); + if (err.empty() && target_height < height) + height = target_height; + uint64_t local_height = get_daemon_blockchain_height(err); + if (err.empty() && local_height > height) + height = local_height; + m_refresh_from_block_height = height >= blocks_per_month ? height - blocks_per_month : 0; } if(m_refresh_from_block_height == 0 && !recover){ @@ -5685,6 +5732,15 @@ uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, ui } } //---------------------------------------------------------------------------------------------------- +bool wallet2::is_synced() const +{ + uint64_t height; + boost::optional<std::string> result = m_node_rpc_proxy.get_target_height(height); + if (result && *result != CORE_RPC_STATUS_OK) + return false; + return get_blockchain_current_height() >= height; +} +//---------------------------------------------------------------------------------------------------- void wallet2::generate_genesis(cryptonote::block& b) { if (m_testnet) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index b853c5f3c..ba9abacf5 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -129,6 +129,8 @@ namespace tools //! Just parses variables. static std::unique_ptr<wallet2> make_dummy(const boost::program_options::variables_map& vm); + static bool verify_password(const std::string& keys_file_name, const std::string& password, bool watch_only); + wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0), m_confirm_missing_payment_id(true), m_ask_password(true), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), m_is_initialized(false), m_restricted(restricted), is_old_file_format(false), m_node_rpc_proxy(m_http_client, m_daemon_rpc_mutex) {} struct transfer_details @@ -199,10 +201,11 @@ namespace tools std::vector<cryptonote::tx_destination_entry> m_dests; crypto::hash m_payment_id; uint64_t m_timestamp; + uint64_t m_unlock_time; - confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(cryptonote::null_hash) {} + confirmed_transfer_details(): m_amount_in(0), m_amount_out(0), m_change((uint64_t)-1), m_block_height(0), m_payment_id(cryptonote::null_hash), m_timestamp(0), m_unlock_time(0) {} confirmed_transfer_details(const unconfirmed_transfer_details &utd, uint64_t height): - m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp) {} + m_amount_in(utd.m_amount_in), m_amount_out(utd.m_amount_out), m_change(utd.m_change), m_block_height(height), m_dests(utd.m_dests), m_payment_id(utd.m_payment_id), m_timestamp(utd.m_timestamp), m_unlock_time(utd.m_tx.unlock_time) {} }; struct tx_construction_data @@ -597,6 +600,8 @@ namespace tools uint64_t get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day); // 1<=month<=12, 1<=day<=31 + bool is_synced() const; + private: /*! * \brief Stores wallet information to wallet file. @@ -704,7 +709,7 @@ BOOST_CLASS_VERSION(tools::wallet2, 18) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 7) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 6) -BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 3) +BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 4) BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 16) BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0) @@ -879,6 +884,13 @@ namespace boost x.m_amount_out += x.m_change; } } + if (ver < 4) + { + if (!typename Archive::is_saving()) + x.m_unlock_time = 0; + return; + } + a & x.m_unlock_time; } template <class Archive> diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index ce431ee6b..8da8c62eb 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -596,6 +596,9 @@ struct Wallet virtual bool verifySignedMessage(const std::string &message, const std::string &addres, const std::string &signature) const = 0; virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0; + + virtual std::string getDefaultDataDir() const = 0; + /* * \brief rescanSpent - Rescan spent outputs - Can only be used with trusted daemon * \return true on success @@ -669,11 +672,20 @@ struct WalletManager /*! * @brief TODO: delme walletExists - check if the given filename is the wallet * @param path - filename - * @return + * @return - true if wallet exists */ virtual bool walletExists(const std::string &path) = 0; /*! + * @brief verifyWalletPassword - check if the given filename is the wallet + * @param keys_file_name - location of keys file + * @param password - password to verify + * @param watch_only - verify only view keys? + * @return - true if password is correct + */ + virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const = 0; + + /*! * \brief findWallets - searches for the wallet files by given path name recursively * \param path - starting point to search * \return - list of strings with found wallets (absolute paths); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index f014c573a..77911e9a7 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -236,6 +236,7 @@ namespace tools entry.height = pd.m_block_height; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.unlock_time = pd.m_unlock_time; entry.fee = 0; // TODO entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "in"; @@ -249,6 +250,7 @@ namespace tools entry.payment_id = entry.payment_id.substr(0,16); entry.height = pd.m_block_height; entry.timestamp = pd.m_timestamp; + entry.unlock_time = pd.m_unlock_time; entry.fee = pd.m_amount_in - pd.m_amount_out; uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known entry.amount = pd.m_amount_in - change - entry.fee; @@ -276,6 +278,7 @@ namespace tools entry.timestamp = pd.m_timestamp; entry.fee = pd.m_amount_in - pd.m_amount_out; entry.amount = pd.m_amount_in - pd.m_change - entry.fee; + entry.unlock_time = pd.m_tx.unlock_time; entry.note = m_wallet->get_tx_note(txid); entry.type = is_failed ? "failed" : "pending"; } @@ -289,6 +292,7 @@ namespace tools entry.height = 0; entry.timestamp = pd.m_timestamp; entry.amount = pd.m_amount; + entry.unlock_time = pd.m_unlock_time; entry.fee = 0; // TODO entry.note = m_wallet->get_tx_note(pd.m_tx_hash); entry.type = "pool"; @@ -1360,7 +1364,12 @@ namespace tools er.message = "Command unavailable in restricted mode."; return false; } - + if (!m_trusted_daemon) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "This command requires a trusted daemon."; + return false; + } try { std::vector<std::pair<crypto::key_image, crypto::signature>> ski; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 5832bb032..fa5c154de 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -560,6 +560,7 @@ namespace wallet_rpc std::string note; std::list<transfer_destination> destinations; std::string type; + uint64_t unlock_time; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); @@ -571,6 +572,7 @@ namespace wallet_rpc KV_SERIALIZE(note); KV_SERIALIZE(destinations); KV_SERIALIZE(type); + KV_SERIALIZE(unlock_time) END_KV_SERIALIZE_MAP() }; |