diff options
-rw-r--r-- | src/cryptonote_config.h | 3 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.cpp | 5 | ||||
-rw-r--r-- | src/cryptonote_core/cryptonote_core.h | 14 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_defs.h | 47 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.h | 7 | ||||
-rw-r--r-- | src/cryptonote_protocol/cryptonote_protocol_handler.inl | 338 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.cpp | 2 | ||||
-rw-r--r-- | src/p2p/net_node.h | 8 | ||||
-rw-r--r-- | src/p2p/net_node.inl | 52 | ||||
-rw-r--r-- | src/p2p/net_node_common.h | 4 | ||||
-rw-r--r-- | src/p2p/p2p_protocol_defs.h | 23 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 40 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 3 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 15 | ||||
-rw-r--r-- | src/wallet/wallet2_api.h | 16 |
15 files changed, 547 insertions, 30 deletions
diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 175a5d26e..95cbf74f0 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -113,6 +113,9 @@ #define P2P_IP_FAILS_BEFORE_BLOCK 10 #define P2P_IDLE_CONNECTION_KILL_INTERVAL (5*60) //5 minutes +#define P2P_SUPPORT_FLAG_FLUFFY_BLOCKS 0x01 +#define P2P_SUPPORT_FLAGS P2P_SUPPORT_FLAG_FLUFFY_BLOCKS + #define ALLOW_DEBUG_COMMANDS #define CRYPTONOTE_NAME "bitmonero" diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 95c49f627..9deb8863d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -915,6 +915,11 @@ namespace cryptonote m_mempool.get_transactions(txs); return true; } + //----------------------------------------------------------------------------------------------- + bool core::get_pool_transaction(const crypto::hash &id, transaction& tx) const + { + return m_mempool.get_transaction(id, tx); + } //----------------------------------------------------------------------------------------------- bool core::get_pool_transactions_and_spent_keys_info(std::vector<tx_info>& tx_infos, std::vector<spent_key_image_info>& key_image_infos) const { diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index a5a97a5ad..5ddab4ed4 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -379,6 +379,13 @@ namespace cryptonote * @note see tx_memory_pool::get_transactions */ bool get_pool_transactions(std::list<transaction>& txs) const; + + /** + * @copydoc tx_memory_pool::get_transaction + * + * @note see tx_memory_pool::get_transaction + */ + bool get_pool_transaction(const crypto::hash& id, transaction& tx) const; /** * @copydoc tx_memory_pool::get_pool_transactions_and_spent_keys_info @@ -611,6 +618,13 @@ namespace cryptonote * @return the number of blocks to sync in one go */ std::pair<uint64_t, uint64_t> get_coinbase_tx_sum(const uint64_t start_offset, const size_t count); + + /** + * @brief get whether we're on testnet or not + * + * @return are we on testnet? + */ + bool get_testnet() const { return m_testnet; }; private: diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h index edcf2af8e..7adc69080 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_defs.h +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -69,6 +69,8 @@ namespace cryptonote uint64_t avg_upload; uint64_t current_upload; + + uint32_t support_flags; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(incoming) @@ -87,6 +89,7 @@ namespace cryptonote KV_SERIALIZE(current_download) KV_SERIALIZE(avg_upload) KV_SERIALIZE(current_upload) + KV_SERIALIZE(support_flags) END_KV_SERIALIZE_MAP() }; @@ -223,5 +226,49 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_NEW_FLUFFY_BLOCK + { + const static int ID = BC_COMMANDS_POOL_BASE + 8; + + struct request + { + block_complete_entry b; + uint64_t current_blockchain_height; + uint32_t hop; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(b) + KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE(hop) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_REQUEST_FLUFFY_MISSING_TX + { + const static int ID = BC_COMMANDS_POOL_BASE + 9; + struct request + { + block_complete_entry b; + uint64_t current_blockchain_height; + std::vector<size_t> missing_tx_indices; + uint32_t hop; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(b) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missing_tx_indices) + KV_SERIALIZE(hop) + KV_SERIALIZE(current_blockchain_height) + END_KV_SERIALIZE_MAP() + }; + }; + } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h index 1c92958c9..ab5d8230d 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.h +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -91,6 +91,8 @@ namespace cryptonote HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) HANDLE_NOTIFY_T2(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) + HANDLE_NOTIFY_T2(NOTIFY_NEW_FLUFFY_BLOCK, &cryptonote_protocol_handler::handle_notify_new_fluffy_block) + HANDLE_NOTIFY_T2(NOTIFY_REQUEST_FLUFFY_MISSING_TX, &cryptonote_protocol_handler::handle_request_fluffy_missing_tx) END_INVOKE_MAP2() bool on_idle(); @@ -115,8 +117,9 @@ namespace cryptonote int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context); int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context); int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); - - + int handle_notify_new_fluffy_block(int command, NOTIFY_NEW_FLUFFY_BLOCK::request& arg, cryptonote_connection_context& context); + int handle_request_fluffy_missing_tx(int command, NOTIFY_REQUEST_FLUFFY_MISSING_TX::request& arg, cryptonote_connection_context& context); + //----------------- i_bc_protocol_layout --------------------------------------- virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context); diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 10756a8ad..65377f990 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -37,6 +37,7 @@ #include <boost/interprocess/detail/atomic.hpp> #include <list> +#include <unordered_map> #include "cryptonote_core/cryptonote_format_utils.h" #include "profile_tools.h" @@ -124,6 +125,7 @@ namespace cryptonote ss << std::setw(30) << std::left << "Remote Host" << std::setw(20) << "Peer id" + << std::setw(20) << "Support Flags" << std::setw(30) << "Recv/Sent (inactive,sec)" << std::setw(25) << "State" << std::setw(20) << "Livetime(sec)" @@ -134,7 +136,7 @@ namespace cryptonote << ENDL; uint32_t ip; - m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) { bool local_ip = false; ip = ntohl(cntxt.m_remote_ip); @@ -145,6 +147,7 @@ namespace cryptonote ss << std::setw(30) << std::left << std::string(cntxt.m_is_income ? " [INC]":"[OUT]") + epee::string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) + ":" + std::to_string(cntxt.m_remote_port) << std::setw(20) << std::hex << peer_id + << std::setw(20) << std::hex << support_flags << std::setw(30) << std::to_string(cntxt.m_recv_cnt)+ "(" + std::to_string(time(NULL) - cntxt.m_last_recv) + ")" + "/" + std::to_string(cntxt.m_send_cnt) + "(" + std::to_string(time(NULL) - cntxt.m_last_send) + ")" << std::setw(25) << get_protocol_state_string(cntxt.m_state) << std::setw(20) << std::to_string(time(NULL) - cntxt.m_started) @@ -184,7 +187,7 @@ namespace cryptonote { std::list<connection_info> connections; - m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id) + m_p2p->for_each_connection([&](const connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) { connection_info cnx; auto timestamp = time(NULL); @@ -197,6 +200,8 @@ namespace cryptonote std::stringstream peer_id_str; peer_id_str << std::hex << peer_id; peer_id_str >> cnx.peer_id; + + cnx.support_flags = support_flags; cnx.recv_count = cntxt.m_recv_cnt; cnx.recv_idle_time = timestamp - cntxt.m_last_recv; @@ -361,6 +366,312 @@ 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 << ")"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; + + m_core.pause_mine(); + + block new_block; + transaction miner_tx; + if(parse_and_validate_block_from_blob(arg.b.block, new_block)) + { + // This is a seccond notification, we must have asked for some missing tx + if(!context.m_requested_objects.empty()) + { + // What we asked for != to what we received .. + if(context.m_requested_objects.size() != arg.b.txs.size()) + { + LOG_ERROR_CCONTEXT + ( + "NOTIFY_NEW_FLUFFY_BLOCK -> request/response mismatch, " + << "block = " << epee::string_tools::pod_to_hex(get_blob_hash(arg.b.block)) + << ", requested = " << context.m_requested_objects.size() + << ", received = " << new_block.tx_hashes.size() + << ", dropping connection" + ); + + m_p2p->drop_connection(context); + m_core.resume_mine(); + return 1; + } + } + + std::list<blobdata> have_tx; + + // Instead of requesting missing transactions by hash like BTC, + // we do it by index (thanks to a suggestion from moneromooo) because + // we're way cooler .. and also because they're smaller than hashes. + // + // Also, remember to pepper some whitespace changes around to bother + // moneromooo ... only because I <3 him. + std::vector<size_t> need_tx_indices; + + transaction tx; + crypto::hash tx_hash; + + BOOST_FOREACH(auto& tx_blob, arg.b.txs) + { + if(parse_and_validate_tx_from_blob(tx_blob, tx)) + { + try + { + if(!get_transaction_hash(tx, tx_hash)) + { + LOG_PRINT_CCONTEXT_L1 + ( + "NOTIFY_NEW_FLUFFY_BLOCK: get_transaction_hash failed" + << ", dropping connection" + ); + + m_p2p->drop_connection(context); + m_core.resume_mine(); + return 1; + } + } + catch(...) + { + LOG_PRINT_CCONTEXT_L1 + ( + "NOTIFY_NEW_FLUFFY_BLOCK: get_transaction_hash failed" + << ", exception thrown" + << ", dropping connection" + ); + + m_p2p->drop_connection(context); + m_core.resume_mine(); + return 1; + } + + // hijacking m_requested objects in connection context to patch up + // a possible DOS vector pointed out by @monero-moo where peers keep + // sending (0...n-1) transactions. + // If requested objects is not empty, then we must have asked for + // some missing transacionts, make sure that they're all there. + // + // Can I safely re-use this field? I think so, but someone check me! + if(!context.m_requested_objects.empty()) + { + auto req_tx_it = context.m_requested_objects.find(tx_hash); + if(req_tx_it == context.m_requested_objects.end()) + { + LOG_ERROR_CCONTEXT + ( + "Peer sent wrong transaction (NOTIFY_NEW_FLUFFY_BLOCK): " + << "transaction with id = " << tx_hash << " wasn't requested, " + << "dropping connection" + ); + + m_p2p->drop_connection(context); + m_core.resume_mine(); + return 1; + } + + context.m_requested_objects.erase(req_tx_it); + } + + // we might already have the tx that the peer + // sent in our pool, so don't verify again.. + if(!m_core.get_pool_transaction(tx_hash, tx)) + { + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + if(!m_core.handle_incoming_tx(tx_blob, tvc, true, true) || tvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L1("Block verification failed: transaction verification failed, dropping connection"); + m_p2p->drop_connection(context); + m_core.resume_mine(); + return 1; + } + + // + // future todo: + // tx should only not be added to pool if verification failed, but + // maybe in the future could not be added for other reasons + // according to monero-moo so keep track of these separately .. + // + } + } + else + { + LOG_ERROR_CCONTEXT + ( + "sent wrong tx: failed to parse and validate transaction: \r\n" + << epee::string_tools::buff_to_hex_nodelimer(tx_blob) + << "\r\n dropping connection" + ); + + m_p2p->drop_connection(context); + m_core.resume_mine(); + return 1; + } + } + + // The initial size equality check could have been fooled if the sender + // gave us the number of transactions we asked for, but not the right + // ones. This check make sure the transactions we asked for were the + // ones we received. + if(context.m_requested_objects.size()) + { + LOG_PRINT_CCONTEXT_RED + ( + "NOTIFY_NEW_FLUFFY_BLOCK: peer sent the number of transaction requested" + << ", but not the actual transactions requested" + << ", context.m_requested_objects.size() = " << context.m_requested_objects.size() + << ", dropping connection", LOG_LEVEL_0 + ); + + m_p2p->drop_connection(context); + m_core.resume_mine(); + return 1; + } + + size_t tx_idx = 0; + BOOST_FOREACH(auto& tx_hash, new_block.tx_hashes) + { + if(m_core.get_pool_transaction(tx_hash, tx)) + { + have_tx.push_back(tx_to_blob(tx)); + } + else + { + need_tx_indices.push_back(tx_idx); + } + + ++tx_idx; + } + + if(!need_tx_indices.empty()) // drats, we don't have everything.. + { + // request non-mempool txs + NOTIFY_REQUEST_FLUFFY_MISSING_TX::request missing_tx_req; + missing_tx_req.b = arg.b; + missing_tx_req.hop = arg.hop; + missing_tx_req.current_blockchain_height = arg.current_blockchain_height; + missing_tx_req.missing_tx_indices = std::move(need_tx_indices); + + m_core.resume_mine(); + post_notify<NOTIFY_REQUEST_FLUFFY_MISSING_TX>(missing_tx_req, context); + } + else // whoo-hoo we've got em all .. + { + block_complete_entry b; + b.block = arg.b.block; + b.txs = have_tx; + + std::list<block_complete_entry> blocks; + blocks.push_back(b); + m_core.prepare_handle_incoming_blocks(blocks); + + block_verification_context bvc = boost::value_initialized<block_verification_context>(); + m_core.handle_incoming_block(arg.b.block, bvc); // got block from handle_notify_new_block + m_core.cleanup_handle_incoming_blocks(true); + m_core.resume_mine(); + + if( bvc.m_verifivation_failed ) + { + LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if( bvc.m_added_to_main_chain ) + { + ++arg.hop; + //TODO: Add here announce protocol usage + NOTIFY_NEW_BLOCK::request reg_arg = AUTO_VAL_INIT(reg_arg); + reg_arg.hop = arg.hop; + reg_arg.current_blockchain_height = arg.current_blockchain_height; + reg_arg.b.block = b.block; + relay_block(reg_arg, context); + } + else if( bvc.m_marked_as_orphaned ) + { + context.m_state = cryptonote_connection_context::state_synchronizing; + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized<NOTIFY_REQUEST_CHAIN::request>(); + m_core.get_short_chain_history(r.block_ids); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + post_notify<NOTIFY_REQUEST_CHAIN>(r, context); + } + } + } + else + { + LOG_ERROR_CCONTEXT + ( + "sent wrong block: failed to parse and validate block: \r\n" + << epee::string_tools::buff_to_hex_nodelimer(arg.b.block) + << "\r\n dropping connection" + ); + + m_core.resume_mine(); + m_p2p->drop_connection(context); + + return 1; + } + + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + 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"); + + std::list<block> local_blocks; + std::list<transaction> local_txs; + if(!m_core.get_blocks(arg.current_blockchain_height - 1, 1, local_blocks, local_txs)) + { + + LOG_ERROR_CCONTEXT + ( + "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX" + << ", get_blocks( start_offset = " << (arg.current_blockchain_height - 1) << " ) failed" + << ", dropping connection" + ); + + m_p2p->drop_connection(context); + return 1; + } + + NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_response; + fluffy_response.b = arg.b; + fluffy_response.current_blockchain_height = m_core.get_current_blockchain_height(); + fluffy_response.hop = arg.hop; + size_t local_txs_count = local_txs.size(); + BOOST_FOREACH(auto& tx_idx, arg.missing_tx_indices) + { + if(tx_idx < local_txs_count) + { + fluffy_response.b.txs.push_back(t_serializable_object_to_blob( *(std::next(local_txs.begin(), tx_idx)) )); + } + else + { + LOG_ERROR_CCONTEXT + ( + "Failed to handle request NOTIFY_REQUEST_FLUFFY_MISSING_TX" + << ", request is asking for a tx whose index is out of bounds " + << ", tx index = " << tx_idx << ", block_height = " << arg.current_blockchain_height + << ", dropping connection" + ); + + m_p2p->drop_connection(context); + return 1; + } + } + + LOG_PRINT_CCONTEXT_L2 + ( + "-->>NOTIFY_RESPONSE_FLUFFY_MISSING_TX: " + << ", txs.size()=" << fluffy_response.b.txs.size() + << ", rsp.current_blockchain_height=" << fluffy_response.current_blockchain_height + ); + + post_notify<NOTIFY_NEW_FLUFFY_BLOCK>(fluffy_response, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + 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"); @@ -775,7 +1086,28 @@ namespace cryptonote template<class t_core> bool t_cryptonote_protocol_handler<t_core>::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) { - return relay_post_notify<NOTIFY_NEW_BLOCK>(arg, exclude_context); + NOTIFY_NEW_FLUFFY_BLOCK::request fluffy_arg = AUTO_VAL_INIT(fluffy_arg); + fluffy_arg.hop = arg.hop; + fluffy_arg.current_blockchain_height = arg.current_blockchain_height; + std::list<blobdata> fluffy_txs; + fluffy_arg.b = arg.b; + fluffy_arg.b.txs = fluffy_txs; + + m_p2p->for_each_connection([this, &arg, &fluffy_arg](connection_context& cntxt, nodetool::peerid_type peer_id, uint32_t support_flags) + { + if(m_core.get_testnet() && support_flags & P2P_SUPPORT_FLAG_FLUFFY_BLOCKS) + { + LOG_PRINT_YELLOW("PEER SUPPORTS FLUFFY BLOCKS - RELAYING THIN/COMPACT WHATEVER BLOCK", LOG_LEVEL_1); + return post_notify<NOTIFY_NEW_FLUFFY_BLOCK>(fluffy_arg, cntxt); + } + else + { + LOG_PRINT_YELLOW("PEER DOESN'T SUPPORT FLUFFY BLOCKS - RELAYING FULL BLOCK", LOG_LEVEL_1); + return post_notify<NOTIFY_NEW_BLOCK>(arg, cntxt); + } + }); + + return 1; } //------------------------------------------------------------------------------------------------------------------------ template<class t_core> diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 7d50ae76b..65b42cc47 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -404,6 +404,7 @@ bool t_rpc_command_executor::print_connections() { tools::msg_writer() << std::setw(30) << std::left << "Remote Host" << std::setw(20) << "Peer id" + << std::setw(20) << "Support Flags" << std::setw(30) << "Recv/Sent (inactive,sec)" << std::setw(25) << "State" << std::setw(20) << "Livetime(sec)" @@ -422,6 +423,7 @@ bool t_rpc_command_executor::print_connections() { //<< std::setw(30) << std::left << in_out << std::setw(30) << std::left << address << std::setw(20) << info.peer_id + << std::setw(20) << info.support_flags << std::setw(30) << std::to_string(info.recv_count) + "(" + std::to_string(info.recv_idle_time) + ")/" + std::to_string(info.send_count) + "(" + std::to_string(info.send_idle_time) + ")" << std::setw(25) << info.state << std::setw(20) << info.live_time diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 5943c248f..619bad40f 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -63,6 +63,7 @@ namespace nodetool struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base { peerid_type peer_id; + uint32_t support_flags; }; template<class t_payload_net_handler> @@ -146,6 +147,7 @@ namespace nodetool HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) #endif + HANDLE_INVOKE_T2(COMMAND_REQUEST_SUPPORT_FLAGS, &node_server::handle_get_support_flags) CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) END_INVOKE_MAP2() @@ -158,6 +160,7 @@ namespace nodetool int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context); int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context); #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_config(); bool store_config(); @@ -174,7 +177,7 @@ namespace nodetool virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context); virtual bool drop_connection(const epee::net_utils::connection_context_base& context); virtual void request_callback(const epee::net_utils::connection_context_base& context); - virtual void for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type)> f); + virtual void for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f); virtual bool add_ip_fail(uint32_t address); //----------------- i_connection_filter -------------------------------------------------------- virtual bool is_remote_ip_allowed(uint32_t adress); @@ -204,6 +207,7 @@ namespace nodetool bool is_addr_connected(const net_address& peer); template<class t_callback> bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); + bool try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f); bool make_expected_connections_count(bool white_list, size_t expected_connections); void cache_connect_fail_info(const net_address& addr); bool is_addr_recently_failed(const net_address& addr); @@ -240,10 +244,12 @@ namespace nodetool { network_config m_net_config; uint64_t m_peer_id; + uint32_t m_support_flags; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(m_net_config) KV_SERIALIZE(m_peer_id) + KV_SERIALIZE(m_support_flags) END_KV_SERIALIZE_MAP() }; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 04d73ba95..d8dadae1f 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -158,6 +158,7 @@ namespace nodetool m_config.m_net_config.connection_timeout = P2P_DEFAULT_CONNECTION_TIMEOUT; m_config.m_net_config.ping_connection_timeout = P2P_DEFAULT_PING_CONNECTION_TIMEOUT; m_config.m_net_config.send_peerlist_sz = P2P_DEFAULT_PEERS_IN_HANDSHAKE; + m_config.m_support_flags = P2P_SUPPORT_FLAGS; m_first_connection_maker_call = true; CATCH_ENTRY_L0("node_server::init_config", false); @@ -165,10 +166,10 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - void node_server<t_payload_net_handler>::for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type)> f) + void node_server<t_payload_net_handler>::for_each_connection(std::function<bool(typename t_payload_net_handler::connection_context&, peerid_type, uint32_t)> f) { m_net_server.get_config_object().foreach_connection([&](p2p_connection_context& cntx){ - return f(cntx, cntx.peer_id); + return f(cntx, cntx.peer_id, cntx.support_flags); }); } //----------------------------------------------------------------------------------- @@ -730,6 +731,13 @@ namespace nodetool LOG_PRINT_CC_L1(context_, "COMMAND_HANDSHAKE Failed"); m_net_server.get_config_object().close(context_.m_connection_id); } + else + { + try_get_support_flags(context_, [](p2p_connection_context& flags_context, const uint32_t& support_flags) + { + flags_context.support_flags = support_flags; + }); + } return hsh_result; } @@ -1221,6 +1229,13 @@ namespace nodetool #endif //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + int node_server<t_payload_net_handler>::handle_get_support_flags(int command, COMMAND_REQUEST_SUPPORT_FLAGS::request& arg, COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context) + { + rsp.support_flags = m_config.m_support_flags; + return 1; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> void node_server<t_payload_net_handler>::request_callback(const epee::net_utils::connection_context_base& context) { m_net_server.get_config_object().request_callback(context.m_connection_id); @@ -1338,6 +1353,32 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::try_get_support_flags(const p2p_connection_context& context, std::function<void(p2p_connection_context&, const uint32_t&)> f) + { + COMMAND_REQUEST_SUPPORT_FLAGS::request support_flags_request; + bool r = epee::net_utils::async_invoke_remote_command2<typename COMMAND_REQUEST_SUPPORT_FLAGS::response> + ( + context.m_connection_id, + COMMAND_REQUEST_SUPPORT_FLAGS::ID, + support_flags_request, + m_net_server.get_config_object(), + [=](int code, const typename COMMAND_REQUEST_SUPPORT_FLAGS::response& rsp, p2p_connection_context& context_) + { + if(code < 0) + { + LOG_PRINT_CC_RED(context_, "COMMAND_REQUEST_SUPPORT_FLAGS invoke failed. (" << code << ", " << epee::levin::get_err_descr(code) << ")", LOG_LEVEL_1); + return; + } + + f(context_, rsp.support_flags); + }, + P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT + ); + + return r; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> int node_server<t_payload_net_handler>::handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) { if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) @@ -1394,7 +1435,7 @@ namespace nodetool if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) { peerid_type peer_id_l = arg.node_data.peer_id; - uint32_t port_l = arg.node_data.my_port; + uint32_t port_l = arg.node_data.my_port; //try ping to be sure that we can add this peer to peer_list try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]() { @@ -1410,6 +1451,11 @@ namespace nodetool LOG_PRINT_CCONTEXT_L2("PING SUCCESS " << epee::string_tools::get_ip_string_from_int32(context.m_remote_ip) << ":" << port_l); }); } + + try_get_support_flags(context, [](p2p_connection_context& flags_context, const uint32_t& support_flags) + { + flags_context.support_flags = support_flags; + }); //fill response m_peerlist.get_peerlist_head(rsp.local_peerlist); diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 5e7645365..846c07779 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -49,7 +49,7 @@ namespace nodetool virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; virtual uint64_t get_connections_count()=0; - virtual void for_each_connection(std::function<bool(t_connection_context&, peerid_type)> f)=0; + virtual void for_each_connection(std::function<bool(t_connection_context&, peerid_type, uint32_t)> f)=0; virtual bool block_ip(uint32_t adress, time_t seconds = 0)=0; virtual bool unblock_ip(uint32_t adress)=0; virtual std::map<uint32_t, time_t> get_blocked_ips()=0; @@ -79,7 +79,7 @@ namespace nodetool { } - virtual void for_each_connection(std::function<bool(t_connection_context&,peerid_type)> f) + virtual void for_each_connection(std::function<bool(t_connection_context&,peerid_type,uint32_t)> f) { } diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index bad4c89e3..ce1ee1449 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -332,6 +332,29 @@ namespace nodetool }; }; + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_REQUEST_SUPPORT_FLAGS + { + const static int ID = P2P_COMMANDS_POOL_BASE + 7; + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint32_t support_flags; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(support_flags) + END_KV_SERIALIZE_MAP() + }; + }; + #endif diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 6c1c1fea2..134ea601c 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -201,6 +201,7 @@ WalletImpl::WalletImpl(bool testnet) , m_wallet2Callback(nullptr) , m_recoveringFromSeed(false) , m_synchronized(false) + , m_rebuildWalletCache(false) { m_wallet = new tools::wallet2(testnet); m_history = new TransactionHistoryImpl(this); @@ -269,6 +270,14 @@ bool WalletImpl::open(const std::string &path, const std::string &password) m_recoveringFromSeed = false; try { // TODO: handle "deprecated" + // Check if wallet cache exists + bool keys_file_exists; + bool wallet_file_exists; + tools::wallet2::wallet_exists(path, keys_file_exists, wallet_file_exists); + if(!wallet_file_exists){ + // Rebuilding wallet cache, using refresh height from .keys file + m_rebuildWalletCache = true; + } m_wallet->load(path, password); m_password = password; @@ -540,15 +549,14 @@ int WalletImpl::autoRefreshInterval() const // - unconfirmed_transfer_details; // - confirmed_transfer_details) -PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, uint64_t amount, uint32_t mixin_count, +PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, PendingTransaction::Priority priority) { clearStatus(); // Pause refresh thread while creating transaction pauseRefresh(); - vector<cryptonote::tx_destination_entry> dsts; - cryptonote::tx_destination_entry de; + cryptonote::account_public_address addr; // indicates if dst_addr is integrated address (address + payment_id) bool has_payment_id; @@ -561,7 +569,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); do { - if(!cryptonote::get_account_integrated_address_from_str(de.addr, has_payment_id, payment_id_short, m_wallet->testnet(), dst_addr)) { + if(!cryptonote::get_account_integrated_address_from_str(addr, has_payment_id, payment_id_short, m_wallet->testnet(), dst_addr)) { // TODO: copy-paste 'if treating as an address fails, try as url' from simplewallet.cpp:1982 m_status = Status_Error; m_errorString = "Invalid destination address"; @@ -595,14 +603,23 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const } } - de.amount = amount; - dsts.push_back(de); //std::vector<tools::wallet2::pending_tx> ptx_vector; try { - transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, - static_cast<uint32_t>(priority), - extra, m_trustedDaemon); + if (amount) { + vector<cryptonote::tx_destination_entry> dsts; + cryptonote::tx_destination_entry de; + de.addr = addr; + de.amount = *amount; + dsts.push_back(de); + transaction->m_pending_tx = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, + static_cast<uint32_t>(priority), + extra, m_trustedDaemon); + } else { + transaction->m_pending_tx = m_wallet->create_transactions_all(addr, fake_outs_count, 0 /* unlock_time */, + static_cast<uint32_t>(priority), + extra, m_trustedDaemon); + } } catch (const tools::error::daemon_busy&) { // TODO: make it translatable with "tr"? @@ -990,8 +1007,9 @@ bool WalletImpl::isNewWallet() const { // in case wallet created without daemon connection, closed and opened again, // it's the same case as if it created from scratch, i.e. we need "fast sync" - // with the daemon (pull hashes instead of pull blocks) - return !(blockChainHeight() > 1 || m_recoveringFromSeed); + // with the daemon (pull hashes instead of pull blocks). + // If wallet cache is rebuilt, creation height stored in .keys is used. + return !(blockChainHeight() > 1 || m_recoveringFromSeed || m_rebuildWalletCache); } void WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index f40551fac..372d6efd7 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -89,7 +89,7 @@ public: PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, - uint64_t amount, uint32_t mixin_count, + optional<uint64_t> amount, uint32_t mixin_count, PendingTransaction::Priority priority = PendingTransaction::Priority_Low); virtual PendingTransaction * createSweepUnmixableTransaction(); @@ -145,6 +145,7 @@ private: // instead of pulling hashes (fast-refresh) bool m_recoveringFromSeed; std::atomic<bool> m_synchronized; + bool m_rebuildWalletCache; }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ac8802ca4..1e3bb6253 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2972,9 +2972,9 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si break; } } - LOG_PRINT_L1("" << num_outs << " outputs of size " << print_money(amount)); + LOG_PRINT_L1("" << num_outs << " unlocked outputs of size " << print_money(amount)); THROW_WALLET_EXCEPTION_IF(num_outs == 0, error::wallet_internal_error, - "histogram reports no outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours"); + "histogram reports no unlocked outputs for " + boost::lexical_cast<std::string>(amount) + ", not even ours"); THROW_WALLET_EXCEPTION_IF(num_recent_outs > num_outs, error::wallet_internal_error, "histogram reports more recent outs than outs for " + boost::lexical_cast<std::string>(amount)); @@ -2984,7 +2984,7 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si recent_outputs_count = 1; // ensure we have at least one, if possible if (recent_outputs_count > num_recent_outs) recent_outputs_count = num_recent_outs; - if (td.m_global_output_index >= num_outs - num_recent_outs) + if (td.m_global_output_index >= num_outs - num_recent_outs && recent_outputs_count > 0) --recent_outputs_count; // if the real out is recent, pick one less recent fake out LOG_PRINT_L1("Using " << recent_outputs_count << " recent outputs"); @@ -3023,6 +3023,9 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); i = (uint64_t)(frac*num_recent_outs) + num_outs - num_recent_outs; + // just in case rounding up to 1 occurs after calc + if (i == num_outs) + --i; LOG_PRINT_L2("picking " << i << " as recent"); } else @@ -3031,11 +3034,11 @@ void wallet2::get_outs(std::vector<std::vector<entry>> &outs, const std::list<si uint64_t r = crypto::rand<uint64_t>() % ((uint64_t)1 << 53); double frac = std::sqrt((double)r / ((uint64_t)1 << 53)); i = (uint64_t)(frac*num_outs); + // just in case rounding up to 1 occurs after calc + if (i == num_outs) + --i; LOG_PRINT_L2("picking " << i << " as triangular"); } - // just in case rounding up to 1 occurs after calc - if (i == num_outs) - --i; if (seen_indices.count(i)) continue; diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index f0a9ea68b..6fe15d1e3 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -41,6 +41,20 @@ namespace Bitmonero { namespace Utils { bool isAddressLocal(const std::string &hostaddr); } + + template<typename T> + class optional { + public: + optional(): set(false) {} + optional(const T &t): t(t), set(true) {} + const T &operator*() const { return t; } + T &operator*() { return t; } + operator bool() const { return set; } + private: + T t; + bool set; + }; + /** * @brief Transaction-like interface for sending money */ @@ -332,7 +346,7 @@ struct Wallet */ virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, - uint64_t amount, uint32_t mixin_count, + optional<uint64_t> amount, uint32_t mixin_count, PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; /*! |