diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/stack_trace.cpp | 2 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 6 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 3 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 33 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 4 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.inl | 25 | ||||
-rw-r--r-- | src/p2p/net_node.h | 12 | ||||
-rw-r--r-- | src/p2p/net_node.inl | 88 | ||||
-rw-r--r-- | src/p2p/net_peerlist.h | 43 | ||||
-rw-r--r-- | src/ringct/rctSigs.cpp | 34 | ||||
-rw-r--r-- | src/ringct/rctTypes.h | 2 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 10 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.h | 2 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 130 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 6 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.cpp | 16 | ||||
-rw-r--r-- | src/wallet/api/wallet_manager.h | 7 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 53 | ||||
-rw-r--r-- | src/wallet/wallet2_api.h | 21 | ||||
-rw-r--r-- | src/wallet/wallet_args.cpp | 2 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.cpp | 2 |
21 files changed, 445 insertions, 56 deletions
diff --git a/src/common/stack_trace.cpp b/src/common/stack_trace.cpp index ce05b7e04..99e4bda2c 100644 --- a/src/common/stack_trace.cpp +++ b/src/common/stack_trace.cpp @@ -38,7 +38,7 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "stacktrace" -#define ST_LOG(x) CERROR(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x +#define ST_LOG(x) CINFO(el::base::Writer,el::base::DispatchAction::FileOnlyLog,MONERO_DEFAULT_LOG_CATEGORY) << x // from http://stackoverflow.com/questions/11665829/how-can-i-print-stack-trace-for-caught-exceptions-in-c-code-injection-in-c diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8f1f0b260..789687ce0 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -636,7 +636,7 @@ crypto::hash Blockchain::get_block_id_by_height(uint64_t height) const return null_hash; } //------------------------------------------------------------------ -bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk) const +bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orphan) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -645,6 +645,8 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk) const try { blk = m_db->get_block(h); + if (orphan) + *orphan = false; return true; } // try to find block in alternative chain @@ -654,6 +656,8 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk) const if (m_alternative_chains.end() != it_alt) { blk = it_alt->second.bl; + if (orphan) + *orphan = true; return true; } } diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 245dc6e73..ca665e1d4 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -197,10 +197,11 @@ namespace cryptonote * * @param h the hash to look for * @param blk return-by-reference variable to put result block in + * @param orphan if non-NULL, will be set to true if not in the main chain, false otherwise * * @return true if the block was found, else false */ - bool get_block_by_hash(const crypto::hash &h, block &blk) const; + bool get_block_by_hash(const crypto::hash &h, block &blk, bool *orphan = NULL) const; /** * @brief get all block hashes (main chain, alt chains, and invalid blocks) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 5294431d6..22d473b35 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -487,6 +487,13 @@ namespace cryptonote } //std::cout << "!"<< tx.vin.size() << std::endl; + if (bad_semantics_txes.find(tx_hash) != bad_semantics_txes.end()) + { + LOG_PRINT_L1("Transaction already seen with bad semantics, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); const size_t max_tx_version = version == 1 ? 1 : 2; if (tx.version == 0 || tx.version > max_tx_version) @@ -496,6 +503,18 @@ namespace cryptonote return false; } + if(m_mempool.have_tx(tx_hash)) + { + LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); + return true; + } + + if(m_blockchain_storage.have_tx(tx_hash)) + { + LOG_PRINT_L2("tx " << tx_hash << " already have transaction in blockchain"); + return true; + } + if(!check_tx_syntax(tx)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"); @@ -522,6 +541,7 @@ namespace cryptonote if(!check_tx_semantic(tx, keeped_by_block)) { LOG_PRINT_L1("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); + bad_semantics_txes.insert(tx_hash); tvc.m_verifivation_failed = true; return false; } @@ -977,9 +997,9 @@ namespace cryptonote return m_blockchain_storage.get_block_id_by_height(height); } //----------------------------------------------------------------------------------------------- - bool core::get_block_by_hash(const crypto::hash &h, block &blk) const + bool core::get_block_by_hash(const crypto::hash &h, block &blk, bool *orphan) const { - return m_blockchain_storage.get_block_by_hash(h, blk); + return m_blockchain_storage.get_block_by_hash(h, blk, orphan); } //----------------------------------------------------------------------------------------------- std::string core::print_pool(bool short_format) const @@ -1000,12 +1020,11 @@ namespace cryptonote MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL << ENDL - << "You can set the level of process detailization* through \"set_log <level|categories>\" command*, where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)" << ENDL - << ENDL - << "Use \"help\" command to see the list of available commands." << ENDL + << "You can set the level of process detailization* through \"set_log <level|categories>\" command*," << ENDL + << "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)" << ENDL << ENDL - << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL - << "**********************************************************************"); + << "Use the \"help\" command to see the list of available commands." << ENDL + << "**********************************************************************" << ENDL); m_starter_message_showed = true; } diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index fa67ff265..1b9518c96 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -330,7 +330,7 @@ namespace cryptonote * * @note see Blockchain::get_block_by_hash */ - bool get_block_by_hash(const crypto::hash &h, block &blk) const; + bool get_block_by_hash(const crypto::hash &h, block &blk, bool *orphan = NULL) const; /** * @copydoc Blockchain::get_alternative_blocks @@ -824,6 +824,8 @@ namespace cryptonote size_t block_sync_size; time_t start_time; + + std::unordered_set<crypto::hash> bad_semantics_txes; }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 58388f8f7..73e4fa72f 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -46,6 +46,8 @@ #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.cn" +#define MLOG_P2P_MESSAGE(x) MCINFO("net.p2p.msg", context << x) + namespace cryptonote { @@ -314,7 +316,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"); + MLOG_P2P_MESSAGE("Received NOTIFY_NEW_BLOCK (hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; m_core.pause_mine(); @@ -365,7 +367,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_FLUFFY_BLOCK (hop " << arg.hop << ")"); + MLOG_P2P_MESSAGE("Received NOTIFY_NEW_FLUFFY_BLOCK (hop " << arg.hop << ", " << arg.b.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; @@ -613,7 +615,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_FLUFFY_MISSING_TX"); + MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_FLUFFY_MISSING_TX (" << arg.missing_tx_indices.size() << " txes)"); std::list<block> local_blocks; std::list<transaction> local_txs; @@ -671,7 +673,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_TRANSACTIONS"); + MLOG_P2P_MESSAGE("Received NOTIFY_NEW_TRANSACTIONS (" << arg.txs.size() << " txes)"); if(context.m_state != cryptonote_connection_context::state_normal) return 1; @@ -703,7 +705,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_GET_OBJECTS"); + MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_GET_OBJECTS (" << arg.blocks.size() << " blocks, " << arg.txs.size() << " txes)"); NOTIFY_RESPONSE_GET_OBJECTS::request rsp; if(!m_core.handle_get_objects(arg, rsp, context)) { @@ -743,7 +745,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_GET_OBJECTS"); + MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_GET_OBJECTS (" << arg.blocks.size() << " blocks, " << arg.txs.size() << " txes)"); // calculate size of request - mainly for logging/debug size_t size = 0; @@ -933,7 +935,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size()); + MLOG_P2P_MESSAGE("Received NOTIFY_REQUEST_CHAIN (" << arg.block_ids.size() << " blocks"); NOTIFY_RESPONSE_CHAIN_ENTRY::request r; if(!m_core.find_blockchain_supplement(arg.block_ids, r)) { @@ -1020,13 +1022,10 @@ namespace cryptonote bool val_expected = false; if(m_synchronized.compare_exchange_strong(val_expected, true)) { - MGINFO_GREEN(ENDL << "**********************************************************************" << ENDL + MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL << "You are now synchronized with the network. You may now start monero-wallet-cli." << ENDL << ENDL - << "Please note, that the blockchain will be saved only after you quit the daemon with \"exit\" command or if you use \"save\" command." << ENDL - << "Otherwise, you will possibly need to synchronize the blockchain again." << ENDL - << ENDL - << "Use \"help\" command to see the list of available commands." << ENDL + << "Use the \"help\" command to see the list of available commands." << ENDL << "**********************************************************************"); m_core.on_synchronized(); } @@ -1048,7 +1047,7 @@ namespace cryptonote template<class t_core> int t_cryptonote_protocol_handler<t_core>::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) { - LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() + MLOG_P2P_MESSAGE("Received NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height); if(!arg.m_block_ids.size()) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 3f5a5ad93..40598fc0f 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -110,7 +110,12 @@ namespace nodetool void serialize(Archive &a, const t_version_type ver) { a & m_peerlist; - a & m_config.m_peer_id; + if (ver == 0) + { + // from v1, we do not store the peer id anymore + peerid_type peer_id; + a & peer_id; + } } // debug functions bool log_peerlist(); @@ -162,6 +167,7 @@ namespace nodetool #endif int handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context); bool init_config(); + bool make_default_peer_id(); bool make_default_config(); bool store_config(); bool check_trust(const proof_of_trust& tr); @@ -229,6 +235,9 @@ namespace nodetool bool has_too_many_connections(const uint32_t ip); + bool check_connection_and_handshake_with_peer(const net_address& na, uint64_t last_seen_stamp); + bool gray_peerlist_housekeeping(); + void kill() { ///< will be called e.g. from deinit() _info("Killing the net_node"); is_closing = true; @@ -289,6 +298,7 @@ namespace nodetool epee::math_helper::once_a_time_seconds<P2P_DEFAULT_HANDSHAKE_INTERVAL> m_peer_handshake_idle_maker_interval; epee::math_helper::once_a_time_seconds<1> m_connections_maker_interval; epee::math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; + epee::math_helper::once_a_time_seconds<60> m_gray_peerlist_housekeeping_interval; std::string m_bind_ip; std::string m_port; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 60e51c222..0e43d9579 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -173,6 +173,9 @@ namespace nodetool make_default_config(); } + // always recreate a new peer id + make_default_peer_id(); + //at this moment we have hardcoded config m_config.m_net_config.handshake_interval = P2P_DEFAULT_HANDSHAKE_INTERVAL; m_config.m_net_config.packet_max_size = P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit @@ -205,20 +208,26 @@ namespace nodetool if(time(nullptr) >= it->second) { m_blocked_ips.erase(it); - MLOG_CYAN(el::Level::Info, "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " unblocked."); + MCLOG_CYAN(el::Level::Info, "global", "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " unblocked."); return true; } return false; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::make_default_config() + bool node_server<t_payload_net_handler>::make_default_peer_id() { m_config.m_peer_id = crypto::rand<uint64_t>(); return true; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::make_default_config() + { + return make_default_peer_id(); + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::block_ip(uint32_t addr, time_t seconds) { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); @@ -237,7 +246,7 @@ namespace nodetool for (const auto &c: conns) m_net_server.get_config_object().close(c); - MLOG_CYAN(el::Level::Info, "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " blocked."); + MCLOG_CYAN(el::Level::Info, "global", "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " blocked."); return true; } //----------------------------------------------------------------------------------- @@ -249,7 +258,7 @@ namespace nodetool if (i == m_blocked_ips.end()) return false; m_blocked_ips.erase(i); - MLOG_CYAN(el::Level::Info, "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " unblocked."); + MCLOG_CYAN(el::Level::Info, "global", "IP " << epee::string_tools::get_ip_string_from_int32(addr) << " unblocked."); return true; } //----------------------------------------------------------------------------------- @@ -926,6 +935,50 @@ namespace nodetool return true; } + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::check_connection_and_handshake_with_peer(const net_address& na, uint64_t last_seen_stamp) + { + LOG_PRINT_L1("Connecting to " << epee::string_tools::get_ip_string_from_int32(na.ip) << ":" + << epee::string_tools::num_to_string_fast(na.port) << "(last_seen: " + << (last_seen_stamp ? epee::misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never") + << ")..."); + + typename net_server::t_connection_context con = AUTO_VAL_INIT(con); + bool res = m_net_server.connect(epee::string_tools::get_ip_string_from_int32(na.ip), + epee::string_tools::num_to_string_fast(na.port), + m_config.m_net_config.connection_timeout, + con); + + if (!res) { + bool is_priority = is_priority_node(na); + + LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Connect failed to " + << epee::string_tools::get_ip_string_from_int32(na.ip) + << ":" << epee::string_tools::num_to_string_fast(na.port)); + + return false; + } + + peerid_type pi = AUTO_VAL_INIT(pi); + res = do_handshake_with_peer(pi, con, true); + + if (!res) { + bool is_priority = is_priority_node(na); + + LOG_PRINT_CC_PRIORITY_NODE(is_priority, con, "Failed to HANDSHAKE with peer " + << epee::string_tools::get_ip_string_from_int32(na.ip) + << ":" << epee::string_tools::num_to_string_fast(na.port)); + + return false; + } + + m_net_server.get_config_object().close(con.m_connection_id); + + LOG_DEBUG_CC(con, "CONNECTION HANDSHAKED OK AND CLOSED."); + + return true; + } + #undef LOG_PRINT_CC_PRIORITY_NODE //----------------------------------------------------------------------------------- @@ -1097,6 +1150,7 @@ namespace nodetool { m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::peer_sync_idle_maker, this)); m_connections_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::connections_maker, this)); + m_gray_peerlist_housekeeping_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::gray_peerlist_housekeeping, this)); m_peerlist_store_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::store_config, this)); return true; } @@ -1704,4 +1758,30 @@ namespace nodetool return count > max_connections; } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::gray_peerlist_housekeeping() + { + peerlist_entry pe = AUTO_VAL_INIT(pe); + + if (!m_peerlist.get_random_gray_peer(pe)) { + return false; + } + + bool success = check_connection_and_handshake_with_peer(pe.adr, pe.last_seen); + + if (!success) { + m_peerlist.remove_from_peer_gray(pe); + + LOG_PRINT_L2("PEER EVICTED FROM GRAY PEER LIST IP address: " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << " Peer ID: " << std::hex << pe.id); + + return true; + } + + m_peerlist.set_peer_just_seen(pe.id, pe.adr); + + LOG_PRINT_L2("PEER PROMOTED TO WHITE PEER LIST IP address: " << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << " Peer ID: " << std::hex << pe.id); + + return true; + } } diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index db9387ceb..160206ed3 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -81,6 +81,8 @@ namespace nodetool bool set_peer_just_seen(peerid_type peer, const net_address& addr); bool set_peer_unreachable(const peerlist_entry& pr); bool is_ip_allowed(uint32_t ip); + bool get_random_gray_peer(peerlist_entry& pe); + bool remove_from_peer_gray(const peerlist_entry& pe); private: @@ -393,9 +395,50 @@ namespace nodetool } return true; CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_random_gray_peer(peerlist_entry& pe) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + if (m_peers_gray.empty()) { + return false; + } + + size_t x = crypto::rand<size_t>() % (m_peers_gray.size() + 1); + size_t res = (x * x * x) / (m_peers_gray.size() * m_peers_gray.size()); //parabola \/ + + LOG_PRINT_L3("Random gray peer index=" << res << "(x="<< x << ", max_index=" << m_peers_gray.size() << ")"); + + peers_indexed::index<by_time>::type& by_time_index = m_peers_gray.get<by_time>(); + pe = *epee::misc_utils::move_it_backward(--by_time_index.end(), res); + return true; + + CATCH_ENTRY_L0("peerlist_manager::get_random_gray_peer()", false); } //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::remove_from_peer_gray(const peerlist_entry& pe) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + peers_indexed::index_iterator<by_addr>::type iterator = m_peers_gray.get<by_addr>().find(pe.adr); + + if (iterator != m_peers_gray.get<by_addr>().end()) { + m_peers_gray.erase(iterator); + } + + return true; + + CATCH_ENTRY_L0("peerlist_manager::remove_from_peer_gray()", false); + } + //-------------------------------------------------------------------------------------------------- } BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER) diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 74fed0ede..b8f3596e8 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -797,23 +797,6 @@ namespace rct { tools::thread_group threadpool(tools::thread_group::optimal_with_max(threads)); if (semantics) { - results.clear(); - results.resize(rv.outPk.size()); - tools::task_region(threadpool, [&] (tools::task_region_handle& region) { - for (size_t i = 0; i < rv.outPk.size(); i++) { - region.run([&, i] { - results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); - }); - } - }); - - for (size_t i = 0; i < results.size(); ++i) { - if (!results[i]) { - LOG_PRINT_L1("Range proof verified failed for output " << i); - return false; - } - } - key sumOutpks = identity(); for (size_t i = 0; i < rv.outPk.size(); i++) { addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); @@ -833,6 +816,23 @@ namespace rct { LOG_PRINT_L1("Sum check failed"); return false; } + + results.clear(); + results.resize(rv.outPk.size()); + tools::task_region(threadpool, [&] (tools::task_region_handle& region) { + for (size_t i = 0; i < rv.outPk.size(); i++) { + region.run([&, i] { + results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); + }); + } + }); + + for (size_t i = 0; i < results.size(); ++i) { + if (!results[i]) { + LOG_PRINT_L1("Range proof verified failed for output " << i); + return false; + } + } } else { const key message = get_pre_mlsag_hash(rv); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index b1aa243b9..0c27745e1 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -40,11 +40,11 @@ #include <cinttypes> extern "C" { -#include "crypto/generic-ops.h" #include "crypto/crypto-ops.h" #include "crypto/random.h" #include "crypto/keccak.h" } +#include "crypto/generic-ops.h" #include "crypto/crypto.h" #include "serialization/serialization.h" diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 1c5c321da..7d896e491 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -985,7 +985,8 @@ namespace cryptonote return false; } block blk; - bool have_block = m_core.get_block_by_hash(block_hash, blk); + bool orphan = false; + bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan); if (!have_block) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -999,7 +1000,7 @@ namespace cryptonote return false; } uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height; - bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.block_header); + bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header); if (!response_filled) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1123,7 +1124,8 @@ namespace cryptonote block_hash = m_core.get_block_id_by_height(req.height); } block blk; - bool have_block = m_core.get_block_by_hash(block_hash, blk); + bool orphan = false; + bool have_block = m_core.get_block_by_hash(block_hash, blk, &orphan); if (!have_block) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; @@ -1137,7 +1139,7 @@ namespace cryptonote return false; } uint64_t block_height = boost::get<txin_gen>(blk.miner_tx.vin.front()).height; - bool response_filled = fill_block_header_response(blk, false, block_height, block_hash, res.block_header); + bool response_filled = fill_block_header_response(blk, orphan, block_height, block_hash, res.block_header); if (!response_filled) { error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 84871e8bb..767bcc715 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -194,3 +194,5 @@ private: bool m_restricted; }; } + +BOOST_CLASS_VERSION(nodetool::node_server<cryptonote::t_cryptonote_protocol_handler<cryptonote::core> >, 1); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 52ecc2e6a..c1cc5d10d 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -197,6 +197,44 @@ bool Wallet::addressValid(const std::string &str, bool testnet) return get_account_integrated_address_from_str(address, has_payment_id, pid, testnet, str); } +bool Wallet::keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) +{ + bool has_payment_id; + cryptonote::account_public_address address; + crypto::hash8 pid; + if(!get_account_integrated_address_from_str(address, has_payment_id, pid, testnet, address_string)) { + error = tr("Failed to parse address"); + return false; + } + + cryptonote::blobdata key_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(secret_key_string, key_data) || key_data.size() != sizeof(crypto::secret_key)) + { + error = tr("Failed to parse key"); + return false; + } + crypto::secret_key key = *reinterpret_cast<const crypto::secret_key*>(key_data.data()); + + // check the key match the given address + crypto::public_key pkey; + if (!crypto::secret_key_to_public_key(key, pkey)) { + error = tr("failed to verify key"); + return false; + } + bool matchAddress = false; + if(isViewKey) + matchAddress = address.m_view_public_key == pkey; + else + matchAddress = address.m_spend_public_key == pkey; + + if(!matchAddress) { + error = tr("key does not match address"); + return false; + } + + return true; +} + std::string Wallet::paymentIdFromAddress(const std::string &str, bool testnet) { bool has_payment_id; @@ -337,6 +375,98 @@ bool WalletImpl::createWatchOnly(const std::string &path, const std::string &pas return true; } +bool WalletImpl::recoverFromKeys(const std::string &path, + const std::string &language, + const std::string &address_string, + const std::string &viewkey_string, + const std::string &spendkey_string) +{ + cryptonote::account_public_address address; + bool has_payment_id; + crypto::hash8 new_payment_id; + if(!get_account_integrated_address_from_str(address, has_payment_id, new_payment_id, m_wallet->testnet(), address_string)) + { + m_errorString = tr("failed to parse address"); + m_status = Status_Error; + return false; + } + + // parse optional spend key + crypto::secret_key spendkey; + bool has_spendkey = false; + if (!spendkey_string.empty()) { + cryptonote::blobdata spendkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) + { + m_errorString = tr("failed to parse secret spend key"); + m_status = Status_Error; + return false; + } + has_spendkey = true; + spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); + } + + // parse view secret key + if (viewkey_string.empty()) { + m_errorString = tr("No view key supplied, cancelled"); + m_status = Status_Error; + return false; + } + cryptonote::blobdata viewkey_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + { + m_errorString = tr("failed to parse secret view key"); + m_status = Status_Error; + return false; + } + crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); + + // check the spend and view keys match the given address + crypto::public_key pkey; + if(has_spendkey) { + if (!crypto::secret_key_to_public_key(spendkey, pkey)) { + m_errorString = tr("failed to verify secret spend key"); + m_status = Status_Error; + return false; + } + if (address.m_spend_public_key != pkey) { + m_errorString = tr("spend key does not match address"); + m_status = Status_Error; + return false; + } + } + if (!crypto::secret_key_to_public_key(viewkey, pkey)) { + m_errorString = tr("failed to verify secret view key"); + m_status = Status_Error; + return false; + } + if (address.m_view_public_key != pkey) { + m_errorString = tr("view key does not match address"); + m_status = Status_Error; + return false; + } + + try + { + if (has_spendkey) { + m_wallet->generate(path, "", address, spendkey, viewkey); + LOG_PRINT_L1("Generated new wallet from keys"); + } + else { + m_wallet->generate(path, "", address, viewkey); + LOG_PRINT_L1("Generated new view only wallet from keys"); + } + + } + catch (const std::exception& e) { + m_errorString = string(tr("failed to generate new wallet: ")) + e.what(); + m_status = Status_Error; + return false; + } + return true; +} + + bool WalletImpl::open(const std::string &path, const std::string &password) { clearStatus(); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index e3df7fd01..16a7f5d95 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -58,6 +58,11 @@ public: const std::string &language) const; bool open(const std::string &path, const std::string &password); bool recover(const std::string &path, const std::string &seed); + bool recoverFromKeys(const std::string &path, + const std::string &language, + const std::string &address_string, + const std::string &viewkey_string, + const std::string &spendkey_string = ""); bool close(); std::string seed() const; std::string getSeedLanguage() const; @@ -94,6 +99,7 @@ public: void setRecoveringFromSeed(bool recoveringFromSeed); bool watchOnly() const; bool rescanSpent(); + bool testnet() const {return m_wallet->testnet();} PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, diff --git a/src/wallet/api/wallet_manager.cpp b/src/wallet/api/wallet_manager.cpp index 904338a72..fcb39d1f3 100644 --- a/src/wallet/api/wallet_manager.cpp +++ b/src/wallet/api/wallet_manager.cpp @@ -72,6 +72,22 @@ Wallet *WalletManagerImpl::recoveryWallet(const std::string &path, const std::st return wallet; } +Wallet *WalletManagerImpl::createWalletFromKeys(const std::string &path, + const std::string &language, + bool testnet, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString) +{ + WalletImpl * wallet = new WalletImpl(testnet); + if(restoreHeight > 0){ + wallet->setRefreshFromBlockHeight(restoreHeight); + } + wallet->recoverFromKeys(path, language, addressString, viewKeyString, spendKeyString); + return wallet; +} + bool WalletManagerImpl::closeWallet(Wallet *wallet) { WalletImpl * wallet_ = dynamic_cast<WalletImpl*>(wallet); diff --git a/src/wallet/api/wallet_manager.h b/src/wallet/api/wallet_manager.h index ca9570254..ce9b70e96 100644 --- a/src/wallet/api/wallet_manager.h +++ b/src/wallet/api/wallet_manager.h @@ -41,6 +41,13 @@ public: const std::string &language, bool testnet); Wallet * openWallet(const std::string &path, const std::string &password, bool testnet); virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet, uint64_t restoreHeight); + virtual Wallet * createWalletFromKeys(const std::string &path, + const std::string &language, + bool testnet, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = ""); virtual bool closeWallet(Wallet *wallet); bool walletExists(const std::string &path); std::vector<std::string> findWallets(const std::string &path); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c08b16a5f..4348b8a62 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -87,6 +87,8 @@ using namespace cryptonote; #define FEE_ESTIMATE_GRACE_BLOCKS 10 // estimate fee valid for that many blocks +#define SECOND_OUTPUT_RELATEDNESS_THRESHOLD 0.0f + #define KILL_IOSERVICE() \ do { \ work.reset(); \ @@ -2720,6 +2722,19 @@ namespace vec.pop_back(); return res; } + + template<typename T> + void pop_if_present(std::vector<T>& vec, T e) + { + for (size_t i = 0; i < vec.size(); ++i) + { + if (e == vec[i]) + { + pop_index (vec, i); + return; + } + } + } } //---------------------------------------------------------------------------------------------------- // This returns a handwavy estimation of how much two outputs are related @@ -4028,6 +4043,17 @@ std::vector<size_t> wallet2::pick_preferred_rct_inputs(uint64_t needed_money) co return picks; } +static bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) +{ + if (!use_rct) + return false; + if (n_transfers > 1) + return false; + if (unused_dust_indices.empty() && unused_transfers_indices.empty()) + return false; + return true; +} + // Another implementation of transaction creation that is hopefully better // While there is anything left to pay, it goes through random outputs and tries // to fill the next destination/amount. If it fully fills it, it will use the @@ -4152,9 +4178,19 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // - we have something to send // - or we need to gather more fee // - or we have just one input in that tx, which is rct (to try and make all/most rct txes 2/2) - while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || (use_rct && txes.back().selected_transfers.size() == 1)) { + while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), unused_transfers_indices, unused_dust_indices)) { TX &tx = txes.back(); + LOG_PRINT_L2("Start of loop with " << unused_transfers_indices.size() << " " << unused_dust_indices.size()); + LOG_PRINT_L2("unused_transfers_indices:"); + for (auto t: unused_transfers_indices) + LOG_PRINT_L2(" " << t); + LOG_PRINT_L2("unused_dust_indices:"); + for (auto t: unused_dust_indices) + LOG_PRINT_L2(" " << t); + LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? -1 : dsts[0].amount)); + LOG_PRINT_L2("adding_fee " << adding_fee << ", use_rct " << use_rct); + // if we need to spend money and don't have any left, we fail if (unused_dust_indices.empty() && unused_transfers_indices.empty()) { LOG_PRINT_L2("No more outputs to choose from"); @@ -4167,9 +4203,20 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp if ((dsts.empty() || dsts[0].amount == 0) && !adding_fee) // the "make rct txes 2/2" case - we pick a small value output to "clean up" the wallet too idx = pop_best_value(unused_dust_indices.empty() ? unused_transfers_indices : unused_dust_indices, tx.selected_transfers, true); - else if (!prefered_inputs.empty()) + else if (!prefered_inputs.empty()) { idx = pop_back(prefered_inputs); - else + pop_if_present(unused_transfers_indices, idx); + pop_if_present(unused_dust_indices, idx); + + // since we're trying to add a second output which is not strictly needed, + // we only add it if it's unrelated enough to the first one + float relatedness = get_output_relatedness(m_transfers[idx], m_transfers[tx.selected_transfers.front()]); + if (relatedness > SECOND_OUTPUT_RELATEDNESS_THRESHOLD) + { + LOG_PRINT_L2("Second outout was not strictly needed, and relatedness " << relatedness << ", not adding"); + break; + } + } else idx = pop_best_value(unused_transfers_indices.empty() ? unused_dust_indices : unused_transfers_indices, tx.selected_transfers); const transfer_details &td = m_transfers[idx]; diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 78caddc0b..563f16eaa 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -295,6 +295,7 @@ struct Wallet virtual bool setPassword(const std::string &password) = 0; virtual std::string address() const = 0; virtual std::string path() const = 0; + virtual bool testnet() const = 0; /*! * \brief integratedAddress - returns integrated address for current wallet address and given payment_id. @@ -434,6 +435,7 @@ struct Wallet static std::string genPaymentId(); static bool paymentIdValid(const std::string &paiment_id); static bool addressValid(const std::string &str, bool testnet); + static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); static std::string paymentIdFromAddress(const std::string &str, bool testnet); static uint64_t maximumAllowedAmount(); @@ -613,6 +615,25 @@ struct WalletManager */ virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet = false, uint64_t restoreHeight = 0) = 0; + /*! + * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted + * \param path Name of wallet file to be created + * \param language language + * \param testnet testnet + * \param restoreHeight restore from start height + * \param addressString public address + * \param viewKeyString view key + * \param spendKeyString spend key (optional) + * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) + */ + virtual Wallet * createWalletFromKeys(const std::string &path, + const std::string &language, + bool testnet, + uint64_t restoreHeight, + const std::string &addressString, + const std::string &viewKeyString, + const std::string &spendKeyString = "") = 0; + /*! * \brief Closes wallet. In case operation succeded, wallet object deleted. in case operation failed, wallet object not deleted * \param wallet previously opened / created wallet instance diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 7ec4ad6e4..b7a4532fd 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -137,7 +137,7 @@ namespace wallet_args if (!vm["log-file"].defaulted()) log_path = command_line::get_arg(vm, arg_log_file); else - log_path = mlog_get_default_log_path("monero-wallet-cli,log"); + log_path = mlog_get_default_log_path("monero-wallet-cli.log"); mlog_configure(log_path, false); if (!vm["log-level"].defaulted()) { diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 33e099ceb..d35e51068 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -369,7 +369,7 @@ namespace tools cryptonote::set_payment_id_to_tx_extra_nonce(extra_nonce, long_payment_id); } /* or short payment ID */ - else if (!wallet2::parse_short_payment_id(payment_id_str, short_payment_id)) { + else if (wallet2::parse_short_payment_id(payment_id_str, short_payment_id)) { cryptonote::set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, short_payment_id); } else { |