diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h index 4887c7ca..2d098e32 100644 --- a/contrib/epee/include/console_handler.h +++ b/contrib/epee/include/console_handler.h @@ -376,7 +376,7 @@ namespace epee bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& prompt, const std::string& usage = "") { async_console_handler console_handler; - return console_handler.run(ptsrv, boost::bind(no_srv_param_adapter, _1, _2, handlr), prompt, usage); + return console_handler.run(ptsrv, boost::bind(no_srv_param_adapter, boost::placeholders::_1, boost::placeholders::_2, handlr), prompt, usage); } template @@ -460,7 +460,7 @@ namespace epee /*template bool start_handling(t_srv& srv, const std::string& usage_string = "") { - start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1)); + start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, boost::placeholders::_1)); return true; }*/ @@ -489,7 +489,7 @@ namespace epee /*template bool run_handling(t_srv& srv, const std::string& usage_string) { - return run_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string); + return run_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, boost::placeholders::_1), usage_string); }*/ }; @@ -510,7 +510,7 @@ namespace epee bool run_handling(t_server* psrv, const std::string& prompt, const std::string& usage_string) { - return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder::process_command_str, this, _1, _2), prompt, usage_string); + return m_console_handler.run(psrv, boost::bind(&srv_console_handlers_binder::process_command_str, this, boost::placeholders::_1, boost::placeholders::_2), prompt, usage_string); } void stop_handling() diff --git a/contrib/epee/include/serialization/keyvalue_helper_structs.h b/contrib/epee/include/serialization/keyvalue_helper_structs.h new file mode 100644 index 00000000..e0e3f16f --- /dev/null +++ b/contrib/epee/include/serialization/keyvalue_helper_structs.h @@ -0,0 +1,40 @@ +// Copyright (c) 2006-2021, Andrey N. Sabelnikov, www.sabelnikov.net +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of the Andrey N. Sabelnikov nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +namespace epee +{ + + template + struct kvserializable_pair : public std::pair<_Ty1, _Ty2> + { + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(first) + KV_SERIALIZE(second) + END_KV_SERIALIZE_MAP() + }; +} \ No newline at end of file diff --git a/contrib/epee/include/time_helper.h b/contrib/epee/include/time_helper.h index f11dae91..40811420 100644 --- a/contrib/epee/include/time_helper.h +++ b/contrib/epee/include/time_helper.h @@ -87,7 +87,7 @@ DISABLE_VS_WARNINGS(4996) POP_VS_WARNINGS if(pt) - strftime( tmpbuf, 199, "%Y_%m_%d %H_%M_%S", pt ); + strftime( tmpbuf, 199, "%Y-%m-%d %H-%M-%S", pt ); else { std::stringstream strs; diff --git a/resources/dmg_installer_bg.png b/resources/dmg_installer_bg.png index 9c88ecb6..ee45f1e0 100644 Binary files a/resources/dmg_installer_bg.png and b/resources/dmg_installer_bg.png differ diff --git a/resources/installer_bg_164x313.bmp b/resources/installer_bg_164x313.bmp index abc47621..80599afe 100644 Binary files a/resources/installer_bg_164x313.bmp and b/resources/installer_bg_164x313.bmp differ diff --git a/resources/installer_bg_191x385.bmp b/resources/installer_bg_191x385.bmp index 48feb48d..b7b74bfe 100644 Binary files a/resources/installer_bg_191x385.bmp and b/resources/installer_bg_191x385.bmp differ diff --git a/resources/installer_bg_246x457.bmp b/resources/installer_bg_246x457.bmp index 865b3de3..3e20a2e9 100644 Binary files a/resources/installer_bg_246x457.bmp and b/resources/installer_bg_246x457.bmp differ diff --git a/resources/installer_bg_328x628.bmp b/resources/installer_bg_328x628.bmp index 280b1a73..5d7613d8 100644 Binary files a/resources/installer_bg_328x628.bmp and b/resources/installer_bg_328x628.bmp differ diff --git a/src/common/command_line.h b/src/common/command_line.h index f19a04ae..ee7985cc 100644 --- a/src/common/command_line.h +++ b/src/common/command_line.h @@ -151,7 +151,7 @@ namespace command_line } template - bool handle_error_helper(const boost::program_options::options_description& desc, F parser) + bool handle_error_helper(const boost::program_options::options_description& desc, std::string& err, F parser) { try { @@ -159,6 +159,7 @@ namespace command_line } catch (std::exception& e) { + err = e.what(); std::cerr << "Failed to parse arguments: " << e.what() << std::endl; std::cerr << desc << std::endl; return false; @@ -171,6 +172,13 @@ namespace command_line } } + template + bool handle_error_helper(const boost::program_options::options_description& desc, F parser) + { + std::string stub_err; + return handle_error_helper(desc, stub_err, parser); + } + template bool has_arg(const boost::program_options::variables_map& vm, const arg_descriptor& arg) { diff --git a/src/common/db_backend_mdbx.cpp b/src/common/db_backend_mdbx.cpp index 09670a89..3307dec8 100644 --- a/src/common/db_backend_mdbx.cpp +++ b/src/common/db_backend_mdbx.cpp @@ -143,9 +143,10 @@ namespace tools else { int res = 0; + //MDBX_txn_flags_t flags = MDBX_txn_flags_t(); unsigned int flags = 0; if (read_only) - flags += MDBX_RDONLY; + flags = MDBX_RDONLY;//flags = MDBX_TXN_RDONLY; //don't use parent tx in write transactions if parent tx was read-only (restriction in mdbx) //see "Nested transactions: Max 1 child, write txns only, no writemap" @@ -340,7 +341,9 @@ namespace tools data[0].iov_base = (void*)v; data[0].iov_len = vs; - res = mdbx_put(get_current_tx(), static_cast(h), &key, data, 0); + //MDBX_put_flags_t flags = MDBX_put_flags_t(); + unsigned flags = 0; + res = mdbx_put(get_current_tx(), static_cast(h), &key, data, flags); CHECK_AND_ASSERT_MESS_MDBX_DB(res, false, "Unable to mdbx_put"); return true; } diff --git a/src/currency_core/account.cpp b/src/currency_core/account.cpp index 97d9480e..def20255 100644 --- a/src/currency_core/account.cpp +++ b/src/currency_core/account.cpp @@ -70,8 +70,7 @@ namespace currency iv = *((crypto::chacha8_iv*)&pass_hash); crypto::chacha8(scr_data, src_length, key, iv, (char*)dst_data); } - - + //----------------------------------------------------------------- std::string account_base::get_seed_phrase(const std::string& password) const { if (m_keys_seed_binary.empty()) diff --git a/src/currency_core/currency_config.h b/src/currency_core/currency_config.h index bda8f652..4dfa5e12 100644 --- a/src/currency_core/currency_config.h +++ b/src/currency_core/currency_config.h @@ -211,7 +211,7 @@ #define MINER_CONFIG_FILENAME "miner_conf.json" #define GUI_SECURE_CONFIG_FILENAME "gui_secure_conf.bin" #define GUI_CONFIG_FILENAME "gui_settings.json" -#define GUI_INTERNAL_CONFIG "gui_internal_config.bin" +#define GUI_INTERNAL_CONFIG2 "gui_internal_config.json" diff --git a/src/currency_protocol/currency_protocol_handler.inl b/src/currency_protocol/currency_protocol_handler.inl index 65993062..39e42a3c 100644 --- a/src/currency_protocol/currency_protocol_handler.inl +++ b/src/currency_protocol/currency_protocol_handler.inl @@ -914,7 +914,8 @@ namespace currency LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height << "\r\nm_start_height=" << arg.start_height << "\r\nm_block_ids.size()=" << arg.m_block_ids.size()); - //m_p2p->drop_connection(context); + m_p2p->drop_connection(context); + m_p2p->add_ip_fail(context.m_remote_ip); } BOOST_FOREACH(auto& bl_details, arg.m_block_ids) diff --git a/src/gui/qt-daemon/Info.plist.in b/src/gui/qt-daemon/Info.plist.in index 5cff05eb..8b33aed9 100644 --- a/src/gui/qt-daemon/Info.plist.in +++ b/src/gui/qt-daemon/Info.plist.in @@ -45,10 +45,19 @@ NSHumanReadableCopyright - - NSHighResolutionCapable - True - + NSHighResolutionCapable + True + CFBundleURLTypes + + + CFBundleURLName + ZanoApp + CFBundleURLSchemes + + zano + + + diff --git a/src/gui/qt-daemon/app.icns b/src/gui/qt-daemon/app.icns index 881e134c..50e0fad5 100644 Binary files a/src/gui/qt-daemon/app.icns and b/src/gui/qt-daemon/app.icns differ diff --git a/src/gui/qt-daemon/app.ico b/src/gui/qt-daemon/app.ico index 5d170210..34837e0e 100644 Binary files a/src/gui/qt-daemon/app.ico and b/src/gui/qt-daemon/app.ico differ diff --git a/src/gui/qt-daemon/application/mainwindow.cpp b/src/gui/qt-daemon/application/mainwindow.cpp index d8ffd569..acdaef1c 100644 --- a/src/gui/qt-daemon/application/mainwindow.cpp +++ b/src/gui/qt-daemon/application/mainwindow.cpp @@ -387,9 +387,9 @@ void MainWindow::changeEvent(QEvent *e) bool MainWindow::store_app_config() { TRY_ENTRY(); - std::string conf_path = m_backend.get_config_folder() + "/" + GUI_INTERNAL_CONFIG; - LOG_PRINT_L0("storing gui internal config from " << conf_path); - CHECK_AND_ASSERT_MES(tools::serialize_obj_to_file(m_config, conf_path), false, "failed to store gui internal config"); + std::string conf_path = m_backend.get_config_folder() + "/" + GUI_INTERNAL_CONFIG2; + LOG_PRINT_L0("storing gui internal config to " << conf_path); + CHECK_AND_ASSERT_MES(epee::serialization::store_t_to_json_file(m_config, conf_path), false, "failed to store gui internal config"); return true; CATCH_ENTRY2(false); } @@ -397,9 +397,9 @@ bool MainWindow::store_app_config() bool MainWindow::load_app_config() { TRY_ENTRY(); - std::string conf_path = m_backend.get_config_folder() + "/" + GUI_INTERNAL_CONFIG; + std::string conf_path = m_backend.get_config_folder() + "/" + GUI_INTERNAL_CONFIG2; LOG_PRINT_L0("loading gui internal config from " << conf_path); - bool r = tools::unserialize_obj_from_file(m_config, conf_path); + bool r = epee::serialization::load_t_from_json_file(m_config, conf_path); LOG_PRINT_L0("gui internal config " << (r ? "loaded ok" : "was not loaded")); return r; CATCH_ENTRY2(false); @@ -643,6 +643,7 @@ bool MainWindow::show_inital() restore_pos(true); else { + m_config = AUTO_VAL_INIT(m_config); this->show(); QSize sz; sz.setHeight(770); @@ -651,6 +652,7 @@ bool MainWindow::show_inital() store_window_pos(); m_config.is_maximazed = false; m_config.is_showed = true; + m_config.disable_notifications = false; } return true; CATCH_ENTRY2(false); @@ -727,14 +729,26 @@ void qt_log_message_handler(QtMsgType type, const QMessageLogContext &context, c bool MainWindow::init_backend(int argc, char* argv[]) { TRY_ENTRY(); - if (!m_backend.init_command_line(argc, argv)) + std::string command_line_fail_details; + if (!m_backend.init_command_line(argc, argv, command_line_fail_details)) + { + this->show_msg_box(command_line_fail_details); return false; + } if (!init_window()) + { + this->show_msg_box("Failed to main screen launch, check logs for the more detais."); return false; + } if (!m_backend.init(this)) + { + this->show_msg_box("Failed to initialize backend, check debug logs for more details."); return false; + } + + if (m_backend.is_qt_logs_enabled()) { @@ -811,8 +825,25 @@ bool MainWindow::nativeEventFilter(const QByteArray &eventType, void *message, l CATCH_ENTRY2(false); } - - +bool MainWindow::get_is_disabled_notifications() +{ + return m_config.disable_notifications; +} +bool MainWindow::set_is_disabled_notifications(const bool& param) +{ + m_config.disable_notifications = param; + return m_config.disable_notifications; +} +QString MainWindow::export_wallet_history(const QString& param) +{ + TRY_ENTRY(); + LOG_API_TIMING(); + PREPARE_ARG_FROM_JSON(view::export_wallet_info, ewi); + PREPARE_RESPONSE(view::api_response, ar); + ar.error_code = m_backend.export_wallet_history(ewi); + return MAKE_RESPONSE(ar); + CATCH_ENTRY2(API_RETURN_CODE_INTERNAL_ERROR); +} bool MainWindow::update_wallets_info(const view::wallets_summary_info& wsi) { TRY_ENTRY(); @@ -834,6 +865,10 @@ bool MainWindow::money_transfer(const view::transfer_event_info& tei) LOG_PRINT_L0(get_wallet_log_prefix(tei.wallet_id) + "SENDING SIGNAL -> [money_transfer]" << std::endl << json_str); //this->money_transfer(json_str.c_str()); QMetaObject::invokeMethod(this, "money_transfer", Qt::QueuedConnection, Q_ARG(QString, json_str.c_str())); + if (m_config.disable_notifications) + return true; + + if (!m_tray_icon) return true; if (!tei.ti.is_income) @@ -852,7 +887,7 @@ bool MainWindow::money_transfer(const view::transfer_event_info& tei) return true; } - auto amount_str = currency::print_money(tei.ti.amount); + auto amount_str = currency::print_money_brief(tei.ti.amount); std::string title, msg; if (tei.ti.height == 0) // unconfirmed trx { @@ -869,6 +904,7 @@ bool MainWindow::money_transfer(const view::transfer_event_info& tei) else if (tei.ti.unlock_time) msg += m_localization[localization_id_locked]; + show_notification(title, msg); return true; diff --git a/src/gui/qt-daemon/application/mainwindow.h b/src/gui/qt-daemon/application/mainwindow.h index ba9216fc..57fcf129 100644 --- a/src/gui/qt-daemon/application/mainwindow.h +++ b/src/gui/qt-daemon/application/mainwindow.h @@ -8,6 +8,7 @@ #include #include "wallet/view_iface.h" +#include "serialization/keyvalue_helper_structs.h" #ifndef Q_MOC_RUN #include "wallet/wallets_manager.h" @@ -60,10 +61,20 @@ public: struct app_config { - std::pair m_window_position; - std::pair m_window_size; + + epee::kvserializable_pair m_window_position; + epee::kvserializable_pair m_window_size; bool is_maximazed; bool is_showed; + bool disable_notifications; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_window_position) + KV_SERIALIZE(m_window_size) + KV_SERIALIZE(is_maximazed) + KV_SERIALIZE(is_showed) + KV_SERIALIZE(disable_notifications) + END_KV_SERIALIZE_MAP() }; protected slots: @@ -150,6 +161,10 @@ public: QString get_default_fee(); QString get_options(); void bool_toggle_icon(const QString& param); + + bool get_is_disabled_notifications(); + bool set_is_disabled_notifications(const bool& param); + QString export_wallet_history(const QString& param); QString get_log_file(); QString check_available_sources(const QString& param); QString open_url_in_browser(const QString& param); @@ -176,6 +191,7 @@ signals: void do_dispatch(const QString status, const QString params); //general function void on_core_event(const QString method_name); //general function void set_options(const QString str); //general function + void get_wallet_name(); private: //-------------------- i_core_event_handler -------------------- @@ -254,7 +270,7 @@ private: enum localization_string_indices { - // order is surprizingly important here! (see also updateLocalisation in AppController.js) + // order is surprisingly important here! (see also updateLocalisation in AppController.js) localization_id_quit = 0, localization_id_is_received, localization_id_is_confirmed, diff --git a/src/gui/qt-daemon/application/urleventfilter.cpp b/src/gui/qt-daemon/application/urleventfilter.cpp new file mode 100644 index 00000000..d7e4e35e --- /dev/null +++ b/src/gui/qt-daemon/application/urleventfilter.cpp @@ -0,0 +1,18 @@ +#include "urleventfilter.h" +#include + +bool URLEventFilter::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::FileOpen) { + QFileOpenEvent *fileEvent = static_cast(event); + if(!fileEvent->url().isEmpty()) + { + QMessageBox msg; + msg.setText(fileEvent->url().toString()); + msg.exec(); + } + } else { + // standard event processing + return QObject::eventFilter(obj, event); + } + }; diff --git a/src/gui/qt-daemon/application/urleventfilter.h b/src/gui/qt-daemon/application/urleventfilter.h new file mode 100644 index 00000000..54341dbd --- /dev/null +++ b/src/gui/qt-daemon/application/urleventfilter.h @@ -0,0 +1,12 @@ +#include +#include +#include + +class URLEventFilter : public QObject +{ + Q_OBJECT +public: + URLEventFilter() : QObject(){}; +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +}; \ No newline at end of file diff --git a/src/gui/qt-daemon/layout b/src/gui/qt-daemon/layout index b028252d..d32d0f9f 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit b028252d30a32e80c12fa890c306d3c5842e937a +Subproject commit d32d0f9f239589b0cdf0ba674991124c231734de diff --git a/src/gui/qt-daemon/main.cpp b/src/gui/qt-daemon/main.cpp index cfa89717..f32dde0f 100644 --- a/src/gui/qt-daemon/main.cpp +++ b/src/gui/qt-daemon/main.cpp @@ -12,7 +12,9 @@ //#include "qtlogger.h" #include "include_base_utils.h" #include "currency_core/currency_config.h" - +#ifdef Q_OS_DARWIN +#include "application/urleventfilter.h" +#endif int main(int argc, char *argv[]) { @@ -26,6 +28,9 @@ int main(int argc, char *argv[]) // See http://crbug.com/436603. // _set_FMA3_enable(0); //#endif // ARCH_CPU_X86_64 && _MSC_VER <= 1800 + + if(argc > 1) + std::cout << argv[1] << std::endl; #ifdef _MSC_VER #ifdef _WIN64 @@ -61,12 +66,18 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); + +#ifdef Q_OS_DARWIN + URLEventFilter url_event_filter; + app.installEventFilter(&url_event_filter); +#endif + MainWindow viewer; if (!viewer.init_backend(argc, argv)) { - static_cast(&viewer)->show_msg_box("Failed to initialize backend, check debug logs for more details."); return 1; } + app.installNativeEventFilter(&viewer); viewer.setWindowTitle(CURRENCY_NAME_BASE); viewer.show_inital(); diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 301b4cda..5c0fb12e 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -81,7 +81,8 @@ namespace nodetool m_last_stat_request_time{}, m_use_only_priority_peers(false), m_peer_livetime{}, - m_debug_requests_enabled(false) + m_debug_requests_enabled(false), + m_ip_auto_blocking_enabled(false) {} static void init_options(boost::program_options::options_description& desc); @@ -215,6 +216,8 @@ namespace nodetool bool urgent_alert_worker(); bool critical_alert_worker(); bool remove_dead_connections(); + bool is_ip_good_for_adding_to_peerlist(uint32_t adress); + bool is_ip_in_blacklist(uint32_t adress); //debug functions @@ -245,6 +248,7 @@ namespace nodetool bool m_hide_my_port; bool m_offline_mode; bool m_debug_requests_enabled; + bool m_ip_auto_blocking_enabled; uint64_t m_startup_time; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 5042548d..477d7e8b 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -26,18 +26,19 @@ namespace nodetool namespace { - const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; - const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(P2P_DEFAULT_PORT)}; - const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; - const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; - const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; - const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; + const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; + const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(P2P_DEFAULT_PORT)}; + const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; + const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; + const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; + const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; const command_line::arg_descriptor arg_p2p_use_only_priority_nodes = {"use-only-priority-nodes", "Try to connect only to priority nodes"}; - const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; - const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; - const command_line::arg_descriptor arg_p2p_offline_mode = { "offline-mode", "Don't connect to any node and reject any connections", false, true }; - const command_line::arg_descriptor arg_p2p_disable_debug_reqs = { "disable-debug-p2p-requests", "Disable p2p debug requests", false, true }; -} + const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; + const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; + const command_line::arg_descriptor arg_p2p_offline_mode = { "offline-mode", "Don't connect to any node and reject any connections", false, true }; + const command_line::arg_descriptor arg_p2p_disable_debug_reqs = { "disable-debug-p2p-requests", "Disable p2p debug requests", false, true }; + const command_line::arg_descriptor arg_p2p_ip_auto_blocking = { "p2p-ip-auto-blocking", "Enable (1) or disable (0) peers auto-blocking by IP <0|1>. Default: 0", 0, false }; + } //----------------------------------------------------------------------------------- template @@ -53,7 +54,8 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_p2p_offline_mode); command_line::add_arg(desc, arg_p2p_disable_debug_reqs); - command_line::add_arg(desc, arg_p2p_use_only_priority_nodes); + command_line::add_arg(desc, arg_p2p_use_only_priority_nodes); + command_line::add_arg(desc, arg_p2p_ip_auto_blocking); } //----------------------------------------------------------------------------------- template @@ -65,7 +67,14 @@ namespace nodetool CHECK_AND_ASSERT_MES(r, false, "Failed to parse P2P_MAINTAINERS_PUB_KEY = " << P2P_MAINTAINERS_PUB_KEY); std::string state_file_path = m_config_folder + "/" + P2P_NET_DATA_FILENAME; - tools::unserialize_obj_from_file(*this, state_file_path); + boost::system::error_code ec = AUTO_VAL_INIT(ec); + std::time_t last_update_time = boost::filesystem::last_write_time(state_file_path, ec); + //let's assume that if p2p peer list file stored more then 2 weeks ago, + //then it outdated and we need to fetch peerlist from seed nodes + if (!ec && time(nullptr) - last_update_time < 86400 * 14) + { + tools::unserialize_obj_from_file(*this, state_file_path); + } //always use new id, to be able differ cloned computers m_config.m_peer_id = crypto::rand(); @@ -100,21 +109,39 @@ namespace nodetool if (m_offline_mode) return false; - //@#@ temporary workaround - return true; -#if 0 + if (!m_ip_auto_blocking_enabled) + return true; + + return !is_ip_in_blacklist(addr); + } + //----------------------------------------------------------------------------------- + template + bool node_server::is_ip_good_for_adding_to_peerlist(uint32_t addr) + { + if (m_offline_mode) + return false; + + // even if IP auto blocking is disabled, bad peers should not be added to peerlists and be shared with other nodes + + return !is_ip_in_blacklist(addr); + } + //----------------------------------------------------------------------------------- + template + bool node_server::is_ip_in_blacklist(uint32_t addr) + { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); auto it = m_blocked_ips.find(addr); - if(it == m_blocked_ips.end()) - return true; - if(time(nullptr) - it->second > P2P_IP_BLOCKTIME ) + if (it == m_blocked_ips.end()) + return false; + + if (time(nullptr) - it->second > P2P_IP_BLOCKTIME) { m_blocked_ips.erase(it); - LOG_PRINT_CYAN("Ip " << string_tools::get_ip_string_from_int32(addr) << "is unblocked.", LOG_LEVEL_0); - return true; + LOG_PRINT_CYAN("IP " << string_tools::get_ip_string_from_int32(addr) << " is unblocked due to blocking expiration.", LOG_LEVEL_0); + return false; } - return false; -#endif + + return true; } //----------------------------------------------------------------------------------- template @@ -122,7 +149,8 @@ namespace nodetool { CRITICAL_REGION_LOCAL(m_blocked_ips_lock); m_blocked_ips[addr] = time(nullptr); - LOG_PRINT_CYAN("Ip " << string_tools::get_ip_string_from_int32(addr) << " blocked.", LOG_LEVEL_0); + m_peerlist.remove_peers_by_ip_from_all(addr); + LOG_PRINT_CYAN("IP " << string_tools::get_ip_string_from_int32(addr) << " blocked and removed from peerlist", LOG_LEVEL_0); return true; } //----------------------------------------------------------------------------------- @@ -138,6 +166,10 @@ namespace nodetool it->second = P2P_IP_FAILS_BEFOR_BLOCK/2; block_ip(address); } + else + { + LOG_PRINT_CYAN("IP " << string_tools::get_ip_string_from_int32(address) << ": fail recorded, total fails count: " << fails, LOG_LEVEL_2); + } return true; } //----------------------------------------------------------------------------------- @@ -162,6 +194,9 @@ namespace nodetool m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); m_offline_mode = command_line::get_arg(vm, arg_p2p_offline_mode); m_debug_requests_enabled = !command_line::get_arg(vm, arg_p2p_disable_debug_reqs); + m_ip_auto_blocking_enabled = (command_line::get_arg(vm, arg_p2p_ip_auto_blocking) != 0); + + LOG_PRINT_L0("p2p peers auto-blocking is " << (m_ip_auto_blocking_enabled ? "enabled" : "disabled")); if (m_offline_mode) { @@ -679,7 +714,6 @@ namespace nodetool << string_tools::get_ip_string_from_int32(na.ip) << ":" << string_tools::num_to_string_fast(na.port) /*<< ", try " << try_count*/); - //m_peerlist.set_peer_unreachable(pe); return false; } peerid_type pi = AUTO_VAL_INIT(pi); @@ -701,12 +735,15 @@ namespace nodetool return true; } - peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); - pe_local.adr = na; - pe_local.id = pi; - time(&pe_local.last_seen); - m_peerlist.append_with_peer_white(pe_local); - //update last seen and push it to peerlist manager + if (is_ip_good_for_adding_to_peerlist(na.ip)) // additional check to avoid IP shown up in peers in the case of non-blocking incoming connections + { + //update last seen and push it to peerlist manager + peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); + pe_local.adr = na; + pe_local.id = pi; + time(&pe_local.last_seen); + m_peerlist.append_with_peer_white(pe_local); + } LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK with peer " << string_tools::get_ip_string_from_int32(na.ip) << ":" << string_tools::num_to_string_fast(na.port), LOG_LEVEL_2); return true; @@ -1373,7 +1410,8 @@ namespace nodetool //associate peer_id with this connection context.peer_id = arg.node_data.peer_id; - if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) + if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port + && is_ip_good_for_adding_to_peerlist(context.m_remote_ip)) { peerid_type peer_id_l = arg.node_data.peer_id; uint32_t port_l = arg.node_data.my_port; diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h index 5cdfa0e8..f133683f 100644 --- a/src/p2p/net_peerlist.h +++ b/src/p2p/net_peerlist.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2019 Zano Project +// Copyright (c) 2014-2021 Zano Project // Copyright (c) 2014-2018 The Louisdor Project // Copyright (c) 2012-2013 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying @@ -11,8 +11,6 @@ #include #include #include -//#include -//#include #include #include #include @@ -55,10 +53,10 @@ namespace nodetool bool append_with_peer_gray(const peerlist_entry& pr); bool set_peer_just_seen(peerid_type peer, uint32_t ip, uint32_t port); bool set_peer_just_seen(peerid_type peer, const net_address& addr); - bool set_peer_unreachable(const peerlist_entry& pr); bool is_ip_allowed(uint32_t ip); void trim_white_peerlist(); void trim_gray_peerlist(); + bool remove_peers_by_ip_from_all(const uint32_t ip); private: @@ -110,17 +108,6 @@ namespace nodetool > > peers_indexed; - typedef boost::multi_index_container< - peerlist_entry, - boost::multi_index::indexed_by< - // access by peerlist_entry::id< - boost::multi_index::ordered_unique, boost::multi_index::member >, - // access by peerlist_entry::net_adress - boost::multi_index::ordered_unique, boost::multi_index::member >, - // sort by peerlist_entry::last_seen< - boost::multi_index::ordered_non_unique, boost::multi_index::member > - > - > peers_indexed_old; public: template @@ -134,9 +121,7 @@ namespace nodetool ar & m_peers_gray; } - private: - bool peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi); - + private: friend class boost::serialization::access; epee::critical_section m_peerlist_lock; std::string m_config_folder; @@ -188,21 +173,6 @@ namespace nodetool return true; } //-------------------------------------------------------------------------------------------------- - inline - bool peerlist_manager::peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi) - { - for(auto x: pio) - { - auto by_addr_it = pi.get().find(x.adr); - if(by_addr_it == pi.get().end()) - { - pi.insert(x); - } - } - - return true; - } - //-------------------------------------------------------------------------------------------------- inline void peerlist_manager::trim_white_peerlist() { CRITICAL_REGION_LOCAL(m_peerlist_lock); @@ -393,6 +363,33 @@ namespace nodetool return true; } //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::remove_peers_by_ip_from_all(const uint32_t ip) + { + TRY_ENTRY(); + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + + for (auto it = m_peers_white.begin(); it != m_peers_white.end();) + { + if (it->adr.ip == ip) + it = m_peers_white.erase(it); + else + ++it; + } + + for (auto it = m_peers_gray.begin(); it != m_peers_gray.end();) + { + if (it->adr.ip == ip) + it = m_peers_gray.erase(it); + else + ++it; + } + + return true; + CATCH_ENTRY_L0("peerlist_manager::remove_peers_by_ip_from_all()", false); + } + //-------------------------------------------------------------------------------------------------- } BOOST_CLASS_VERSION(nodetool::peerlist_manager, CURRENT_PEERLIST_STORAGE_ARCHIVE_VER) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 71fbffc0..cfb48a64 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -228,6 +228,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), "sign_transfer - sign unsigned tx from a watch-only wallet"); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), "submit_transfer - broadcast signed tx"); + m_cmd_binder.set_handler("export_history", boost::bind(&simple_wallet::submit_transfer, this, _1), "Export transaction history in CSV file"); } //---------------------------------------------------------------------------------------------------- simple_wallet::~simple_wallet() @@ -798,12 +799,7 @@ bool simple_wallet::list_recent_transfers(const std::vector& args) std::string wti_to_text_line(const tools::wallet_public::wallet_transfer_info& wti) { stringstream ss; - ss << (wti.is_income ? "[INC]" : "[OUT]") << "\t" - << epee::misc_utils::get_time_str(wti.timestamp) << "\t" - << print_money(wti.amount) << "\t" - << print_money(wti.fee) << "\t" - << wti.remote_addresses << "\t" - << wti.comment << "\t"; + return ss.str(); } //---------------------------------------------------------------------------------------------------- @@ -811,58 +807,37 @@ bool simple_wallet::export_recent_transfers(const std::vector& args { bool export_to_json = true; bool ignore_pos = false; - if (args.size()) - { - if (args[0] == "json") - export_to_json = true; - else if (args[0] == "txt") - export_to_json = false; - } if (args.size() > 1) { if (args[1] == "ignore-pos") ignore_pos = true; } - std::vector unconfirmed; - std::vector recent; - uint64_t total = 0; - uint64_t last_index = 0; - m_wallet->get_recent_transfers_history(recent, 0, 0, total, last_index, false); - m_wallet->get_unconfirmed_transfers(unconfirmed, false); - //workaround for missed fee - stringstream ss; - LOG_PRINT_GREEN("Generating text....", LOG_LEVEL_0); - ss << "Unconfirmed transfers: " << ENDL; - for (auto & wti : unconfirmed) + std::string format = "csv"; + if (args.size() > 0) { - if(ignore_pos && wti.is_mining) - continue; - if (!wti.fee) - wti.fee = currency::get_tx_fee(wti.tx); - if(export_to_json) - ss << epee::serialization::store_t_to_json(wti) << ENDL; + if (args[0] == "json" || args[0] == "csv" || args[0] == "text") + { + format = args[0]; + } else - ss << wti_to_text_line(wti) << ENDL; - + { + fail_msg_writer() << "Unknown format: \"" << args[0] << "\", only \"csv\"(default), \"json\" and \"text\" supported"; + } } - ss << "Recent transfers: " << ENDL; - for (auto & wti : recent) + + try { + boost::filesystem::ofstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(log_space::log_singletone::get_default_log_folder() + "/wallet_recent_transfers.txt", std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + m_wallet->export_transaction_history(fstream, format, !ignore_pos); + fstream.close(); + } + catch (...) { - if (ignore_pos && wti.is_mining) - continue; - if (!wti.fee) - wti.fee = currency::get_tx_fee(wti.tx); - - if (export_to_json) - ss << epee::serialization::store_t_to_json(wti) << ENDL; - else - ss << wti_to_text_line(wti) << ENDL; + success_msg_writer() << "Failed"; + return false; } - LOG_PRINT_GREEN("Storing text to wallet_recent_transfers.txt....", LOG_LEVEL_0); - file_io_utils::save_string_to_file(log_space::log_singletone::get_default_log_folder() + "/wallet_recent_transfers.txt", ss.str()); - LOG_PRINT_GREEN("Done", LOG_LEVEL_0); - return true; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/plain_wallet_api.cpp b/src/wallet/plain_wallet_api.cpp index b2f31850..2e7914fb 100644 --- a/src/wallet/plain_wallet_api.cpp +++ b/src/wallet/plain_wallet_api.cpp @@ -201,7 +201,8 @@ namespace plain_wallet args[1] = const_cast(argss_1.c_str()); args[2] = const_cast(argss_2.c_str()); args[3] = nullptr; - if (!(ptr->gwm.init_command_line(3, args) && ptr->gwm.init(nullptr))) + std::string command_line_fail_details; + if (!(ptr->gwm.init_command_line(3, args, command_line_fail_details) && ptr->gwm.init(nullptr))) { LOG_ERROR("Failed to init wallets_manager"); return GENERAL_INTERNAL_ERRROR_INIT; diff --git a/src/wallet/view_iface.h b/src/wallet/view_iface.h index 1dc31f2c..8c5e52c4 100644 --- a/src/wallet/view_iface.h +++ b/src/wallet/view_iface.h @@ -284,6 +284,20 @@ public: END_KV_SERIALIZE_MAP() }; + struct export_wallet_info + { + uint64_t wallet_id; + bool include_pos_transactions; + std::string path; + std::string format; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(wallet_id) + KV_SERIALIZE(include_pos_transactions) + KV_SERIALIZE(path) + KV_SERIALIZE(format) + END_KV_SERIALIZE_MAP() + }; struct response_mining_estimate { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 546ccff1..5c9028f2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3254,6 +3254,87 @@ void wallet2::get_recent_transfers_history(std::vector(ss, " ")); + ss << "]" << ","; + ss << wti.tx_hash << ","; + ss << wti.height << ","; + ss << wti.unlock_time << ","; + ss << wti.tx_blob_size << ","; + ss << epee::string_tools::buff_to_hex_nodelimer(wti.payment_id) << ","; + ss << "["; + std::copy(wti.recipients_aliases.begin(), wti.recipients_aliases.end(), std::ostream_iterator(ss, " ")); + ss << "]" << ","; + ss << (wti.is_income ? "in" : "out") << ","; + ss << (wti.is_service ? "[SERVICE]" : "") << (wti.is_mixing ? "[MIXINS]" : "") << (wti.is_mining ? "[MINING]" : "") << ","; + ss << wti.tx_type << ","; + ss << wti.fee << ENDL; +}; + +void wallet2::wti_to_txt_line(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index) +{ + ss << (wti.is_income ? "[INC]" : "[OUT]") << "\t" + << epee::misc_utils::get_time_str(wti.timestamp) << "\t" + << print_money(wti.amount) << "\t" + << print_money(wti.fee) << "\t" + << wti.remote_addresses << "\t" + << wti.comment << ENDL; +}; + +void wallet2::wti_to_json_line(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index) +{ + ss << epee::serialization::store_t_to_json(wti, 4) << ","; +}; + + +//---------------------------------------------------------------------------------------------------- +void wallet2::export_transaction_history(std::ostream& ss, const std::string& format, bool include_pos_transactions) +{ + //typedef int(*t_somefunc)(int, int); + typedef void(*playout_cb_type)(std::ostream&, const wallet_public::wallet_transfer_info&, size_t); + playout_cb_type cb_csv = &wallet2::wti_to_csv_entry; + playout_cb_type cb_json = &wallet2::wti_to_json_line; + playout_cb_type cb_plain_text = &wallet2::wti_to_txt_line; + + playout_cb_type cb = cb_csv; + if (format == "json") + { + ss << "{ \"history\": ["; + cb = cb_json; + } + else if (format == "text") + { + cb = cb_plain_text; + } + else + { + //csv by default + ss << "N, Date, Amount, Comment, Address, ID, Height, Unlock timestamp, Tx size, Alias, In/Out, Flags, Type, Fee" << ENDL; + } + + + enum_container(m_transfer_history.begin(), m_transfer_history.end(), [&](wallet_public::wallet_transfer_info& wti, size_t index) { + if (!include_pos_transactions) + { + if (currency::is_coinbase(wti.tx)) + return true; + } + cb(ss, wti, index); + return true; + }); + + if (format == "json") + { + ss << "{}]}"; + } + } //---------------------------------------------------------------------------------------------------- bool wallet2::get_transfer_address(const std::string& adr_str, currency::account_public_address& addr, std::string& payment_id) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 70a674a1..fdf2afad 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -861,6 +861,8 @@ namespace tools uint64_t get_wallet_file_size()const; void set_use_deffered_global_outputs(bool use); construct_tx_param get_default_construct_tx_param_inital(); + + void export_transaction_history(std::ostream& ss, const std::string& format, bool include_pos_transactions = true); /* create_htlc_proposal: if htlc_hash == null_hash, then this wallet is originator of the atomic process, and @@ -872,7 +874,6 @@ namespace tools void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin, currency::transaction& result_tx); void redeem_htlc(const crypto::hash& htlc_tx_id, const std::string& origin); bool check_htlc_redeemed(const crypto::hash& htlc_tx_id, std::string& origin, crypto::hash& redeem_tx_id); - private: void add_transfers_to_expiration_list(const std::vector& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id); @@ -1006,6 +1007,10 @@ private: void push_alias_info_to_extra_according_to_hf_status(const currency::extra_alias_entry& ai, std::vector& extra); void remove_transfer_from_amount_gindex_map(uint64_t tid); + static void wti_to_csv_entry(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index); + static void wti_to_txt_line(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index); + static void wti_to_json_line(std::ostream& ss, const wallet_public::wallet_transfer_info& wti, size_t index); + currency::account_base m_account; bool m_watch_only; std::string m_log_prefix; // part of pub address, prefix for logging functions diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e762ebbf..ca888e83 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -56,7 +56,11 @@ namespace tools command_line::add_arg(desc, arg_deaf_mode); } //------------------------------------------------------------------------------------------------------------------------------ - wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w), m_do_mint(false), m_deaf(false) + wallet_rpc_server::wallet_rpc_server(wallet2& w) + : m_wallet(w) + , m_do_mint(false) + , m_deaf(false) + , m_last_wallet_store_height(0) {} //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::run(bool do_mint, bool offline_mode, const currency::account_public_address& miner_address) @@ -309,7 +313,7 @@ namespace tools else { //put it to attachments - ctp.attachments.insert(ctp.extra.end(), req.service_entries.begin(), req.service_entries.end()); + ctp.attachments.insert(ctp.attachments.end(), req.service_entries.begin(), req.service_entries.end()); } bool wrap = false; std::vector& dsts = ctp.dsts; @@ -397,7 +401,7 @@ namespace tools currency::finalized_tx result = AUTO_VAL_INIT(result); std::string unsigned_tx_blob_str; ctp.fee = req.fee; - ctp.fake_outputs_count = 0; + ctp.fake_outputs_count = req.mixin; m_wallet.transfer(ctp, result, true, &unsigned_tx_blob_str); if (m_wallet.is_watch_only()) { diff --git a/src/wallet/wallets_manager.cpp b/src/wallet/wallets_manager.cpp index 6155b4b0..2beb7c2f 100644 --- a/src/wallet/wallets_manager.cpp +++ b/src/wallet/wallets_manager.cpp @@ -152,7 +152,7 @@ bool wallets_manager::do_exception_safe_call(guarded_code_t guarded_code, error_ } -bool wallets_manager::init_command_line(int argc, char* argv[]) +bool wallets_manager::init_command_line(int argc, char* argv[], std::string& fail_message) { TRY_ENTRY(); po::options_description desc_cmd_only("Command line options"); @@ -197,9 +197,8 @@ bool wallets_manager::init_command_line(int argc, char* argv[]) po::options_description desc_options("Allowed options"); desc_options.add(desc_cmd_only).add(desc_cmd_sett); - - - bool coomand_line_parsed = command_line::handle_error_helper(desc_options, [&]() + std::string err_str; + bool command_line_parsed = command_line::handle_error_helper(desc_options, err_str, [&]() { po::store(po::parse_command_line(argc, argv, desc_options), m_vm); @@ -230,23 +229,29 @@ bool wallets_manager::init_command_line(int argc, char* argv[]) return true; }); - if (!coomand_line_parsed) + if (!command_line_parsed) { std::stringstream ss; ss << "Command line has wrong arguments: " << std::endl; for (int i = 0; i != argc; i++) ss << "[" << i << "] " << argv[i] << std::endl; std::cerr << ss.str() << std::endl << std::flush; + + fail_message = "Error parsing arguments.\n"; + fail_message += err_str + "\n"; + std::stringstream s; + desc_options.print(s); + fail_message += s.str(); return false; } m_qt_logs_enbaled = command_line::get_arg(m_vm, arg_enable_qt_logs); m_qt_dev_tools = command_line::get_arg(m_vm, arg_qt_dev_tools); - return true; CATCH_ENTRY2(false); } + void terminate_handler_func() { LOG_ERROR("\n\nTERMINATE HANDLER\n"); // should print callstack @@ -798,6 +803,24 @@ std::string wallets_manager::get_tx_pool_info(currency::COMMAND_RPC_GET_POOL_INF } +std::string wallets_manager::export_wallet_history(const view::export_wallet_info& ewi) +{ + GET_WALLET_OPT_BY_ID(ewi.wallet_id, wo); + try { + + boost::filesystem::ofstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(ewi.path, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + wo.w->get()->export_transaction_history(fstream, ewi.format, ewi.include_pos_transactions); + fstream.close(); + } + catch (...) + { + return API_RETURN_CODE_FAIL; + } + return API_RETURN_CODE_OK; +} + uint64_t wallets_manager::get_default_fee() { return TX_DEFAULT_FEE; @@ -1802,7 +1825,10 @@ void wallets_manager::on_transfer2(size_t wallet_id, const tools::wallet_public: GET_WALLET_OPTIONS_BY_ID_VOID_RET(wallet_id, w); tei.is_wallet_in_sync_process = w.long_refresh_in_progress; - m_pview->money_transfer(tei); + if (!(w.w->get()->is_watch_only())) + { + m_pview->money_transfer(tei); + } } void wallets_manager::on_pos_block_found(size_t wallet_id, const currency::block& b) { diff --git a/src/wallet/wallets_manager.h b/src/wallet/wallets_manager.h index 151680d1..10d6a103 100644 --- a/src/wallet/wallets_manager.h +++ b/src/wallet/wallets_manager.h @@ -89,7 +89,7 @@ public: wallets_manager(); ~wallets_manager(); - bool init_command_line(int argc, char* argv[]); + bool init_command_line(int argc, char* argv[], std::string& fail_message); bool init(view::i_view* pview_handler); bool start(); bool stop(); @@ -139,6 +139,7 @@ public: std::string get_my_offers(const bc_services::core_offers_filter& filter, std::list& offers); std::string get_fav_offers(const std::list& hashes, const bc_services::core_offers_filter& filter, std::list& offers); std::string get_tx_pool_info(currency::COMMAND_RPC_GET_POOL_INFO::response& res); + std::string export_wallet_history(const view::export_wallet_info& ewi); uint64_t get_default_fee(); std::string get_mining_estimate(uint64_t amuont_coins, uint64_t time, diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index b15bdacc..01aa5f8b 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -2053,6 +2053,25 @@ bool check_ring_signature_at_gen_time(const std::vector& event return true; } +bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, currency::core& c) +{ + std::shared_ptr ptce = c.get_blockchain_storage().get_tx_chain_entry(tx_id); + if (!ptce) + return false; + + for (size_t i = 0; i < ptce->tx.vin.size(); ++i) + { + auto& input = ptce->tx.vin[i]; + if (input.type() == typeid(txin_to_key)) + { + auto& intk = boost::get(input); + CHECK_AND_ASSERT_MES(intk.key_offsets.size() == mixin + 1, false, "for input #" << i << " mixin count is " << intk.key_offsets.size() - 1 << ", expected is " << mixin); + } + } + + return true; +} + //------------------------------------------------------------------------------ void test_chain_unit_base::register_callback(const std::string& cb_name, verify_callback cb) diff --git a/tests/core_tests/chaingen.h b/tests/core_tests/chaingen.h index 6b9b4c04..7cbf4822 100644 --- a/tests/core_tests/chaingen.h +++ b/tests/core_tests/chaingen.h @@ -679,6 +679,7 @@ bool generate_pos_block_with_given_coinstake(test_generator& generator, const st bool check_ring_signature_at_gen_time(const std::vector& events, const crypto::hash& last_block_id, const currency::txin_to_key& in_t_k, const crypto::hash& hash_for_sig, const std::vector &sig); +bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, currency::core& c); //-------------------------------------------------------------------------- template diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 6cd4425d..b50044b6 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -867,6 +867,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(wallet_rpc_integrated_address); GENERATE_AND_PLAY(wallet_rpc_integrated_address_transfer); + GENERATE_AND_PLAY(wallet_rpc_transfer); GENERATE_AND_PLAY(wallet_chain_switch_with_spending_the_same_ki); GENERATE_AND_PLAY(wallet_sending_to_integrated_address); diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index 12bb2f8a..626dfae2 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -148,6 +148,7 @@ bool wallet_rpc_integrated_address_transfer::c1(currency::core& c, size_t ev_ind alice_wlt->get_payments(payment_id, payments); CHECK_AND_ASSERT_MES(payments.size() == 1, false, "Invalid payments count: " << payments.size()); CHECK_AND_ASSERT_MES(payments.front().m_amount == MK_TEST_COINS(3), false, "Invalid payment"); + CHECK_AND_ASSERT_MES(check_mixin_value_for_each_input(0, payments.front().m_tx_hash, c), false, ""); // make sure number of decoys is correct // 3. standard address + invalid external payment id => fail @@ -181,9 +182,127 @@ bool wallet_rpc_integrated_address_transfer::c1(currency::core& c, size_t ev_ind alice_wlt->get_payments(payment_id, payments); CHECK_AND_ASSERT_MES(payments.size() == 1, false, "Invalid payments count: " << payments.size()); CHECK_AND_ASSERT_MES(payments.front().m_amount == MK_TEST_COINS(7), false, "Invalid payment"); + CHECK_AND_ASSERT_MES(check_mixin_value_for_each_input(0, payments.front().m_tx_hash, c), false, ""); // make sure number of decoys is correct return true; } //------------------------------------------------------------------------------ + +wallet_rpc_transfer::wallet_rpc_transfer() +{ + REGISTER_CALLBACK_METHOD(wallet_rpc_transfer, configure_core); + REGISTER_CALLBACK_METHOD(wallet_rpc_transfer, c1); +} + +bool wallet_rpc_transfer::generate(std::vector& events) const +{ + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); + DO_CALLBACK(events, "configure_core"); + set_hard_fork_heights_to_generator(generator); + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 6); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool wallet_rpc_transfer::configure_core(currency::core& c, size_t ev_index, const std::vector& events) +{ + currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config(); + pc.hard_fork_01_starts_after_height = 1; + pc.hard_fork_02_starts_after_height = 1; + pc.hard_fork_03_starts_after_height = 1; + c.get_blockchain_storage().set_core_runtime_config(pc); + return true; +} + +bool wallet_rpc_transfer::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + bool r = false; + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX); + + miner_wlt->refresh(); + + // wallet RPC server + tools::wallet_rpc_server miner_wlt_rpc(*miner_wlt); + epee::json_rpc::error je; + tools::wallet_rpc_server::connection_context ctx; + + // 1. Check non-zero mixin and default settings + tools::wallet_public::COMMAND_RPC_TRANSFER::request req = AUTO_VAL_INIT(req); + req.fee = TESTS_DEFAULT_FEE; + req.mixin = 2; + tools::wallet_public::transfer_destination tds = AUTO_VAL_INIT(tds); + tds.address = m_accounts[ALICE_ACC_IDX].get_public_address_str(); + tds.amount = MK_TEST_COINS(3); + req.destinations.push_back(tds); + + tools::wallet_public::COMMAND_RPC_TRANSFER::response res = AUTO_VAL_INIT(res); + + r = miner_wlt_rpc.on_transfer(req, res, je, ctx); + CHECK_AND_ASSERT_MES(r, false, "RPC call failed, code: " << je.code << ", msg: " << je.message); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "enexpected pool txs count: " << c.get_pool_transactions_count()); + + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Tx pool is not empty: " << c.get_pool_transactions_count()); + + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, tds.amount), false, ""); + + // check the transfer has been received + tools::wallet2::transfer_details td = AUTO_VAL_INIT(td); + CHECK_AND_ASSERT_MES(alice_wlt->get_transfer_info_by_index(0, td), false, ""); + CHECK_AND_ASSERT_MES(td.amount() == MK_TEST_COINS(3), false, "Invalid payment"); + CHECK_AND_ASSERT_MES(check_mixin_value_for_each_input(2, td.tx_hash(), c), false, ""); + + // make sure tx_received is set by default, but tx_payer is not + std::shared_ptr pche = c.get_blockchain_storage().get_tx_chain_entry(td.tx_hash()); + CHECK_AND_ASSERT_MES(currency::count_type_in_variant_container(pche->tx.extra) == 1, false, "tx_receiver: incorrect count of items"); + CHECK_AND_ASSERT_MES(currency::count_type_in_variant_container(pche->tx.extra) == 0, false, "tx_payer: incorrect count of items"); + + + // 2. check tx_receiver and tx_payer non-default + req.mixin = 1; + req.hide_receiver = true; + req.push_payer = true; + tds.amount = MK_TEST_COINS(5); + req.destinations.clear(); + req.destinations.push_back(tds); + + res = AUTO_VAL_INIT(res); + + r = miner_wlt_rpc.on_transfer(req, res, je, ctx); + CHECK_AND_ASSERT_MES(r, false, "RPC call failed, code: " << je.code << ", msg: " << je.message); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "enexpected pool txs count: " << c.get_pool_transactions_count()); + + r = mine_next_pow_block_in_playtime(m_accounts[MINER_ACC_IDX].get_public_address(), c); + CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Tx pool is not empty: " << c.get_pool_transactions_count()); + + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(3 + 5)), false, ""); + + td = AUTO_VAL_INIT(td); + CHECK_AND_ASSERT_MES(alice_wlt->get_transfer_info_by_index(1, td), false, ""); + CHECK_AND_ASSERT_MES(td.amount() == MK_TEST_COINS(5), false, "Invalid payment"); + CHECK_AND_ASSERT_MES(check_mixin_value_for_each_input(1, td.tx_hash(), c), false, ""); + + // make sure tx_received is set by default, but tx_payer is not + pche = c.get_blockchain_storage().get_tx_chain_entry(td.tx_hash()); + CHECK_AND_ASSERT_MES(currency::count_type_in_variant_container(pche->tx.extra) == 0, false, "tx_receiver: incorrect count of items"); + CHECK_AND_ASSERT_MES(currency::count_type_in_variant_container(pche->tx.extra) == 1, false, "tx_payer: incorrect count of items"); + + + return true; +} diff --git a/tests/core_tests/wallet_rpc_tests.h b/tests/core_tests/wallet_rpc_tests.h index 9d6674f5..1c0ea50c 100644 --- a/tests/core_tests/wallet_rpc_tests.h +++ b/tests/core_tests/wallet_rpc_tests.h @@ -21,3 +21,11 @@ struct wallet_rpc_integrated_address_transfer : public wallet_test bool generate(std::vector& events) const; bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; + +struct wallet_rpc_transfer : public wallet_test +{ + wallet_rpc_transfer(); + bool generate(std::vector& events) const; + bool configure_core(currency::core& c, size_t ev_index, const std::vector& events); + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; diff --git a/utils/Zano.sh b/utils/Zano.sh index d097abbc..e0eac8cc 100755 --- a/utils/Zano.sh +++ b/utils/Zano.sh @@ -12,7 +12,7 @@ out_file_name=~/.local/share/applications/Zano.desktop call_app() { pushd $script_dir - ./Zano + ./Zano "$@" popd exit } @@ -25,17 +25,20 @@ create_desktop_icon() rm -f $target_file_name echo [Desktop Entry] | tee -a $target_file_name > /dev/null echo Version=1.0 | tee -a $target_file_name > /dev/null - echo Name=My Application | tee -a $target_file_name > /dev/null - echo GenericName=My Application | tee -a $target_file_name > /dev/null - echo Comment=Doing some funny stuff | tee -a $target_file_name > /dev/null + echo Name=Zano | tee -a $target_file_name > /dev/null + echo GenericName=Zano | tee -a $target_file_name > /dev/null + echo Comment=Privacy blockchain | tee -a $target_file_name > /dev/null echo Icon=$script_dir/html/files/desktop_linux_icon.png | tee -a $target_file_name > /dev/null - echo Exec=$script_dir/Zano | tee -a $target_file_name > /dev/null + echo Exec=$script_dir/Zano.sh %u | tee -a $target_file_name > /dev/null echo Terminal=true | tee -a $target_file_name > /dev/null echo Type=Application | tee -a $target_file_name > /dev/null echo "Categories=Qt;Utility;" | tee -a $target_file_name > /dev/null + echo "MimeType=x-scheme-handler/zano;" | tee -a $target_file_name > /dev/null } create_desktop_icon $out_file_name -call_app +xdg-mime default Zano.desktop x-scheme-handler/zano + +call_app "$@" \ No newline at end of file diff --git a/utils/build_script_linux.sh b/utils/build_script_linux.sh index 9ffb1d54..2cd431a2 100755 --- a/utils/build_script_linux.sh +++ b/utils/build_script_linux.sh @@ -46,25 +46,18 @@ if [ $? -ne 0 ]; then exit 1 fi -make -j1 daemon Zano; +make -j2 daemon simplewallet connectivity_tool if [ $? -ne 0 ]; then echo "Failed to make!" exit 1 fi -make -j1 simplewallet; +make -j1 Zano if [ $? -ne 0 ]; then echo "Failed to make!" exit 1 fi -make -j1 connectivity_tool; -if [ $? -ne 0 ]; then - echo "Failed to make!" - exit 1 -fi - - read version_str <<< $(./src/zanod --version | awk '/^Zano/ { print $2 }') version_str=${version_str} diff --git a/utils/setup_32.iss b/utils/setup_32.iss index e57a6838..24c18c80 100644 --- a/utils/setup_32.iss +++ b/utils/setup_32.iss @@ -51,6 +51,8 @@ Root: HKCR; Subkey: "ZanoWalletDataKyesFile"; ValueType: string; ValueName: ""; Root: HKCR; Subkey: "ZanoWalletDataFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\zano.exe,0" Root: HKCR; Subkey: "ZanoWalletDataKyesFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\zano.exe,0" +Root: HKCR; Subkey: "Zano"; ValueType: string; ValueName: "URL Protocol"; ValueData: "" +Root: HKCR; Subkey: "Zano\shell\open\command"; ValueType: string; ValueName: ""; ValueData: "{app}\zano.exe %1" [Files] diff --git a/utils/setup_64.iss b/utils/setup_64.iss index e4a7e88a..adee27a8 100644 --- a/utils/setup_64.iss +++ b/utils/setup_64.iss @@ -52,6 +52,9 @@ Root: HKCR; Subkey: "ZanoWalletDataKyesFile"; ValueType: string; ValueName: ""; Root: HKCR; Subkey: "ZanoWalletDataFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\Zano.exe,0" Root: HKCR; Subkey: "ZanoWalletDataKyesFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\Zano.exe,0" +Root: HKCR; Subkey: "Zano"; ValueType: string; ValueName: "URL Protocol"; ValueData: "" +Root: HKCR; Subkey: "Zano\shell\open\command"; ValueType: string; ValueName: ""; ValueData: "{app}\Zano.exe %1" + [Files]