diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptonote_core/cryptonote_basic.h | 4 | ||||
-rw-r--r-- | src/daemon/command_parser_executor.cpp | 29 | ||||
-rw-r--r-- | src/daemon/command_parser_executor.h | 6 | ||||
-rw-r--r-- | src/daemon/command_server.cpp | 15 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.cpp | 110 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.h | 6 | ||||
-rw-r--r-- | src/p2p/net_node.h | 4 | ||||
-rw-r--r-- | src/p2p/net_node.inl | 18 | ||||
-rw-r--r-- | src/p2p/net_node_common.h | 14 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 52 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.h | 30 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 66 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 113 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.h | 9 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 146 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 15 |
16 files changed, 579 insertions, 58 deletions
diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 94887b5a0..aa8ba4f39 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -284,8 +284,8 @@ namespace cryptonote uint32_t nonce; BEGIN_SERIALIZE() - VARINT_FIELD(major_version) - VARINT_FIELD(minor_version) + FIELD(major_version) + FIELD(minor_version) VARINT_FIELD(timestamp) FIELD(prev_id) FIELD(nonce) diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index a07bb25de..487d86071 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -392,5 +392,34 @@ bool t_command_parser_executor::hard_fork_info(const std::vector<std::string>& a return m_executor.hard_fork_info(version); } +bool t_command_parser_executor::show_bans(const std::vector<std::string>& args) +{ + if (!args.empty()) return false; + return m_executor.print_bans(); +} + +bool t_command_parser_executor::ban(const std::vector<std::string>& args) +{ + if (args.size() != 1 && args.size() != 2) return false; + std::string ip = args[0]; + time_t seconds = P2P_IP_BLOCKTIME; + if (args.size() > 1) + { + seconds = std::stoi(args[0]); + if (seconds == 0) + { + return false; + } + } + return m_executor.ban(ip, seconds); +} + +bool t_command_parser_executor::unban(const std::vector<std::string>& args) +{ + if (args.size() != 1) return false; + std::string ip = args[0]; + return m_executor.unban(ip); +} + } // namespace daemonize diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index f00fbd77e..0c042cd5d 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -106,6 +106,12 @@ public: bool stop_save_graph(const std::vector<std::string>& args); bool hard_fork_info(const std::vector<std::string>& args); + + bool show_bans(const std::vector<std::string>& args); + + bool ban(const std::vector<std::string>& args); + + bool unban(const std::vector<std::string>& args); }; } // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 8714b2569..0999ed30c 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -194,6 +194,21 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::hard_fork_info, &m_parser, p::_1) , "Print hard fork voting information" ); + m_command_lookup.set_handler( + "bans" + , std::bind(&t_command_parser_executor::show_bans, &m_parser, p::_1) + , "Show the currently banned IPs" + ); + m_command_lookup.set_handler( + "ban" + , std::bind(&t_command_parser_executor::ban, &m_parser, p::_1) + , "Ban a given IP for a time" + ); + m_command_lookup.set_handler( + "unban" + , std::bind(&t_command_parser_executor::unban, &m_parser, p::_1) + , "Unban a given IP" + ); } bool t_command_server::process_command_str(const std::string& cmd) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 176df81fc..74dbc0012 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -242,7 +242,7 @@ bool t_rpc_command_executor::show_difficulty() { tools::success_msg_writer() << "BH: " << res.height << ", DIFF: " << res.difficulty - << ", HR: " << (int) res.difficulty / 60L << " H/s"; + << ", HR: " << (int) res.difficulty / res.target << " H/s"; return true; } @@ -287,7 +287,7 @@ bool t_rpc_command_executor::show_status() { % (100.0f * ires.height / (ires.target_height ? ires.target_height < ires.height ? ires.height : ires.target_height : ires.height)) % (ires.testnet ? "testnet" : "mainnet") % [&ires]()->std::string { - float hr = ires.difficulty / 60.0f; + float hr = ires.difficulty / ires.target; if (hr>1e9) return (boost::format("%.2f GH/s") % (hr/1e9)).str(); if (hr>1e6) return (boost::format("%.2f MH/s") % (hr/1e6)).str(); if (hr>1e3) return (boost::format("%.2f kH/s") % (hr/1e3)).str(); @@ -1036,4 +1036,110 @@ bool t_rpc_command_executor::hard_fork_info(uint8_t version) return true; } +bool t_rpc_command_executor::print_bans() +{ + cryptonote::COMMAND_RPC_GETBANS::request req; + cryptonote::COMMAND_RPC_GETBANS::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "get_bans", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_get_bans(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + time_t now = time(nullptr); + for (auto i = res.bans.begin(); i != res.bans.end(); ++i) + { + time_t seconds = i->seconds - now; + tools::msg_writer() << epee::string_tools::get_ip_string_from_int32(i->ip) << " banned for " << seconds << " seconds"; + } + + return true; +} + + +bool t_rpc_command_executor::ban(const std::string &ip, time_t seconds) +{ + cryptonote::COMMAND_RPC_SETBANS::request req; + cryptonote::COMMAND_RPC_SETBANS::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + cryptonote::COMMAND_RPC_SETBANS::ban ban; + if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip)) + { + tools::fail_msg_writer() << "Invalid IP"; + return true; + } + ban.ban = true; + ban.seconds = seconds; + req.bans.push_back(ban); + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "set_bans", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_set_bans(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + return true; +} + +bool t_rpc_command_executor::unban(const std::string &ip) +{ + cryptonote::COMMAND_RPC_SETBANS::request req; + cryptonote::COMMAND_RPC_SETBANS::response res; + std::string fail_message = "Unsuccessful"; + epee::json_rpc::error error_resp; + + cryptonote::COMMAND_RPC_SETBANS::ban ban; + if (!epee::string_tools::get_ip_int32_from_string(ban.ip, ip)) + { + tools::fail_msg_writer() << "Invalid IP"; + return true; + } + ban.ban = false; + ban.seconds = 0; + req.bans.push_back(ban); + + if (m_is_rpc) + { + if (!m_rpc_client->json_rpc_request(req, res, "set_bans", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_set_bans(req, res, error_resp)) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index 778b73acb..95c5624fa 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -124,6 +124,12 @@ public: bool stop_save_graph(); bool hard_fork_info(uint8_t version); + + bool print_bans(); + + bool ban(const std::string &ip, time_t seconds); + + bool unban(const std::string &ip); }; } // namespace daemonize diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 4aaac813e..39cbe01fa 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -117,6 +117,9 @@ namespace nodetool size_t get_outgoing_connections_count(); peerlist_manager& get_peerlist_manager(){return m_peerlist;} void delete_connections(size_t count); + virtual bool block_ip(uint32_t adress, time_t seconds = P2P_IP_BLOCKTIME); + virtual bool unblock_ip(uint32_t address); + virtual std::map<uint32_t, time_t> get_blocked_ips() const { return m_blocked_ips; } private: const std::vector<std::string> m_seed_nodes_list = { "seeds.moneroseeds.se" @@ -171,7 +174,6 @@ namespace nodetool 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 bool block_ip(uint32_t adress); virtual bool add_ip_fail(uint32_t address); //----------------- i_connection_filter -------------------------------------------------------- virtual bool is_remote_ip_allowed(uint32_t adress); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 11df7ee49..067f6378d 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -169,7 +169,7 @@ namespace nodetool auto it = m_blocked_ips.find(addr); if(it == m_blocked_ips.end()) return true; - if(time(nullptr) - it->second > P2P_IP_BLOCKTIME ) + if(time(nullptr) >= it->second) { m_blocked_ips.erase(it); LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(addr) << "is unblocked.", LOG_LEVEL_0); @@ -186,15 +186,27 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> - bool node_server<t_payload_net_handler>::block_ip(uint32_t addr) + bool node_server<t_payload_net_handler>::block_ip(uint32_t addr, time_t seconds) { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); - m_blocked_ips[addr] = time(nullptr); + m_blocked_ips[addr] = time(nullptr) + seconds; LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(addr) << " blocked.", LOG_LEVEL_0); return true; } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::unblock_ip(uint32_t addr) + { + CRITICAL_REGION_LOCAL(m_blocked_ips_lock); + auto i = m_blocked_ips.find(addr); + if (i == m_blocked_ips.end()) + return false; + m_blocked_ips.erase(i); + LOG_PRINT_CYAN("IP " << epee::string_tools::get_ip_string_from_int32(addr) << " unblocked.", LOG_LEVEL_0); + return true; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::add_ip_fail(uint32_t address) { CRITICAL_REGION_LOCAL(m_ip_fails_score_lock); diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h index 93b29deb2..2505006ad 100644 --- a/src/p2p/net_node_common.h +++ b/src/p2p/net_node_common.h @@ -50,7 +50,9 @@ namespace nodetool 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 bool block_ip(uint32_t adress)=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()const=0; virtual bool add_ip_fail(uint32_t adress)=0; }; @@ -86,10 +88,18 @@ namespace nodetool { return false; } - virtual bool block_ip(uint32_t adress) + virtual bool block_ip(uint32_t adress, time_t seconds) { return true; } + virtual bool unblock_ip(uint32_t adress) + { + return true; + } + virtual std::map<uint32_t, time_t> get_blocked_ips() const + { + return std::map<uint32_t, time_t>(); + } virtual bool add_ip_fail(uint32_t adress) { return true; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index f5e700033..4ba3acc37 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -51,6 +51,7 @@ namespace cryptonote command_line::add_arg(desc, arg_rpc_bind_ip); command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_testnet_rpc_bind_port); + command_line::add_arg(desc, arg_restricted_rpc); } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server( @@ -69,6 +70,7 @@ namespace cryptonote m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); m_port = command_line::get_arg(vm, p2p_bind_arg); + m_restricted = command_line::get_arg(vm, arg_restricted_rpc); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -119,6 +121,7 @@ namespace cryptonote res.height = m_core.get_current_blockchain_height(); res.target_height = m_core.get_target_blockchain_height(); res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); + res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET; res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); @@ -899,6 +902,49 @@ namespace cryptonote #endif } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + + std::map<uint32_t, time_t> blocked_ips = m_p2p.get_blocked_ips(); + for (std::map<uint32_t, time_t>::const_iterator i = blocked_ips.begin(); i != blocked_ips.end(); ++i) + { + COMMAND_RPC_GETBANS::ban b; + b.ip = i->first; + b.seconds = i->second; + res.bans.push_back(b); + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp) + { + if(!check_core_busy()) + { + error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; + error_resp.message = "Core is busy."; + return false; + } + + for (auto i = req.bans.begin(); i != req.bans.end(); ++i) + { + if (i->ban) + m_p2p.block_ip(i->ip, i->seconds); + else + m_p2p.unblock_ip(i->ip); + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_fast_exit(const COMMAND_RPC_FAST_EXIT::request& req, COMMAND_RPC_FAST_EXIT::response& res) { cryptonote::core::set_fast_exit(); @@ -957,4 +1003,10 @@ namespace cryptonote , std::to_string(config::testnet::RPC_DEFAULT_PORT) }; + const command_line::arg_descriptor<bool> core_rpc_server::arg_restricted_rpc = { + "restricted-rpc" + , "Restrict RPC to view only commands" + , false + }; + } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 1fbd0981f..8fe17ff5d 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -55,6 +55,7 @@ namespace cryptonote static const command_line::arg_descriptor<std::string> arg_rpc_bind_ip; static const command_line::arg_descriptor<std::string> arg_rpc_bind_port; static const command_line::arg_descriptor<std::string> arg_testnet_rpc_bind_port; + static const command_line::arg_descriptor<bool> arg_restricted_rpc; typedef epee::net_utils::connection_context_base connection_context; @@ -79,20 +80,20 @@ namespace cryptonote MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) MAP_URI_AUTO_JON2("/is_key_image_spent", on_is_key_image_spent, COMMAND_RPC_IS_KEY_IMAGE_SPENT) MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) - MAP_URI_AUTO_JON2("/start_mining", on_start_mining, COMMAND_RPC_START_MINING) - MAP_URI_AUTO_JON2("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING) - MAP_URI_AUTO_JON2("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS) - MAP_URI_AUTO_JON2("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC) - MAP_URI_AUTO_JON2("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST) - MAP_URI_AUTO_JON2("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE) - MAP_URI_AUTO_JON2("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL) + MAP_URI_AUTO_JON2_IF("/start_mining", on_start_mining, COMMAND_RPC_START_MINING, !m_restricted) + MAP_URI_AUTO_JON2_IF("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING, !m_restricted) + MAP_URI_AUTO_JON2_IF("/mining_status", on_mining_status, COMMAND_RPC_MINING_STATUS, !m_restricted) + MAP_URI_AUTO_JON2_IF("/save_bc", on_save_bc, COMMAND_RPC_SAVE_BC, !m_restricted) + MAP_URI_AUTO_JON2_IF("/get_peer_list", on_get_peer_list, COMMAND_RPC_GET_PEER_LIST, !m_restricted) + MAP_URI_AUTO_JON2_IF("/set_log_hash_rate", on_set_log_hash_rate, COMMAND_RPC_SET_LOG_HASH_RATE, !m_restricted) + MAP_URI_AUTO_JON2_IF("/set_log_level", on_set_log_level, COMMAND_RPC_SET_LOG_LEVEL, !m_restricted) MAP_URI_AUTO_JON2("/get_transaction_pool", on_get_transaction_pool, COMMAND_RPC_GET_TRANSACTION_POOL) - MAP_URI_AUTO_JON2("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON) + MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted) MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) - MAP_URI_AUTO_JON2("/fast_exit", on_fast_exit, COMMAND_RPC_FAST_EXIT) - MAP_URI_AUTO_JON2("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS) - MAP_URI_AUTO_JON2("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH) - MAP_URI_AUTO_JON2("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH) + MAP_URI_AUTO_JON2_IF("/fast_exit", on_fast_exit, COMMAND_RPC_FAST_EXIT, !m_restricted) + MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) + MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) + MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) @@ -105,6 +106,8 @@ namespace cryptonote MAP_JON_RPC_WE("get_connections", on_get_connections, COMMAND_RPC_GET_CONNECTIONS) MAP_JON_RPC_WE("get_info", on_get_info_json, COMMAND_RPC_GET_INFO) MAP_JON_RPC_WE("hard_fork_info", on_hard_fork_info, COMMAND_RPC_HARD_FORK_INFO) + MAP_JON_RPC_WE("setbans", on_set_bans, COMMAND_RPC_SETBANS) + MAP_JON_RPC_WE("getbans", on_get_bans, COMMAND_RPC_GETBANS) END_JSON_RPC_MAP() END_URI_MAP2() @@ -142,6 +145,8 @@ namespace cryptonote bool on_get_connections(const COMMAND_RPC_GET_CONNECTIONS::request& req, COMMAND_RPC_GET_CONNECTIONS::response& res, epee::json_rpc::error& error_resp); bool on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp); bool on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp); + bool on_set_bans(const COMMAND_RPC_SETBANS::request& req, COMMAND_RPC_SETBANS::response& res, epee::json_rpc::error& error_resp); + bool on_get_bans(const COMMAND_RPC_GETBANS::request& req, COMMAND_RPC_GETBANS::response& res, epee::json_rpc::error& error_resp); //----------------------- private: @@ -161,5 +166,6 @@ private: std::string m_port; std::string m_bind_ip; bool m_testnet; + bool m_restricted; }; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index aa88ffcb4..b70164614 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -273,6 +273,7 @@ namespace cryptonote uint64_t height; uint64_t target_height; uint64_t difficulty; + uint64_t target; uint64_t tx_count; uint64_t tx_pool_size; uint64_t alt_blocks_count; @@ -888,5 +889,70 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_GETBANS + { + struct ban + { + uint32_t ip; + uint32_t seconds; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(ip) + KV_SERIALIZE(seconds) + END_KV_SERIALIZE_MAP() + }; + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + std::vector<ban> bans; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(bans) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SETBANS + { + struct ban + { + uint32_t ip; + bool ban; + uint32_t seconds; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(ip) + KV_SERIALIZE(ban) + KV_SERIALIZE(seconds) + END_KV_SERIALIZE_MAP() + }; + + struct request + { + std::vector<ban> bans; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(bans) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index b1755ed62..f44eb33d0 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -421,6 +421,42 @@ bool simple_wallet::set_default_mixin(const std::vector<std::string> &args/* = s } } +bool simple_wallet::set_auto_refresh(const std::vector<std::string> &args/* = std::vector<std::string>()*/) +{ + bool success = false; + tools::password_container pwd_container; + success = pwd_container.read_password(); + if (!success) + { + fail_msg_writer() << tr("failed to read wallet password"); + return true; + } + + /* verify password before using so user doesn't accidentally set a new password for rewritten wallet */ + success = m_wallet->verify_password(pwd_container.password()); + if (!success) + { + fail_msg_writer() << tr("invalid password"); + return true; + } + + bool auto_refresh = is_it_true(args[1]); + m_wallet->auto_refresh(auto_refresh); + if (auto_refresh && !m_auto_refresh_run.load(std::memory_order_relaxed)) + { + m_auto_refresh_run.store(true, std::memory_order_relaxed); + m_auto_refresh_thread = std::thread([&]{wallet_refresh_thread();}); + } + else if (!auto_refresh && m_auto_refresh_run.load(std::memory_order_relaxed)) + { + m_auto_refresh_run.store(false, std::memory_order_relaxed); + m_auto_refresh_thread.join(); + } + + m_wallet->rewrite(m_wallet_file, pwd_container.password()); + return true; +} + bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/) { success_msg_writer() << get_commands_str(); @@ -430,6 +466,8 @@ bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<st simple_wallet::simple_wallet() : m_daemon_port(0) , m_refresh_progress_reporter(*this) + , m_auto_refresh_run(false) + , m_auto_refresh_refreshing(false) { m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), tr("start_mining [<number_of_threads>] - Start mining in daemon")); m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), tr("Stop mining in daemon")); @@ -450,7 +488,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("viewkey", boost::bind(&simple_wallet::viewkey, this, _1), tr("Get viewkey")); m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Get spendkey")); m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Get deterministic seed")); - m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("available options: seed language - Set wallet seed langage; always-confirm-transfers <1|0> - whether to confirm unsplit txes; store-tx-info <1|0> - whether to store per outgoing tx info (destination address, payment id, tx secret key) for future reference; default_mixin <n> - set default mixin (default default is 4")); + m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("available options: seed language - Set wallet seed langage; always-confirm-transfers <1|0> - whether to confirm unsplit txes; store-tx-info <1|0> - whether to store per outgoing tx info (destination address, payment id, tx secret key) for future reference; default_mixin <n> - set default mixin (default default is 4; auto-refresh <1|0> - whether to automatically refresh new blocks from the daemon")); m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs")); m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given tx")); m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to a given address in a partcular tx")); @@ -462,7 +500,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) { if (args.empty()) { - fail_msg_writer() << tr("set: needs an argument. available options: seed, always-confirm-transfers, default-mixin"); + fail_msg_writer() << tr("set: needs an argument. available options: seed, always-confirm-transfers, default-mixin, auto-refresh"); return true; } else @@ -527,6 +565,21 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) return true; } } + else if (args[0] == "auto-refresh") + { + if (args.size() <= 1) + { + fail_msg_writer() << tr("set auto-refresh: needs an argument (0 or 1)"); + return true; + } + else + { + std::vector<std::string> local_args = args; + local_args.erase(local_args.begin(), local_args.begin()+2); + set_auto_refresh(local_args); + return true; + } + } } fail_msg_writer() << tr("set: unrecognized argument(s)"); return true; @@ -1028,6 +1081,12 @@ bool simple_wallet::open_wallet(const string &wallet_file, const std::string& pa //---------------------------------------------------------------------------------------------------- bool simple_wallet::close_wallet() { + if (m_auto_refresh_run.load(std::memory_order_relaxed)) + { + m_auto_refresh_run.store(false, std::memory_order_relaxed); + m_auto_refresh_thread.join(); + } + bool r = m_wallet->deinit(); if (!r) { @@ -1175,38 +1234,50 @@ bool simple_wallet::save_bc(const std::vector<std::string>& args) //---------------------------------------------------------------------------------------------------- void simple_wallet::on_new_block(uint64_t height, const cryptonote::block& block) { - m_refresh_progress_reporter.update(height, false); + if (!m_auto_refresh_refreshing) + m_refresh_progress_reporter.update(height, false); } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_money_received(uint64_t height, const cryptonote::transaction& tx, size_t out_index) { - message_writer(epee::log_space::console_color_green, false) << + message_writer(epee::log_space::console_color_green, false) << "\r" << tr("Height ") << height << ", " << tr("transaction ") << get_transaction_hash(tx) << ", " << tr("received ") << print_money(tx.vout[out_index].amount); - m_refresh_progress_reporter.update(height, true); + if (m_auto_refresh_refreshing) + m_cmd_binder.print_prompt(); + else + m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_money_spent(uint64_t height, const cryptonote::transaction& in_tx, size_t out_index, const cryptonote::transaction& spend_tx) { - message_writer(epee::log_space::console_color_magenta, false) << + message_writer(epee::log_space::console_color_magenta, false) << "\r" << tr("Height ") << height << ", " << tr("transaction ") << get_transaction_hash(spend_tx) << ", " << tr("spent ") << print_money(in_tx.vout[out_index].amount); - m_refresh_progress_reporter.update(height, true); + if (m_auto_refresh_refreshing) + m_cmd_binder.print_prompt(); + else + m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_skip_transaction(uint64_t height, const cryptonote::transaction& tx) { - message_writer(epee::log_space::console_color_red, true) << + message_writer(epee::log_space::console_color_red, true) << "\r" << tr("Height ") << height << ", " << tr("transaction ") << get_transaction_hash(tx) << ", " << tr("unsupported transaction format"); - m_refresh_progress_reporter.update(height, true); + if (m_auto_refresh_refreshing) + m_cmd_binder.print_prompt(); + else + m_refresh_progress_reporter.update(height, true); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::refresh(const std::vector<std::string>& args) { + std::unique_lock<std::mutex> lock(m_auto_refresh_mutex); + if (!try_connect_to_daemon()) return true; @@ -2146,9 +2217,33 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) return true; } //---------------------------------------------------------------------------------------------------- +void simple_wallet::wallet_refresh_thread() +{ + while (true) + { + std::unique_lock<std::mutex> lock(m_auto_refresh_mutex); + m_auto_refresh_refreshing = true; + try + { + uint64_t fetched_blocks; + m_wallet->refresh(0, fetched_blocks); + } + catch(...) {} + m_auto_refresh_refreshing = false; + m_auto_refresh_cond.wait_for(lock, chrono::seconds(90)); + if (!m_auto_refresh_run.load(std::memory_order_relaxed)) + break; + } +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::run() { std::string addr_start = m_wallet->get_account().get_public_address_str(m_wallet->testnet()).substr(0, 6); + m_auto_refresh_run = m_wallet->auto_refresh(); + if (m_auto_refresh_run) + { + m_auto_refresh_thread = std::thread([&]{wallet_refresh_thread();}); + } return m_cmd_binder.run_handling(std::string("[") + tr("wallet") + " " + addr_start + "]: ", ""); } //---------------------------------------------------------------------------------------------------- diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 2ad54d4ca..be510ffb2 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -77,6 +77,8 @@ namespace cryptonote bool run_console_handler(); + void wallet_refresh_thread(); + bool new_wallet(const std::string &wallet_file, const std::string& password, const crypto::secret_key& recovery_key, bool recover, bool two_random, bool testnet, const std::string &old_language); bool new_wallet(const std::string &wallet_file, const std::string& password, const cryptonote::account_public_address& address, @@ -101,6 +103,7 @@ namespace cryptonote bool set_always_confirm_transfers(const std::vector<std::string> &args = std::vector<std::string>()); bool set_store_tx_info(const std::vector<std::string> &args = std::vector<std::string>()); bool set_default_mixin(const std::vector<std::string> &args = std::vector<std::string>()); + bool set_auto_refresh(const std::vector<std::string> &args = std::vector<std::string>()); bool help(const std::vector<std::string> &args = std::vector<std::string>()); bool start_mining(const std::vector<std::string> &args); bool stop_mining(const std::vector<std::string> &args); @@ -229,5 +232,11 @@ namespace cryptonote std::unique_ptr<tools::wallet2> m_wallet; epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; + + std::atomic<bool> m_auto_refresh_run; + bool m_auto_refresh_refreshing; + std::thread m_auto_refresh_thread; + std::mutex m_auto_refresh_mutex; + std::condition_variable m_auto_refresh_cond; }; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a6ac860c7..c4df78ba9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -203,31 +203,64 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ tx_pub_key = pub_key_field.pub_key; bool r = true; - int threads; + int threads = std::thread::hardware_concurrency(); if (miner_tx && m_refresh_type == RefreshNoCoinbase) { // assume coinbase isn't for us } else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) { - for (size_t i = 0; i < tx.vout.size(); ++i) + uint64_t money_transfered = 0; + bool error = false; + check_acc_out(m_account.get_keys(), tx.vout[0], tx_pub_key, 0, money_transfered, error); + if (error) { - uint64_t money_transfered = 0; - bool error = false; - check_acc_out(m_account.get_keys(), tx.vout[i], tx_pub_key, i, money_transfered, error); - if (error) + r = false; + } + else + { + // this assumes that the miner tx pays a single address + if (money_transfered > 0) { - r = false; - break; + outs.push_back(0); + tx_money_got_in_outs = money_transfered; + + // process the other outs from that tx + boost::asio::io_service ioservice; + boost::thread_group threadpool; + std::auto_ptr < boost::asio::io_service::work > work(new boost::asio::io_service::work(ioservice)); + for (int i = 0; i < threads; i++) + { + threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioservice)); + } + + const account_keys &keys = m_account.get_keys(); + std::vector<uint64_t> money_transfered(tx.vout.size()); + std::deque<bool> error(tx.vout.size()); + // the first one was already checked + for (size_t i = 1; i < tx.vout.size(); ++i) + { + ioservice.dispatch(boost::bind(&wallet2::check_acc_out, this, std::cref(keys), std::cref(tx.vout[i]), std::cref(tx_pub_key), i, + std::ref(money_transfered[i]), std::ref(error[i]))); + } + KILL_IOSERVICE(); + for (size_t i = 1; i < tx.vout.size(); ++i) + { + if (error[i]) + { + r = false; + break; + } + if (money_transfered[i]) + { + outs.push_back(i); + tx_money_got_in_outs += money_transfered[i]; + } + } } - // this assumes that the miner tx pays a single address - if (money_transfered == 0) - break; - outs.push_back(i); - tx_money_got_in_outs += money_transfered; } } - else if (tx.vout.size() > 1 && (threads = std::thread::hardware_concurrency()) > 1) + else if (tx.vout.size() > 1 && threads > 1) { boost::asio::io_service ioservice; boost::thread_group threadpool; @@ -274,7 +307,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); req.txid = get_transaction_hash(tx); + m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); + m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status); @@ -478,19 +513,28 @@ void wallet2::parse_block_round(const cryptonote::blobdata &blob, cryptonote::bl bl_id = get_block_hash(bl); } //---------------------------------------------------------------------------------------------------- -void wallet2::pull_blocks(uint64_t start_height, uint64_t& blocks_added) +void wallet2::pull_blocks(uint64_t start_height, uint64_t &blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks) { - blocks_added = 0; cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); - get_short_chain_history(req.block_ids); + req.block_ids = short_chain_history; + req.start_height = start_height; + m_daemon_rpc_mutex.lock(); bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); + m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); - size_t current_index = res.start_height; + blocks_start_height = res.start_height; + blocks = res.blocks; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added) +{ + size_t current_index = start_height; + blocks_added = 0; int threads = std::thread::hardware_concurrency(); if (threads > 1) @@ -498,7 +542,6 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t& blocks_added) std::vector<crypto::hash> round_block_hashes(threads); std::vector<cryptonote::block> round_blocks(threads); std::deque<bool> error(threads); - const std::list<block_complete_entry> &blocks = res.blocks; size_t blocks_size = blocks.size(); std::list<block_complete_entry>::const_iterator blocki = blocks.begin(); for (size_t b = 0; b < blocks_size; b += threads) @@ -559,10 +602,10 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t& blocks_added) } else { - BOOST_FOREACH(auto& bl_entry, res.blocks) + BOOST_FOREACH(auto& bl_entry, blocks) { cryptonote::block bl; - r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); + bool r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block); crypto::hash bl_id = get_block_hash(bl); @@ -574,9 +617,9 @@ void wallet2::pull_blocks(uint64_t start_height, uint64_t& blocks_added) else if(bl_id != m_blockchain[current_index]) { //split detected here !!! - THROW_WALLET_EXCEPTION_IF(current_index == res.start_height, error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error, "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) + - " (height " + std::to_string(res.start_height) + "), local block id at this height: " + + " (height " + std::to_string(start_height) + "), local block id at this height: " + string_tools::pod_to_hex(m_blockchain[current_index])); detach_blockchain(current_index); @@ -604,6 +647,23 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched) refresh(start_height, blocks_fetched, received_money); } //---------------------------------------------------------------------------------------------------- +void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks) +{ + // prepend the last 3 blocks, should be enough to guard against a block or two's reorg + cryptonote::block bl; + std::list<cryptonote::block_complete_entry>::const_reverse_iterator i = prev_blocks.rbegin(); + for (size_t n = 0; n < std::min((size_t)3, prev_blocks.size()); ++n) + { + bool ok = cryptonote::parse_and_validate_block_from_blob(i->block, bl); + THROW_WALLET_EXCEPTION_IF(!ok, error::block_parse_error, i->block); + short_chain_history.push_front(cryptonote::get_block_hash(bl)); + ++i; + } + + // pull the new blocks + pull_blocks(start_height, blocks_start_height, short_chain_history, blocks); +} +//---------------------------------------------------------------------------------------------------- void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& received_money) { received_money = false; @@ -611,15 +671,33 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re uint64_t added_blocks = 0; size_t try_count = 0; crypto::hash last_tx_hash_id = m_transfers.size() ? get_transaction_hash(m_transfers.back().m_tx) : null_hash; + std::list<crypto::hash> short_chain_history; + std::thread pull_thread; + uint64_t blocks_start_height; + std::list<cryptonote::block_complete_entry> blocks; + + // pull the first set of blocks + get_short_chain_history(short_chain_history); + pull_blocks(start_height, blocks_start_height, short_chain_history, blocks); while(m_run.load(std::memory_order_relaxed)) { try { - pull_blocks(start_height, added_blocks); + // pull the next set of blocks while we're processing the current one + uint64_t next_blocks_start_height; + std::list<cryptonote::block_complete_entry> next_blocks; + pull_thread = std::thread([&]{pull_next_blocks(start_height, next_blocks_start_height, short_chain_history, blocks, next_blocks);}); + + process_blocks(blocks_start_height, blocks, added_blocks); blocks_fetched += added_blocks; + pull_thread.join(); if(!added_blocks) break; + + // switch to the new blocks from the daemon + blocks_start_height = next_blocks_start_height; + blocks = next_blocks; } catch (const std::exception&) { @@ -685,6 +763,14 @@ void wallet2::detach_blockchain(uint64_t height) ++it; } + for (auto it = m_confirmed_txs.begin(); it != m_confirmed_txs.end(); ) + { + if(height <= it->second.m_block_height) + it = m_confirmed_txs.erase(it); + else + ++it; + } + LOG_PRINT_L0("Detached blockchain on height " << height << ", transfers detached " << transfers_detached << ", blocks detached " << blocks_detached); } //---------------------------------------------------------------------------------------------------- @@ -744,6 +830,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const std::string& p value2.SetUint(m_default_mixin); json.AddMember("default_mixin", value2, json.GetAllocator()); + value2.SetInt(m_auto_refresh ? 1 :0); + json.AddMember("auto_refresh", value2, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -828,6 +917,7 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa m_store_tx_info = (json.HasMember("store_tx_keys") && (json["store_tx_keys"].GetInt() != 0)) || (json.HasMember("store_tx_info") && (json["store_tx_info"].GetInt() != 0)); m_default_mixin = json.HasMember("default_mixin") ? json["default_mixin"].GetUint() : 0; + m_auto_refresh = json.HasMember("auto_refresh") && (json["auto_refresh"].GetInt() != 0); } const cryptonote::account_keys& keys = m_account.get_keys(); @@ -1050,6 +1140,8 @@ bool wallet2::prepare_file_names(const std::string& file_path) //---------------------------------------------------------------------------------------------------- bool wallet2::check_connection() { + std::lock_guard<std::mutex> lock(m_daemon_rpc_mutex); + if(m_http_client.is_connected()) return true; @@ -1262,7 +1354,9 @@ void wallet2::rescan_spent() COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); req.key_images = key_images; + m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/is_key_image_spent", req, daemon_resp, m_http_client, 200000); + m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); @@ -1569,7 +1663,9 @@ void wallet2::commit_tx(pending_tx& ptx) COMMAND_RPC_SEND_RAW_TX::request req; req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; + m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); + m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status); @@ -1757,7 +1853,9 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent req.amounts.push_back(it->amount()); } + m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); + m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index e036020b8..d6aea182d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -87,10 +87,10 @@ namespace tools }; private: - wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase) {} + wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true) {} public: - wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase) {} + wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true) {} struct transfer_details { uint64_t m_block_height; @@ -330,6 +330,8 @@ namespace tools void store_tx_info(bool store) { m_store_tx_info = store; } uint32_t default_mixin() const { return m_default_mixin; } void default_mixin(uint32_t m) { m_default_mixin = m; } + bool auto_refresh() const { return m_auto_refresh; } + void auto_refresh(bool r) { m_auto_refresh = r; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const; @@ -355,7 +357,9 @@ namespace tools bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; bool is_transfer_unlocked(const transfer_details& td) const; bool clear(); - void pull_blocks(uint64_t start_height, uint64_t& blocks_added); + void pull_blocks(uint64_t start_height, uint64_t& blocks_start_height, const std::list<crypto::hash> &short_chain_history, std::list<cryptonote::block_complete_entry> &blocks); + void pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::list<cryptonote::block_complete_entry> &prev_blocks, std::list<cryptonote::block_complete_entry> &blocks); + void process_blocks(uint64_t start_height, const std::list<cryptonote::block_complete_entry> &blocks, uint64_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list<transfer_container::iterator>& selected_transfers); bool prepare_file_names(const std::string& file_path); void process_unconfirmed(const cryptonote::transaction& tx, uint64_t height); @@ -387,6 +391,8 @@ namespace tools std::atomic<bool> m_run; + std::mutex m_daemon_rpc_mutex; + i_wallet2_callback* m_callback; bool m_testnet; bool m_restricted; @@ -397,6 +403,7 @@ namespace tools bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */ uint32_t m_default_mixin; RefreshType m_refresh_type; + bool m_auto_refresh; }; } BOOST_CLASS_VERSION(tools::wallet2, 10) @@ -563,7 +570,9 @@ namespace tools req.amounts.push_back(it->amount()); } + m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); + m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); |