diff options
47 files changed, 613 insertions, 244 deletions
@@ -41,7 +41,11 @@ debug-test: debug-all: mkdir -p build/debug - cd build/debug && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) + cd build/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) + +debug-static-all: + mkdir -p build/debug + cd build/debug && cmake -D BUILD_TESTS=ON -D STATIC=ON -D CMAKE_BUILD_TYPE=Debug ../.. && $(MAKE) cmake-release: mkdir -p build/release diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h index 6eeb1441f..ec4bcbe2d 100644 --- a/contrib/epee/include/misc_log_ex.h +++ b/contrib/epee/include/misc_log_ex.h @@ -60,6 +60,7 @@ #define MCDEBUG(cat,x) CLOG(DEBUG,cat) << x #define MCTRACE(cat,x) CLOG(TRACE,cat) << x #define MCLOG(level,cat,x) ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::NormalLog, cat) << x +#define MCLOG_FILE(level,cat,x) ELPP_WRITE_LOG(el::base::Writer, level, el::base::DispatchAction::FileOnlyLog, cat) << x #define MCLOG_COLOR(level,cat,color,x) MCLOG(level,cat,"\033[1;" color "m" << x << "\033[0m") #define MCLOG_RED(level,cat,x) MCLOG_COLOR(level,cat,"31",x) diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl index d9eca2479..c92a13bcc 100644 --- a/contrib/epee/include/net/http_protocol_handler.inl +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -547,7 +547,7 @@ namespace net_utils LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data); m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); - if(response.m_body.size()) + if(response.m_body.size() && (query_info.m_http_method != http::http_method_head)) m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); return res; } diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl index 50a01aaa5..ab7c32c32 100644 --- a/contrib/epee/include/net/levin_client.inl +++ b/contrib/epee/include/net/levin_client.inl @@ -99,7 +99,7 @@ int levin_client_impl::invoke(int command, const std::string& in_buff, std::stri if(head.m_signature!=LEVIN_SIGNATURE) { - LOG_PRINT_L1("Signature missmatch in response"); + LOG_PRINT_L1("Signature mismatch in response"); return -1; } @@ -160,7 +160,7 @@ inline if(head.m_signature!=LEVIN_SIGNATURE) { - LOG_PRINT_L1("Signature missmatch in response"); + LOG_PRINT_L1("Signature mismatch in response"); return -1; } diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h index 4b48070d6..337d345c4 100644 --- a/contrib/epee/include/net/levin_client_async.h +++ b/contrib/epee/include/net/levin_client_async.h @@ -408,7 +408,7 @@ namespace levin if(head.m_signature!=LEVIN_SIGNATURE) { - LOG_ERROR("Signature missmatch in response"); + LOG_ERROR("Signature mismatch in response"); return false; } diff --git a/contrib/epee/include/net/levin_helper.h b/contrib/epee/include/net/levin_helper.h index c51d7244b..05560dd90 100644 --- a/contrib/epee/include/net/levin_helper.h +++ b/contrib/epee/include/net/levin_helper.h @@ -94,7 +94,7 @@ namespace levin } if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) { - LOG_PRINT_L3("sizes missmatch, at load_struct_from_levin_message"); + LOG_PRINT_L3("sizes mismatch, at load_struct_from_levin_message"); return false; } @@ -121,7 +121,7 @@ namespace levin } if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) { - LOG_ERROR("sizes missmatch, at load_struct_from_levin_message"); + LOG_ERROR("sizes mismatch, at load_struct_from_levin_message"); return false; } diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h index 3e1b8493a..fbc9727e2 100644 --- a/contrib/epee/include/net/levin_protocol_handler.h +++ b/contrib/epee/include/net/levin_protocol_handler.h @@ -103,7 +103,7 @@ namespace levin { if(m_cach_in_buffer.size() >= sizeof(uint64_t) && *((uint64_t*)m_cach_in_buffer.data()) != LEVIN_SIGNATURE) { - LOG_ERROR_CC(m_conn_context, "Signature missmatch on accepted connection"); + LOG_ERROR_CC(m_conn_context, "Signature mismatch on accepted connection"); return false; } is_continue = false; @@ -113,7 +113,7 @@ namespace levin bucket_head* phead = (bucket_head*)m_cach_in_buffer.data(); if(LEVIN_SIGNATURE != phead->m_signature) { - LOG_ERROR_CC(m_conn_context, "Signature missmatch on accepted connection"); + LOG_ERROR_CC(m_conn_context, "Signature mismatch on accepted connection"); return false; } m_current_head = *phead; diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h index 432169990..1d808cc4c 100644 --- a/contrib/epee/include/net/net_helper.h +++ b/contrib/epee/include/net/net_helper.h @@ -473,7 +473,7 @@ namespace net_utils if(bytes_transfered != buff.size()) { - LOG_ERROR("Transferred missmatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size()); + LOG_ERROR("Transferred mismatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size()); return false; } diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h index 3bea11985..ef3a1d146 100644 --- a/contrib/epee/include/net/net_utils_base.h +++ b/contrib/epee/include/net/net_utils_base.h @@ -155,7 +155,6 @@ namespace net_utils const network_address m_remote_address; const bool m_is_income; const time_t m_started; - bool m_in_timedsync; time_t m_last_recv; time_t m_last_send; uint64_t m_recv_cnt; @@ -171,7 +170,6 @@ namespace net_utils m_remote_address(remote_address), m_is_income(is_income), m_started(time(NULL)), - m_in_timedsync(false), m_last_recv(last_recv), m_last_send(last_send), m_recv_cnt(recv_cnt), @@ -184,7 +182,6 @@ namespace net_utils m_remote_address(new ipv4_network_address(0,0)), m_is_income(false), m_started(time(NULL)), - m_in_timedsync(false), m_last_recv(0), m_last_send(0), m_recv_cnt(0), diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h index bf2c8dacd..f4442e46c 100644 --- a/contrib/epee/include/serialization/keyvalue_serialization.h +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -70,6 +70,12 @@ public: \ #define KV_SERIALIZE_N(varialble, val_name) \ epee::serialization::selector<is_store>::serialize(this_ref.varialble, stg, hparent_section, val_name); +#define KV_SERIALIZE_OPT_N(variable, val_name, default_value) \ + do { \ + if (!epee::serialization::selector<is_store>::serialize(this_ref.variable, stg, hparent_section, val_name)) \ + this_ref.variable = default_value; \ + } while (0); + #define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ epee::serialization::selector<is_store>::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); @@ -86,6 +92,7 @@ public: \ #define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) #define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check #define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) +#define KV_SERIALIZE_OPT(variable,default_value) KV_SERIALIZE_OPT_N(variable, #variable, default_value) } diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h index ba9292306..2023e2f2a 100644 --- a/contrib/epee/include/storages/portable_storage.h +++ b/contrib/epee/include/storages/portable_storage.h @@ -158,7 +158,7 @@ namespace epee pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB ) { - LOG_ERROR("portable_storage: wrong binary format - signature missmatch"); + LOG_ERROR("portable_storage: wrong binary format - signature mismatch"); return false; } if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index a51654d36..39661a1a0 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -91,7 +91,7 @@ static const char *get_default_categories(int level) switch (level) { case 0: - categories = "*:WARNING,net:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO,msgwriter:INFO"; + categories = "*:WARNING,net:FATAL,net.p2p:FATAL,net.cn:FATAL,global:INFO,verify:FATAL,stacktrace:INFO,logging:INFO"; break; case 1: categories = "*:WARNING,global:INFO,stacktrace:INFO,logging:INFO,msgwriter:INFO"; diff --git a/contrib/epee/src/readline_buffer.cpp b/contrib/epee/src/readline_buffer.cpp index 549f49d45..ce8260ef6 100644 --- a/contrib/epee/src/readline_buffer.cpp +++ b/contrib/epee/src/readline_buffer.cpp @@ -152,13 +152,11 @@ static int process_input() static void handle_line(char* line) { - if(last_line == "exit" || last_line == "q") - { - return; - } - std::lock_guard<std::mutex> lock(sync_mutex); - rl_set_prompt(last_prompt.c_str()); - rl_already_prompted = 1; + // This function never gets called now as we are trapping newlines. + // However, it still needs to be present for readline to know we are + // manually handling lines. + rl_done = 1; + return; } static int handle_enter(int x, int y) @@ -167,30 +165,42 @@ static int handle_enter(int x, int y) char* line = NULL; line = rl_copy_text(0, rl_end); + std::string test_line = line; + boost::trim_right(test_line); + rl_crlf(); rl_on_new_line(); + + if(test_line.empty()) + { + last_line = ""; + rl_set_prompt(last_prompt.c_str()); + rl_replace_line("", 1); + rl_redisplay(); + have_line.notify_one(); + return 0; + } + rl_set_prompt(""); rl_replace_line("", 1); rl_redisplay(); - std::string test_line = line; - boost::trim_right(test_line); - if (test_line.length() > 0) + if (!test_line.empty()) { last_line = test_line; add_history(test_line.c_str()); - have_line.notify_one(); + history_set_pos(history_length); } free(line); if(last_line != "exit" && last_line != "q") { rl_set_prompt(last_prompt.c_str()); - rl_on_new_line_with_prompt(); + rl_replace_line("", 1); rl_redisplay(); } - rl_done = 1; + have_line.notify_one(); return 0; } @@ -236,6 +246,7 @@ static void install_line_handler() rl_startup_hook = startup_hook; rl_attempted_completion_function = attempted_completion; rl_callback_handler_install("", handle_line); + stifle_history(500); } static void remove_line_handler() diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index ab38cbbae..e7ff11c5c 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -26,12 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "common/command_line.h" -#include "common/i18n.h" #include "common/dns_utils.h" +#include "common/i18n.h" #include "cryptonote_basic/cryptonote_basic_impl.h" -#include <cstring> -#include <sstream> // check local first (in the event of static or in-source compilation of libunbound) #include "unbound.h" @@ -307,12 +304,8 @@ DNSResolver& DNSResolver::instance() { boost::lock_guard<boost::mutex> lock(instance_lock); - static DNSResolver* staticInstance = NULL; - if (staticInstance == NULL) - { - staticInstance = new DNSResolver(); - } - return *staticInstance; + static DNSResolver staticInstance; + return staticInstance; } DNSResolver DNSResolver::create() @@ -405,7 +398,7 @@ std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec return addresses; } -std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm) +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm) { // attempt to get address from dns query auto addresses = addresses_from_url(url, dnssec_valid); @@ -414,44 +407,7 @@ std::string get_account_address_as_str_from_url(const std::string& url, bool& dn LOG_ERROR("wrong address: " << url); return {}; } - // for now, move on only if one address found - if (addresses.size() > 1) - { - LOG_ERROR("not yet supported: Multiple Monero addresses found for given URL: " << url); - return {}; - } - if (!cli_confirm) - return addresses[0]; - // prompt user for confirmation. - // inform user of DNSSEC validation status as well. - std::string dnssec_str; - if (dnssec_valid) - { - dnssec_str = tr("DNSSEC validation passed"); - } - else - { - dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!"); - } - std::stringstream prompt; - prompt << tr("For URL: ") << url - << ", " << dnssec_str << std::endl - << tr(" Monero Address = ") << addresses[0] - << std::endl - << tr("Is this OK? (Y/n) ") - ; - // prompt the user for confirmation given the dns query and dnssec status - std::string confirm_dns_ok = command_line::input_line(prompt.str()); - if (std::cin.eof()) - { - return {}; - } - if (!command_line::is_yes(confirm_dns_ok)) - { - std::cout << tr("you have cancelled the transfer request") << std::endl; - return {}; - } - return addresses[0]; + return dns_confirm(url, addresses, dnssec_valid); } namespace diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index 53c0c1c7b..69b32f435 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -101,7 +101,7 @@ public: * * @return A vector of strings containing a TXT record; or an empty vector */ - // TODO: modify this to accomodate DNSSEC + // TODO: modify this to accommodate DNSSEC std::vector<std::string> get_txt_record(const std::string& url, bool& dnssec_available, bool& dnssec_valid); /** @@ -142,7 +142,7 @@ private: * * @return A vector of strings containing the requested record; or an empty vector */ - // TODO: modify this to accomodate DNSSEC + // TODO: modify this to accommodate DNSSEC std::vector<std::string> get_record(const std::string& url, int record_type, std::string (*reader)(const char *,size_t), bool& dnssec_available, bool& dnssec_valid); /** @@ -163,7 +163,7 @@ namespace dns_utils std::string address_from_txt_record(const std::string& s); std::vector<std::string> addresses_from_url(const std::string& url, bool& dnssec_valid); -std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, bool cli_confirm = true); +std::string get_account_address_as_str_from_url(const std::string& url, bool& dnssec_valid, std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> confirm_dns); bool load_txt_records_from_dns(std::vector<std::string> &records, const std::vector<std::string> &dns_urls); diff --git a/src/common/scoped_message_writer.h b/src/common/scoped_message_writer.h index 7ee4f1379..e31f8f0b2 100644 --- a/src/common/scoped_message_writer.h +++ b/src/common/scoped_message_writer.h @@ -91,7 +91,7 @@ public: { m_flush = false; - MCLOG(m_log_level, "msgwriter", m_oss.str()); + MCLOG_FILE(m_log_level, "msgwriter", m_oss.str()); if (epee::console_color_default == m_color) { diff --git a/src/common/util.h b/src/common/util.h index 4291d7e18..2452bc9d5 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -40,7 +40,6 @@ #include <string> #include "crypto/hash.h" -#include "p2p/p2p_protocol_defs.h" /*! \brief Various Tools * @@ -108,14 +107,6 @@ namespace tools bool sanitize_locale(); - inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) - { - std::string s; - s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id)); - s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time)); - return crypto::cn_fast_hash(s.data(), s.size()); - } - /*! \brief Defines a signal handler for win32 and *nix */ class signal_handler diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index edd67793f..a59f96956 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -308,13 +308,13 @@ namespace cryptonote { , crypto::hash8& payment_id , bool testnet , const std::string& str_or_url - , bool cli_confirm + , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm ) { if (get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, str_or_url)) return true; bool dnssec_valid; - std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, cli_confirm); + std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(str_or_url, dnssec_valid, dns_confirm); return !address_str.empty() && get_account_integrated_address_from_str(address, has_payment_id, payment_id, testnet, address_str); } @@ -323,12 +323,12 @@ namespace cryptonote { cryptonote::account_public_address& address , bool testnet , const std::string& str_or_url - , bool cli_confirm + , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm ) { bool has_payment_id; crypto::hash8 payment_id; - return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, cli_confirm); + return get_account_address_from_str_or_url(address, has_payment_id, payment_id, testnet, str_or_url, dns_confirm); } //-------------------------------------------------------------------------------- bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 14c03ac4c..9838fcb47 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -67,6 +67,15 @@ namespace cryptonote { }; #pragma pack (pop) + namespace + { + std::string return_first_address(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid) + { + if (addresses.empty()) + return {}; + return addresses[0]; + } + } /************************************************************************/ /* Cryptonote helper functions */ @@ -109,14 +118,14 @@ namespace cryptonote { , crypto::hash8& payment_id , bool testnet , const std::string& str_or_url - , bool cli_confirm = true + , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address ); bool get_account_address_from_str_or_url( cryptonote::account_public_address& address , bool testnet , const std::string& str_or_url - , bool cli_confirm = true + , std::function<std::string(const std::string&, const std::vector<std::string>&, bool)> dns_confirm = return_first_address ); bool is_coinbase(const transaction& tx); diff --git a/src/cryptonote_basic/miner.cpp b/src/cryptonote_basic/miner.cpp index cd0943618..9248e2e1d 100644 --- a/src/cryptonote_basic/miner.cpp +++ b/src/cryptonote_basic/miner.cpp @@ -846,40 +846,100 @@ namespace cryptonote #elif defined(__linux__) - // i've only tested on UBUNTU, these paths might be different on other systems - // need to figure out a way to make this more flexible - std::string power_supply_path = ""; - const std::string POWER_SUPPLY_STATUS_PATHS[] = - { - "/sys/class/power_supply/ACAD/online", - "/sys/class/power_supply/AC/online", - "/sys/class/power_supply/AC0/online", - "/sys/class/power_supply/ADP0/online" - }; + // Use the power_supply class http://lxr.linux.no/#linux+v4.10.1/Documentation/power/power_supply_class.txt + std::string power_supply_class_path = "/sys/class/power_supply"; - for(const std::string& path : POWER_SUPPLY_STATUS_PATHS) + boost::tribool on_battery = boost::logic::tribool(boost::logic::indeterminate); + if (boost::filesystem::is_directory(power_supply_class_path)) { - if( epee::file_io_utils::is_file_exist(path) ) + const boost::filesystem::directory_iterator end_itr; + for (boost::filesystem::directory_iterator iter(power_supply_class_path); iter != end_itr; ++iter) { - power_supply_path = path; - break; + const boost::filesystem::path& power_supply_path = iter->path(); + if (boost::filesystem::is_directory(power_supply_path)) + { + std::ifstream power_supply_present_stream((power_supply_path / "present").string()); + if (power_supply_present_stream.fail()) + { + LOG_PRINT_L0("Unable to read from " << power_supply_path << " to check if power supply present"); + continue; + } + + if (power_supply_present_stream.get() != '1') + { + LOG_PRINT_L4("Power supply not present at " << power_supply_path); + continue; + } + + boost::filesystem::path power_supply_type_path = power_supply_path / "type"; + if (boost::filesystem::is_regular_file(power_supply_type_path)) + { + std::ifstream power_supply_type_stream(power_supply_type_path.string()); + if (power_supply_type_stream.fail()) + { + LOG_PRINT_L0("Unable to read from " << power_supply_type_path << " to check power supply type"); + continue; + } + + std::string power_supply_type; + std::getline(power_supply_type_stream, power_supply_type); + + // If there is an AC adapter that's present and online we can break early + if (boost::starts_with(power_supply_type, "Mains")) + { + boost::filesystem::path power_supply_online_path = power_supply_path / "online"; + if (boost::filesystem::is_regular_file(power_supply_online_path)) + { + std::ifstream power_supply_online_stream(power_supply_online_path.string()); + if (power_supply_online_stream.fail()) + { + LOG_PRINT_L0("Unable to read from " << power_supply_online_path << " to check ac power supply status"); + continue; + } + + if (power_supply_online_stream.get() == '1') + { + return boost::logic::tribool(false); + } + } + } + else if (boost::starts_with(power_supply_type, "Battery") && boost::logic::indeterminate(on_battery)) + { + boost::filesystem::path power_supply_status_path = power_supply_path / "status"; + if (boost::filesystem::is_regular_file(power_supply_status_path)) + { + std::ifstream power_supply_status_stream(power_supply_status_path.string()); + if (power_supply_status_stream.fail()) + { + LOG_PRINT_L0("Unable to read from " << power_supply_status_path << " to check battery power supply status"); + continue; + } + + // Possible status are Charging, Full, Discharging, Not Charging, and Unknown + // We are only need to handle negative states right now + std::string power_supply_status; + std::getline(power_supply_status_stream, power_supply_status); + if (boost::starts_with(power_supply_status, "Charging") || boost::starts_with(power_supply_status, "Full")) + { + on_battery = boost::logic::tribool(false); + } + + if (boost::starts_with(power_supply_status, "Discharging")) + { + on_battery = boost::logic::tribool(true); + } + } + } + } + } } } - if( power_supply_path.empty() ) + if (boost::logic::indeterminate(on_battery)) { - LOG_ERROR("Couldn't find battery/power status file, can't determine if plugged in!"); - return boost::logic::tribool(boost::logic::indeterminate);; + LOG_ERROR("couldn't query power status from " << power_supply_class_path); } - - std::ifstream power_stream(power_supply_path); - if( power_stream.fail() ) - { - LOG_ERROR("failed to open '" << power_supply_path << "'"); - return boost::logic::tribool(boost::logic::indeterminate);; - } - - return boost::logic::tribool( (power_stream.get() != '1') ); + return on_battery; #endif diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 745608b9f..794dee8aa 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -279,7 +279,8 @@ uint64_t Blockchain::get_current_blockchain_height() const bool Blockchain::init(BlockchainDB* db, const bool testnet, const cryptonote::test_options *test_options) { LOG_PRINT_L3("Blockchain::" << __func__); - CRITICAL_REGION_LOCAL(m_blockchain_lock); + CRITICAL_REGION_LOCAL(m_tx_pool); + CRITICAL_REGION_LOCAL1(m_blockchain_lock); bool fakechain = test_options != NULL; @@ -3097,6 +3098,8 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& CRITICAL_REGION_LOCAL(m_blockchain_lock); TIME_MEASURE_START(t1); + static bool seen_future_version = false; + m_db->block_txn_start(true); if(bl.prev_id != get_tail_id()) { @@ -3106,6 +3109,18 @@ leave: return false; } + // warn users if they're running an old version + if (!seen_future_version && bl.major_version > m_hardfork->get_ideal_version()) + { + seen_future_version = true; + const el::Level level = el::Level::Warning; + MCLOG_RED(level, "global", "**********************************************************************"); + MCLOG_RED(level, "global", "A block was seen on the network with a version higher than the last"); + MCLOG_RED(level, "global", "known one. This may be an old version of the daemon, and a software"); + MCLOG_RED(level, "global", "update may be required to sync further. Try running: update check"); + MCLOG_RED(level, "global", "**********************************************************************"); + } + // this is a cheap test if (!m_hardfork->check(bl)) { @@ -3541,19 +3556,17 @@ void Blockchain::set_enforce_dns_checkpoints(bool enforce_checkpoints) } //------------------------------------------------------------------ -void Blockchain::block_longhash_worker(const uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const +void Blockchain::block_longhash_worker(uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const { TIME_MEASURE_START(t); slow_hash_allocate_state(); - //FIXME: height should be changing here, as get_block_longhash expects - // the height of the block passed to it for (const auto & block : blocks) { if (m_cancel) - return; + break; crypto::hash id = get_block_hash(block); - crypto::hash pow = get_block_longhash(block, height); + crypto::hash pow = get_block_longhash(block, height++); map.emplace(id, pow); } @@ -3745,9 +3758,11 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::list<block_complete_e if (!blocks_exist) { m_blocks_longhash_table.clear(); + uint64_t thread_height = height; for (uint64_t i = 0; i < threads; i++) { - thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, height + (i * batches), std::cref(blocks[i]), std::ref(maps[i])))); + thread_list.push_back(new boost::thread(attrs, boost::bind(&Blockchain::block_longhash_worker, this, thread_height, std::cref(blocks[i]), std::ref(maps[i])))); + thread_height += blocks[i].size(); } for (size_t j = 0; j < thread_list.size(); j++) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 4f2e4f0d3..564b53af3 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -846,7 +846,7 @@ namespace cryptonote * @param blocks the blocks to be hashed * @param map return-by-reference the hashes for each block */ - void block_longhash_worker(const uint64_t height, const std::vector<block> &blocks, + void block_longhash_worker(uint64_t height, const std::vector<block> &blocks, std::unordered_map<crypto::hash, crypto::hash> &map) const; /** diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index a96bacd44..62e92ba30 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -662,6 +662,12 @@ namespace cryptonote return false; } + if (!check_tx_inputs_ring_members_diff(tx)) + { + MERROR_VER("tx uses duplicate ring members"); + return false; + } + if (!check_tx_inputs_keyimages_domain(tx)) { MERROR_VER("tx uses key image not in the valid domain"); @@ -754,6 +760,22 @@ namespace cryptonote return true; } //----------------------------------------------------------------------------------------------- + bool core::check_tx_inputs_ring_members_diff(const transaction& tx) const + { + const uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); + if (version >= 6) + { + for(const auto& in: tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + for (size_t n = 1; n < tokey_in.key_offsets.size(); ++n) + if (tokey_in.key_offsets[n] == 0) + return false; + } + } + return true; + } + //----------------------------------------------------------------------------------------------- bool core::check_tx_inputs_keyimages_domain(const transaction& tx) const { std::unordered_set<crypto::key_image> ki; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index e5fbf7f91..4ced91e23 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -781,6 +781,15 @@ namespace cryptonote bool check_tx_inputs_keyimages_diff(const transaction& tx) const; /** + * @brief verify that each ring uses distinct members + * + * @param tx the transaction to check + * + * @return false if any ring uses duplicate members, true otherwise + */ + bool check_tx_inputs_ring_members_diff(const transaction& tx) const; + + /** * @brief verify that each input key image in a transaction is in * the valid domain * diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 26d5fb767..abb4b31ec 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -265,7 +265,7 @@ namespace cryptonote // "Shuffle" outs std::vector<tx_destination_entry> shuffled_dsts(destinations); - std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); + std::random_shuffle(shuffled_dsts.begin(), shuffled_dsts.end(), [](int i) { return crypto::rand<int>() % i; }); uint64_t summary_outs_money = 0; //fill outputs diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index 1858ccdd8..47a41d070 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -493,7 +493,6 @@ private: */ std::unordered_set<crypto::hash> m_timed_out_transactions; - std::string m_config_folder; //!< the folder to save state to Blockchain& m_blockchain; //!< reference to the Blockchain object }; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c5bc834ad..3dfe86fe1 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -208,7 +208,7 @@ namespace cryptonote cnx.recv_idle_time = timestamp - cntxt.m_last_recv; cnx.send_count = cntxt.m_send_cnt; - cnx.send_idle_time = timestamp; + cnx.send_idle_time = timestamp - cntxt.m_last_send; cnx.state = get_protocol_state_string(cntxt.m_state); diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index a7caeeffc..d654954ef 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -256,7 +256,8 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, args.front())) { bool dnssec_valid; - std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(args.front(), dnssec_valid); + std::string address_str = tools::dns_utils::get_account_address_as_str_from_url(args.front(), dnssec_valid, + [](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid){return addresses[0];}); if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, false, address_str)) { if(!cryptonote::get_account_integrated_address_from_str(adr, has_payment_id, payment_id, true, address_str)) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 5d8d95b03..bf2d8a7b7 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1583,6 +1583,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) double avgreward = 0; std::vector<uint64_t> sizes; sizes.reserve(nblocks); + uint64_t earliest = std::numeric_limits<uint64_t>::max(), latest = 0; std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0); for (const auto &bhr: bhres.headers) { @@ -1594,12 +1595,14 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) static_assert(sizeof(bhr.minor_version) == 1, "major_version expected to be uint8_t"); major_versions[(unsigned)bhr.major_version]++; minor_versions[(unsigned)bhr.minor_version]++; + earliest = std::min(earliest, bhr.timestamp); + latest = std::max(latest, bhr.timestamp); } avgdiff /= nblocks; avgnumtxes /= nblocks; avgreward /= nblocks; uint64_t median_block_size = epee::misc_utils::median(sizes); - tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", avg num txes " << avgnumtxes + tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes << ", avg. reward " << cryptonote::print_money(avgreward) << ", median block size " << median_block_size; unsigned int max_major = 256, max_minor = 256; diff --git a/src/daemonizer/posix_daemonizer.inl b/src/daemonizer/posix_daemonizer.inl index f8be15dda..506c7766f 100644 --- a/src/daemonizer/posix_daemonizer.inl +++ b/src/daemonizer/posix_daemonizer.inl @@ -43,6 +43,10 @@ namespace daemonizer "detach" , "Run as daemon" }; + const command_line::arg_descriptor<std::string> arg_pidfile = { + "pidfile" + , "File path to write the daemon's PID to (optional, requires --detach)" + }; const command_line::arg_descriptor<bool> arg_non_interactive = { "non-interactive" , "Run non-interactive" @@ -55,6 +59,7 @@ namespace daemonizer ) { command_line::add_arg(normal_options, arg_detach); + command_line::add_arg(normal_options, arg_pidfile); command_line::add_arg(normal_options, arg_non_interactive); } @@ -80,7 +85,12 @@ namespace daemonizer if (command_line::has_arg(vm, arg_detach)) { tools::success_msg_writer() << "Forking to background..."; - posix::fork(); + std::string pidfile; + if (command_line::has_arg(vm, arg_pidfile)) + { + pidfile = command_line::get_arg(vm, arg_pidfile); + } + posix::fork(pidfile); auto daemon = executor.create_daemon(vm); return daemon.run(); } diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp index d9b4a6d0e..4dff04f3f 100644 --- a/src/daemonizer/posix_fork.cpp +++ b/src/daemonizer/posix_fork.cpp @@ -21,15 +21,43 @@ namespace posix { namespace { - void quit(std::string const & message) + void quit(const std::string & message) { LOG_ERROR(message); throw std::runtime_error(message); } } -void fork() +void fork(const std::string & pidfile) { + // If a PID file is specified, we open the file here, because + // we can't report errors after the fork operation. + // When we fork, we close thise file in each of the parent + // processes. + // Only in the final child process do we write the PID to the + // file (and close it). + std::ofstream pidofs; + if (! pidfile.empty ()) + { + int oldpid; + std::ifstream pidrifs; + pidrifs.open(pidfile, std::fstream::in); + if (! pidrifs.fail()) + { + // Read the PID and send signal 0 to see if the process exists. + if (pidrifs >> oldpid && oldpid > 1 && kill(oldpid, 0) == 0) + { + quit("PID file " + pidfile + " already exists and the PID therein is valid"); + } + pidrifs.close(); + } + + pidofs.open(pidfile, std::fstream::out | std::fstream::trunc); + if (pidofs.fail()) + { + quit("Failed to open specified PID file for writing"); + } + } // Fork the process and have the parent exit. If the process was started // from a shell, this returns control to the user. Forking a new process is // also a prerequisite for the subsequent call to setsid(). @@ -38,7 +66,7 @@ void fork() if (pid > 0) { // We're in the parent process and need to exit. - // + pidofs.close(); // When the exit() function is used, the program terminates without // invoking local variables' destructors. Only global variables are // destroyed. @@ -59,6 +87,7 @@ void fork() { if (pid > 0) { + pidofs.close(); exit(0); } else @@ -67,6 +96,13 @@ void fork() } } + if (! pidofs.fail()) + { + int pid = ::getpid(); + pidofs << pid << std::endl; + pidofs.close(); + } + // Close the standard streams. This decouples the daemon from the terminal // that started it. close(0); diff --git a/src/daemonizer/posix_fork.h b/src/daemonizer/posix_fork.h index 459417d25..77ef4cb19 100644 --- a/src/daemonizer/posix_fork.h +++ b/src/daemonizer/posix_fork.h @@ -1,21 +1,21 @@ // Copyright (c) 2014-2017, The Monero Project -// +// // All rights reserved. -// +// // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. -// +// // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL @@ -27,12 +27,13 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma once +#include <string> #ifndef WIN32 namespace posix { -void fork(); +void fork(const std::string & pidfile); } diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 8798a52e0..ed00de731 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -61,8 +61,11 @@ namespace nodetool template<class base_type> struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base { + p2p_connection_context_t(): support_flags(0), m_in_timedsync(false) {} + peerid_type peer_id; uint32_t support_flags; + bool m_in_timedsync; }; template<class t_payload_net_handler> diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index b23090c7d..3cd48ccf0 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -444,11 +444,11 @@ namespace nodetool std::vector<std::vector<std::string>> dns_results; dns_results.resize(m_seed_nodes_list.size()); - std::list<boost::thread*> dns_threads; + std::list<boost::thread> dns_threads; uint64_t result_index = 0; for (const std::string& addr_str : m_seed_nodes_list) { - boost::thread* th = new boost::thread([=, &dns_results, &addr_str] + boost::thread th = boost::thread([=, &dns_results, &addr_str] { MDEBUG("dns_threads[" << result_index << "] created for: " << addr_str); // TODO: care about dnssec avail/valid @@ -474,19 +474,19 @@ namespace nodetool dns_results[result_index] = addr_list; }); - dns_threads.push_back(th); + dns_threads.push_back(std::move(th)); ++result_index; } MDEBUG("dns_threads created, now waiting for completion or timeout of " << CRYPTONOTE_DNS_TIMEOUT_MS << "ms"); boost::chrono::system_clock::time_point deadline = boost::chrono::system_clock::now() + boost::chrono::milliseconds(CRYPTONOTE_DNS_TIMEOUT_MS); uint64_t i = 0; - for (boost::thread* th : dns_threads) + for (boost::thread& th : dns_threads) { - if (! th->try_join_until(deadline)) + if (! th.try_join_until(deadline)) { MWARNING("dns_threads[" << i << "] timed out, sending interrupt"); - th->interrupt(); + th.interrupt(); } ++i; } @@ -714,6 +714,14 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::send_stop_signal() { + std::list<boost::uuids::uuid> connection_ids; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { + connection_ids.push_back(cntxt.m_connection_id); + return true; + }); + for (const auto &connection_id: connection_ids) + m_net_server.get_config_object().close(connection_id); + m_payload_handler.stop(); m_net_server.send_stop_signal(); MDEBUG("[node] Stop signal sent"); @@ -1382,7 +1390,7 @@ namespace nodetool } crypto::public_key pk = AUTO_VAL_INIT(pk); epee::string_tools::hex_to_pod(::config::P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY, pk); - crypto::hash h = tools::get_proof_of_trust_hash(tr); + crypto::hash h = get_proof_of_trust_hash(tr); if(!crypto::check_signature(h, pk, tr.sign)) { LOG_ERROR("check_trust failed: sign check failed"); diff --git a/src/p2p/network_throttle.cpp b/src/p2p/network_throttle.cpp index 6d68f3286..18148fd6a 100644 --- a/src/p2p/network_throttle.cpp +++ b/src/p2p/network_throttle.cpp @@ -77,28 +77,22 @@ int network_throttle_manager::xxx; // ================================================================================================ // methods: i_network_throttle & network_throttle_manager::get_global_throttle_in() { - boost::call_once(m_once_get_global_throttle_in, [] { m_obj_get_global_throttle_in.reset(new network_throttle("in/all","<<< global-IN",10)); } ); - return * m_obj_get_global_throttle_in; + static network_throttle obj_get_global_throttle_in("in/all","<<< global-IN",10); + return obj_get_global_throttle_in; } -boost::once_flag network_throttle_manager::m_once_get_global_throttle_in; -std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_in; i_network_throttle & network_throttle_manager::get_global_throttle_inreq() { - boost::call_once(m_once_get_global_throttle_inreq, [] { m_obj_get_global_throttle_inreq.reset(new network_throttle("inreq/all", "<== global-IN-REQ",10)); } ); - return * m_obj_get_global_throttle_inreq; + static network_throttle obj_get_global_throttle_inreq("inreq/all", "<== global-IN-REQ",10); + return obj_get_global_throttle_inreq; } -boost::once_flag network_throttle_manager::m_once_get_global_throttle_inreq; -std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_inreq; i_network_throttle & network_throttle_manager::get_global_throttle_out() { - boost::call_once(m_once_get_global_throttle_out, [] { m_obj_get_global_throttle_out.reset(new network_throttle("out/all", ">>> global-OUT",10)); } ); - return * m_obj_get_global_throttle_out; + static network_throttle obj_get_global_throttle_out("out/all", ">>> global-OUT",10); + return obj_get_global_throttle_out; } -boost::once_flag network_throttle_manager::m_once_get_global_throttle_out; -std::unique_ptr<i_network_throttle> network_throttle_manager::m_obj_get_global_throttle_out; diff --git a/src/p2p/network_throttle.hpp b/src/p2p/network_throttle.hpp index a747cdd71..81d77d5c0 100644 --- a/src/p2p/network_throttle.hpp +++ b/src/p2p/network_throttle.hpp @@ -111,13 +111,6 @@ class network_throttle_manager { //protected: public: // XXX - // [[note1]] - static boost::once_flag m_once_get_global_throttle_in; - static boost::once_flag m_once_get_global_throttle_inreq; // [[note2]] - static boost::once_flag m_once_get_global_throttle_out; - static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_in; - static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_inreq; - static std::unique_ptr<i_network_throttle> m_obj_get_global_throttle_out; static boost::mutex m_lock_get_global_throttle_in; static boost::mutex m_lock_get_global_throttle_inreq; diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index a471211a6..d8932a1df 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -440,6 +440,14 @@ namespace nodetool #endif + inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) + { + std::string s; + s.append(reinterpret_cast<const char*>(&pot.peer_id), sizeof(pot.peer_id)); + s.append(reinterpret_cast<const char*>(&pot.time), sizeof(pot.time)); + return crypto::cn_fast_hash(s.data(), s.size()); + } + } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 97fe18696..097958d24 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -227,6 +227,28 @@ namespace cryptonote res.status = CORE_RPC_STATUS_OK; return true; } + bool core_rpc_server::on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res) + { + CHECK_CORE_BUSY(); + std::list<block> blks; + + if(!m_core.get_alternative_blocks(blks)) + { + res.status = "Failed"; + return false; + } + + res.blks_hashes.reserve(blks.size()); + + for (auto const& blk: blks) + { + res.blks_hashes.push_back(epee::string_tools::pod_to_hex(get_block_hash(blk))); + } + + MDEBUG("on_get_alt_blocks_hashes: " << blks.size() << " blocks " ); + res.status = CORE_RPC_STATUS_OK; + return true; + } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res) { diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 44ac6f07a..cf95b0edf 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -81,6 +81,7 @@ namespace cryptonote MAP_URI_AUTO_BIN2("/get_outs.bin", on_get_outs_bin, COMMAND_RPC_GET_OUTPUTS_BIN) MAP_URI_AUTO_BIN2("/getrandom_rctouts.bin", on_get_random_rct_outs, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS) MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) + MAP_URI_AUTO_JON2("/get_alt_blocks_hashes", on_get_alt_blocks_hashes, COMMAND_RPC_GET_ALT_BLOCKS_HASHES) 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_IF("/start_mining", on_start_mining, COMMAND_RPC_START_MINING, !m_restricted) @@ -128,6 +129,7 @@ namespace cryptonote bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res); bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); + bool on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res); bool on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res); bool on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res); bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 7a1f5a963..a38952bff 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -49,7 +49,7 @@ namespace cryptonote // advance which version they will stop working with // Don't go over 32767 for any of these #define CORE_RPC_VERSION_MAJOR 1 -#define CORE_RPC_VERSION_MINOR 12 +#define CORE_RPC_VERSION_MINOR 13 #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) @@ -146,6 +146,25 @@ namespace cryptonote }; }; + struct COMMAND_RPC_GET_ALT_BLOCKS_HASHES + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::vector<std::string> blks_hashes; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(blks_hashes) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; struct COMMAND_RPC_GET_HASHES_FAST { diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index adf2fde80..bf8fbe3d0 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -281,6 +281,42 @@ namespace { return boost::lexical_cast<std::string>(version >> 16) + "." + boost::lexical_cast<std::string>(version & 0xffff); } + + std::string oa_prompter(const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid) + { + if (addresses.empty()) + return {}; + // prompt user for confirmation. + // inform user of DNSSEC validation status as well. + std::string dnssec_str; + if (dnssec_valid) + { + dnssec_str = tr("DNSSEC validation passed"); + } + else + { + dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!"); + } + std::stringstream prompt; + prompt << tr("For URL: ") << url + << ", " << dnssec_str << std::endl + << tr(" Monero Address = ") << addresses[0] + << std::endl + << tr("Is this OK? (Y/n) ") + ; + // prompt the user for confirmation given the dns query and dnssec status + std::string confirm_dns_ok = command_line::input_line(prompt.str()); + if (std::cin.eof()) + { + return {}; + } + if (!command_line::is_yes(confirm_dns_ok)) + { + std::cout << tr("you have cancelled the transfer request") << std::endl; + return {}; + } + return addresses[0]; + } } @@ -2215,7 +2251,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri cryptonote::tx_destination_entry de; bool has_payment_id; crypto::hash8 new_payment_id; - if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i])) + if (!cryptonote::get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -2375,7 +2411,8 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri { auto & ptx = ptx_vector.back(); m_wallet->commit_tx(ptx); - success_msg_writer(true) << tr("Money successfully sent, transaction ") << get_transaction_hash(ptx.tx); + success_msg_writer(true) << tr("Transaction successfully submitted, transaction ") << get_transaction_hash(ptx.tx) << ENDL + << tr("You can check its status by using the `show_transfers` command."); // if no exception, remove element from vector ptx_vector.pop_back(); @@ -2713,7 +2750,7 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector<std::string> &a bool has_payment_id; crypto::hash8 new_payment_id; cryptonote::account_public_address address; - if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0])) + if (!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[0], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -2978,12 +3015,39 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, // gather info to ask the user uint64_t amount = 0, amount_to_dests = 0, change = 0; size_t min_mixin = ~0; - std::unordered_map<std::string, uint64_t> dests; + std::unordered_map<std::string, std::pair<std::string, uint64_t>> dests; const std::string wallet_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); int first_known_non_zero_change_index = -1; + std::string payment_id_string = ""; for (size_t n = 0; n < get_num_txes(); ++n) { const tools::wallet2::tx_construction_data &cd = get_tx(n); + + std::vector<tx_extra_field> tx_extra_fields; + bool has_encrypted_payment_id = false; + crypto::hash8 payment_id8 = cryptonote::null_hash8; + if (cryptonote::parse_tx_extra(cd.extra, tx_extra_fields)) + { + tx_extra_nonce extra_nonce; + if (find_tx_extra_field_by_type(tx_extra_fields, extra_nonce)) + { + crypto::hash payment_id; + if(get_encrypted_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id8)) + { + if (!payment_id_string.empty()) + payment_id_string += ", "; + payment_id_string = std::string("encrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id8); + has_encrypted_payment_id = true; + } + else if (get_payment_id_from_tx_extra_nonce(extra_nonce.nonce, payment_id)) + { + if (!payment_id_string.empty()) + payment_id_string += ", "; + payment_id_string = std::string("unencrypted payment ID ") + epee::string_tools::pod_to_hex(payment_id); + } + } + } + for (size_t s = 0; s < cd.sources.size(); ++s) { amount += cd.sources[s].amount; @@ -2994,23 +3058,30 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, for (size_t d = 0; d < cd.splitted_dsts.size(); ++d) { const tx_destination_entry &entry = cd.splitted_dsts[d]; - std::string address = get_account_address_as_str(m_wallet->testnet(), entry.addr); - std::unordered_map<std::string,uint64_t>::iterator i = dests.find(address); + std::string address, standard_address = get_account_address_as_str(m_wallet->testnet(), entry.addr); + if (has_encrypted_payment_id) + { + address = get_account_integrated_address_as_str(m_wallet->testnet(), entry.addr, payment_id8); + address += std::string(" (" + standard_address + " with encrypted payment id " + epee::string_tools::pod_to_hex(payment_id8) + ")"); + } + else + address = standard_address; + std::unordered_map<std::string,std::pair<std::string,uint64_t>>::iterator i = dests.find(standard_address); if (i == dests.end()) - dests.insert(std::make_pair(address, entry.amount)); + dests.insert(std::make_pair(standard_address, std::make_pair(address, entry.amount))); else - i->second += entry.amount; + i->second.second += entry.amount; amount_to_dests += entry.amount; } if (cd.change_dts.amount > 0) { - std::unordered_map<std::string, uint64_t>::iterator it = dests.find(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr)); + std::unordered_map<std::string, std::pair<std::string, uint64_t>>::iterator it = dests.find(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr)); if (it == dests.end()) { fail_msg_writer() << tr("Claimed change does not go to a paid address"); return false; } - if (it->second < cd.change_dts.amount) + if (it->second.second < cd.change_dts.amount) { fail_msg_writer() << tr("Claimed change is larger than payment to the change address"); return false; @@ -3026,15 +3097,19 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, } } change += cd.change_dts.amount; - it->second -= cd.change_dts.amount; - if (it->second == 0) + it->second.second -= cd.change_dts.amount; + if (it->second.second == 0) dests.erase(get_account_address_as_str(m_wallet->testnet(), cd.change_dts.addr)); } } + + if (payment_id_string.empty()) + payment_id_string = "no payment ID"; + std::string dest_string; - for (std::unordered_map<std::string, uint64_t>::const_iterator i = dests.begin(); i != dests.end(); ) + for (std::unordered_map<std::string, std::pair<std::string, uint64_t>>::const_iterator i = dests.begin(); i != dests.end(); ) { - dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second) % i->first).str(); + dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second.second) % i->second.first).str(); ++i; if (i != dests.end()) dest_string += ", "; @@ -3052,7 +3127,7 @@ bool simple_wallet::accept_loaded_tx(const std::function<size_t()> get_num_txes, change_string += tr("no change"); uint64_t fee = amount - amount_to_dests; - std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % extra_message).str(); + std::string prompt_str = (boost::format(tr("Loaded %lu transactions, for %s, fee %s, %s, %s, with min mixin %lu, %s. %sIs this okay? (Y/Yes/N/No): ")) % (unsigned long)get_num_txes() % print_money(amount) % print_money(fee) % dest_string % change_string % (unsigned long)min_mixin % payment_id_string % extra_message).str(); return command_line::is_yes(command_line::input_line(prompt_str)); } //---------------------------------------------------------------------------------------------------- @@ -3273,7 +3348,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &args) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1])) + if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -3375,7 +3450,7 @@ bool simple_wallet::check_tx_key(const std::vector<std::string> &args_) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), local_args[2])) + if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), local_args[2], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -3527,7 +3602,7 @@ bool simple_wallet::check_tx_proof(const std::vector<std::string> &args) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1])) + if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), args[1], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -4045,7 +4120,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id8; - if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), args[1])) + if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), args[1], oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); return true; @@ -4236,7 +4311,7 @@ bool simple_wallet::verify(const std::vector<std::string> &args) cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), address_string)) + if(!cryptonote::get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), address_string, oa_prompter)) { fail_msg_writer() << tr("failed to parse address"); return true; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6b1026a55..034b0a02a 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1388,7 +1388,7 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei } } //---------------------------------------------------------------------------------------------------- -void wallet2::update_pool_state() +void wallet2::update_pool_state(bool refreshed) { MDEBUG("update_pool_state start"); @@ -1424,13 +1424,14 @@ void wallet2::update_pool_state() // a tx is removed from the pool due to being found in a new block, but // just before the block is visible by refresh. So we keep a boolean, so // that the first time we don't see the tx, we set that boolean, and only - // delete it the second time it is checked + // delete it the second time it is checked (but only when refreshed, so + // we're sure we've seen the blockchain state first) if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending) { LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as not in pool"); pit->second.m_state = wallet2::unconfirmed_transfer_details::pending_not_in_pool; } - else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool) + else if (pit->second.m_state == wallet2::unconfirmed_transfer_details::pending_not_in_pool && refreshed) { LOG_PRINT_L1("Pending txid " << txid << " not in pool, marking as failed"); pit->second.m_state = wallet2::unconfirmed_transfer_details::failed; @@ -1459,24 +1460,30 @@ void wallet2::update_pool_state() MDEBUG("update_pool_state done first loop"); // remove pool txes to us that aren't in the pool anymore - std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); - while (uit != m_unconfirmed_payments.end()) + // but only if we just refreshed, so that the tx can go in + // the in transfers list instead (or nowhere if it just + // disappeared without being mined) + if (refreshed) { - const crypto::hash &txid = uit->second.m_tx_hash; - bool found = false; - for (const auto &it2: res.tx_hashes) + std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); + while (uit != m_unconfirmed_payments.end()) { - if (it2 == txid) + const crypto::hash &txid = uit->second.m_tx_hash; + bool found = false; + for (const auto &it2: res.tx_hashes) { - found = true; - break; + if (it2 == txid) + { + found = true; + break; + } + } + auto pit = uit++; + if (!found) + { + MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool"); + m_unconfirmed_payments.erase(pit); } - } - auto pit = uit++; - if (!found) - { - MDEBUG("Removing " << txid << " from unconfirmed payments, not found in pool"); - m_unconfirmed_payments.erase(pit); } } MDEBUG("update_pool_state done second loop"); @@ -1490,7 +1497,16 @@ void wallet2::update_pool_state() LOG_PRINT_L2("Already seen " << txid << ", skipped"); continue; } - if (m_unconfirmed_payments.find(txid) == m_unconfirmed_payments.end()) + bool txid_found_in_up = false; + for (const auto &up: m_unconfirmed_payments) + { + if (up.second.m_tx_hash == txid) + { + txid_found_in_up = true; + break; + } + } + if (!txid_found_in_up) { LOG_PRINT_L1("Found new pool tx: " << txid); bool found = false; @@ -1685,6 +1701,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re uint64_t blocks_start_height; std::list<cryptonote::block_complete_entry> blocks; std::vector<COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices> o_indices; + bool refreshed = false; // pull the first set of blocks get_short_chain_history(short_chain_history); @@ -1726,6 +1743,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re if(blocks_start_height == next_blocks_start_height) { m_node_rpc_proxy.set_height(m_blockchain.size()); + refreshed = true; break; } @@ -1764,7 +1782,7 @@ void wallet2::refresh(uint64_t start_height, uint64_t & blocks_fetched, bool& re { // If stop() is called we don't need to check pending transactions if(m_run.load(std::memory_order_relaxed)) - update_pool_state(); + update_pool_state(refreshed); } catch (...) { @@ -4359,7 +4377,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // try to pick outputs not from the same block. We will get two outputs, one for // the destination, and one for change. LOG_PRINT_L2("checking preferred"); - std::vector<size_t> prefered_inputs; + std::vector<size_t> preferred_inputs; uint64_t rct_outs_needed = 2 * (fake_outs_count + 1); rct_outs_needed += 100; // some fudge factor since we don't know how many are locked if (use_rct && get_num_rct_outputs() >= rct_outs_needed) @@ -4367,12 +4385,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which // will get us a known fee. uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count + 1, 2), fee_multiplier); - prefered_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee); - if (!prefered_inputs.empty()) + preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee); + if (!preferred_inputs.empty()) { string s; - for (auto i: prefered_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") "; - LOG_PRINT_L1("Found prefered rct inputs for rct tx: " << s); + for (auto i: preferred_inputs) s += boost::lexical_cast<std::string>(i) + "(" + print_money(m_transfers[i].amount()) + ") "; + LOG_PRINT_L1("Found preferred rct inputs for rct tx: " << s); } } LOG_PRINT_L2("done checking preferred"); @@ -4427,8 +4445,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } pop_if_present(unused_transfers_indices, idx); pop_if_present(unused_dust_indices, idx); - } else if (!prefered_inputs.empty()) { - idx = pop_back(prefered_inputs); + } else if (!preferred_inputs.empty()) { + idx = pop_back(preferred_inputs); pop_if_present(unused_transfers_indices, idx); pop_if_present(unused_dust_indices, idx); } else @@ -4521,7 +4539,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp { uint64_t new_paid_amount = i->amount /*+ test_ptx.fee*/ - needed_fee; LOG_PRINT_L2("Adjusting amount paid to " << get_account_address_as_str(m_testnet, i->addr) << " from " << - print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accomodate " << + print_money(i->amount) << " to " << print_money(new_paid_amount) << " to accommodate " << print_money(needed_fee) << " fee"); dsts[0].amount += i->amount - new_paid_amount; i->amount = new_paid_amount; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index e7692badb..b853c5f3c 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -585,7 +585,7 @@ namespace tools uint64_t import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent); uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); - void update_pool_state(); + void update_pool_state(bool refreshed = false); std::string encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated = true) const; std::string encrypt_with_view_secret_key(const std::string &plaintext, bool authenticated = true) const; diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 1508f3791..34c5a2a5d 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -149,8 +149,10 @@ namespace wallet_args if (command_line::get_arg(vm, command_line::arg_help)) { - tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; - tools::msg_writer() << wallet_args::tr("Usage:") << ' ' << usage; + tools::msg_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; + tools::msg_writer() << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n" + "daemon to work correctly.") << ENDL; + tools::msg_writer() << wallet_args::tr("Usage:") << ENDL << " " << usage; tools::msg_writer() << desc_all; return boost::none; } diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e7b9b5a71..58b020740 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -352,10 +352,25 @@ namespace tools cryptonote::tx_destination_entry de; bool has_payment_id; crypto::hash8 new_payment_id; - if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, false)) + er.message = ""; + if(!get_account_address_from_str_or_url(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), it->address, + [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { + if (!dnssec_valid) + { + er.message = std::string("Invalid DNSSEC for ") + url; + return {}; + } + if (addresses.empty()) + { + er.message = std::string("No Monero address found at ") + url; + return {}; + } + return addresses[0]; + })) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; - er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; + if (er.message.empty()) + er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + it->address; return false; } de.amount = it->amount; @@ -454,7 +469,8 @@ namespace tools return false; } - m_wallet->commit_tx(ptx_vector); + if (!req.do_not_relay) + m_wallet->commit_tx(ptx_vector); // populate response with tx hash res.tx_hash = epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(ptx_vector.back().tx)); @@ -463,6 +479,13 @@ namespace tools res.tx_key = epee::string_tools::pod_to_hex(ptx_vector.back().tx_key); } res.fee = ptx_vector.back().fee; + + if (req.get_tx_hex) + { + cryptonote::blobdata blob; + tx_to_blob(ptx_vector.back().tx, blob); + res.tx_blob = epee::string_tools::buff_to_hex_nodelimer(blob); + } return true; } catch (const tools::error::daemon_busy& e) @@ -519,9 +542,12 @@ namespace tools ptx_vector = m_wallet->create_transactions_2(dsts, mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); LOG_PRINT_L2("on_transfer_split called create_transactions_2"); - LOG_PRINT_L2("on_transfer_split calling commit_tx"); - m_wallet->commit_tx(ptx_vector); - LOG_PRINT_L2("on_transfer_split called commit_tx"); + if (!req.do_not_relay) + { + LOG_PRINT_L2("on_transfer_split calling commit_tx"); + m_wallet->commit_tx(ptx_vector); + LOG_PRINT_L2("on_transfer_split called commit_tx"); + } // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -538,6 +564,13 @@ namespace tools res.amount_list.push_back(ptx_amount); res.fee_list.push_back(ptx.fee); + + if (req.get_tx_hex) + { + cryptonote::blobdata blob; + tx_to_blob(ptx.tx, blob); + res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob)); + } } return true; @@ -577,7 +610,8 @@ namespace tools { std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_unmixable_sweep_transactions(m_trusted_daemon); - m_wallet->commit_tx(ptx_vector); + if (!req.do_not_relay) + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -588,6 +622,12 @@ namespace tools res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key)); } res.fee_list.push_back(ptx.fee); + if (req.get_tx_hex) + { + cryptonote::blobdata blob; + tx_to_blob(ptx.tx, blob); + res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob)); + } } return true; @@ -640,7 +680,8 @@ namespace tools { std::vector<wallet2::pending_tx> ptx_vector = m_wallet->create_transactions_all(req.below_amount, dsts[0].addr, req.mixin, req.unlock_time, req.priority, extra, m_trusted_daemon); - m_wallet->commit_tx(ptx_vector); + if (!req.do_not_relay) + m_wallet->commit_tx(ptx_vector); // populate response with tx hashes for (auto & ptx : ptx_vector) @@ -650,7 +691,12 @@ namespace tools { res.tx_key_list.push_back(epee::string_tools::pod_to_hex(ptx.tx_key)); } - res.fee_list.push_back(ptx.fee); + if (req.get_tx_hex) + { + cryptonote::blobdata blob; + tx_to_blob(ptx.tx, blob); + res.tx_blob_list.push_back(epee::string_tools::buff_to_hex_nodelimer(blob)); + } } return true; @@ -1018,10 +1064,23 @@ namespace tools cryptonote::account_public_address address; bool has_payment_id; crypto::hash8 payment_id; - if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, false)) + er.message = ""; + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id, m_wallet->testnet(), req.address, + [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { + if (!dnssec_valid) + { + er.message = std::string("Invalid DNSSEC for ") + url; + return {}; + } + if (addresses.empty()) + { + er.message = std::string("No Monero address found at ") + url; + return {}; + } + return addresses[0]; + })) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; - er.message = ""; return false; } @@ -1412,10 +1471,25 @@ namespace tools bool has_payment_id; crypto::hash8 payment_id8; crypto::hash payment_id = cryptonote::null_hash; - if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, false)) + er.message = ""; + if(!get_account_address_from_str_or_url(address, has_payment_id, payment_id8, m_wallet->testnet(), req.address, + [&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string { + if (!dnssec_valid) + { + er.message = std::string("Invalid DNSSEC for ") + url; + return {}; + } + if (addresses.empty()) + { + er.message = std::string("No Monero address found at ") + url; + return {}; + } + return addresses[0]; + })) { er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS; - er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address; + if (er.message.empty()) + er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address; return false; } if (has_payment_id) diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index 12ac281e4..5832bb032 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -119,6 +119,8 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_key; + bool do_not_relay; + bool get_tx_hex; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -127,6 +129,8 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_key) + KV_SERIALIZE_OPT(do_not_relay, false) + KV_SERIALIZE_OPT(get_tx_hex, false) END_KV_SERIALIZE_MAP() }; @@ -136,12 +140,14 @@ namespace wallet_rpc std::string tx_key; std::list<std::string> amount_keys; uint64_t fee; + std::string tx_blob; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_key) KV_SERIALIZE(amount_keys) KV_SERIALIZE(fee) + KV_SERIALIZE(tx_blob) END_KV_SERIALIZE_MAP() }; }; @@ -156,6 +162,8 @@ namespace wallet_rpc uint64_t unlock_time; std::string payment_id; bool get_tx_keys; + bool do_not_relay; + bool get_tx_hex; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(destinations) @@ -164,6 +172,8 @@ namespace wallet_rpc KV_SERIALIZE(unlock_time) KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) + KV_SERIALIZE_OPT(do_not_relay, false) + KV_SERIALIZE_OPT(get_tx_hex, false) END_KV_SERIALIZE_MAP() }; @@ -182,12 +192,14 @@ namespace wallet_rpc std::list<std::string> tx_key_list; std::list<uint64_t> amount_list; std::list<uint64_t> fee_list; + std::list<std::string> tx_blob_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(amount_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(tx_blob_list) END_KV_SERIALIZE_MAP() }; }; @@ -197,9 +209,13 @@ namespace wallet_rpc struct request { bool get_tx_keys; + bool do_not_relay; + bool get_tx_hex; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(get_tx_keys) + KV_SERIALIZE_OPT(do_not_relay, false) + KV_SERIALIZE_OPT(get_tx_hex, false) END_KV_SERIALIZE_MAP() }; @@ -217,11 +233,13 @@ namespace wallet_rpc std::list<std::string> tx_hash_list; std::list<std::string> tx_key_list; std::list<uint64_t> fee_list; + std::list<std::string> tx_blob_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(tx_blob_list) END_KV_SERIALIZE_MAP() }; }; @@ -237,6 +255,8 @@ namespace wallet_rpc std::string payment_id; bool get_tx_keys; uint64_t below_amount; + bool do_not_relay; + bool get_tx_hex; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(address) @@ -246,6 +266,8 @@ namespace wallet_rpc KV_SERIALIZE(payment_id) KV_SERIALIZE(get_tx_keys) KV_SERIALIZE(below_amount) + KV_SERIALIZE_OPT(do_not_relay, false) + KV_SERIALIZE_OPT(get_tx_hex, false) END_KV_SERIALIZE_MAP() }; @@ -263,11 +285,13 @@ namespace wallet_rpc std::list<std::string> tx_hash_list; std::list<std::string> tx_key_list; std::list<uint64_t> fee_list; + std::list<std::string> tx_blob_list; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_hash_list) KV_SERIALIZE(tx_key_list) KV_SERIALIZE(fee_list) + KV_SERIALIZE(tx_blob_list) END_KV_SERIALIZE_MAP() }; }; diff --git a/utils/systemd/monerod.service b/utils/systemd/monerod.service index 182878ebb..12395eb8c 100644 --- a/utils/systemd/monerod.service +++ b/utils/systemd/monerod.service @@ -8,15 +8,10 @@ Group=monero WorkingDirectory=~ Type=forking -ExecStart=/usr/bin/monerod --config-file /etc/monerod.conf --detach +PIDFile=/var/run/monerod.pid -# This is necessary because monerod does not yet support -# writing a PID file, which means systemd tries to guess the PID -# by default, but it guesses wrong (sometimes, depending on -# random timing of events), because the daemon forks twice. -# The ultimate fix is for the daemon to write a PID file, and -# a workaround is to disable the guessing feature in systemd. -GuessMainPID=no +ExecStart=/usr/bin/monerod --config-file /etc/monerod.conf \ + --detach --pidfile /var/run/monerod.pid [Install] WantedBy=multi-user.target |