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/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 54813472..6bb47719 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -578,13 +578,13 @@ bool blockchain_storage::set_checkpoints(checkpoints&& chk_pts) catch (const std::exception& ex) { m_db.abort_transaction(); - LOG_ERROR("UNKNOWN EXCEPTION WHILE ADDINIG NEW BLOCK: " << ex.what()); + LOG_ERROR("UNKNOWN EXCEPTION WHILE SETTING CHECKPOINTS: " << ex.what()); return false; } catch (...) { m_db.abort_transaction(); - LOG_ERROR("UNKNOWN EXCEPTION WHILE ADDINIG NEW BLOCK."); + LOG_ERROR("UNKNOWN EXCEPTION WHILE SETTING CHECKPOINTS."); return false; } @@ -594,7 +594,7 @@ bool blockchain_storage::prune_ring_signatures_and_attachments(uint64_t height, { CRITICAL_REGION_LOCAL(m_read_lock); - CHECK_AND_ASSERT_MES(height < m_db_blocks.size(), false, "prune_ring_signatures called with wrong parameter: " << height << ", m_blocks.size() " << m_db_blocks.size()); + CHECK_AND_ASSERT_MES(height < m_db_blocks.size(), false, "prune_ring_signatures called with wrong parameter: " << height << ", m_blocks.size() = " << m_db_blocks.size()); auto vptr = m_db_blocks[height]; CHECK_AND_ASSERT_MES(vptr.get(), false, "Failed to get block on height"); @@ -626,22 +626,20 @@ bool blockchain_storage::prune_ring_signatures_and_attachments_if_need() { CRITICAL_REGION_LOCAL(m_read_lock); - if (m_db_blocks.size() > 1 && m_checkpoints.get_top_checkpoint_height() && m_checkpoints.get_top_checkpoint_height() > m_db_current_pruned_rs_height) - { - uint64_t pruning_last_height = std::min(m_db_blocks.size() - 1, m_checkpoints.get_top_checkpoint_height()); - if (pruning_last_height > m_db_current_pruned_rs_height) + uint64_t top_block_height = get_top_block_height(); + uint64_t pruning_end_height = m_checkpoints.get_checkpoint_before_height(top_block_height); + if (pruning_end_height > m_db_current_pruned_rs_height) + { + LOG_PRINT_CYAN("Starting pruning ring signatues and attachments from height " << m_db_current_pruned_rs_height + 1 << " to height " << pruning_end_height + << " (" << pruning_end_height - m_db_current_pruned_rs_height << " blocks), top block height is " << top_block_height, LOG_LEVEL_0); + uint64_t tx_count = 0, sig_count = 0, attach_count = 0; + for(uint64_t height = m_db_current_pruned_rs_height + 1; height <= pruning_end_height; height++) { - LOG_PRINT_CYAN("Starting pruning ring signatues and attachments from height " << m_db_current_pruned_rs_height + 1 << " to height " << pruning_last_height - << " (" << pruning_last_height - m_db_current_pruned_rs_height << " blocks)", LOG_LEVEL_0); - uint64_t tx_count = 0, sig_count = 0, attach_count = 0; - for(uint64_t height = m_db_current_pruned_rs_height + 1; height <= pruning_last_height; height++) - { - bool res = prune_ring_signatures_and_attachments(height, tx_count, sig_count, attach_count); - CHECK_AND_ASSERT_MES(res, false, "failed to prune_ring_signatures_and_attachments for height = " << height); - } - m_db_current_pruned_rs_height = pruning_last_height; - LOG_PRINT_CYAN("Transaction pruning finished: " << sig_count << " signatures and " << attach_count << " attachments released in " << tx_count << " transactions.", LOG_LEVEL_0); + bool res = prune_ring_signatures_and_attachments(height, tx_count, sig_count, attach_count); + CHECK_AND_ASSERT_MES(res, false, "failed to prune_ring_signatures_and_attachments for height = " << height); } + m_db_current_pruned_rs_height = pruning_end_height; + LOG_PRINT_CYAN("Transaction pruning finished: " << sig_count << " signatures and " << attach_count << " attachments released in " << tx_count << " transactions.", LOG_LEVEL_0); } return true; } @@ -1036,7 +1034,9 @@ void blockchain_storage::purge_alt_block_txs_hashs(const block& b) //------------------------------------------------------------------ void blockchain_storage::do_erase_altblock(alt_chain_container::iterator it) { - purge_altblock_keyimages_from_big_heap(it->second.bl, get_block_hash(it->second.bl)); + crypto::hash id = get_block_hash(it->second.bl); + LOG_PRINT_L1("erasing alt block " << print16(id) << " @ " << get_block_height(it->second.bl)); + purge_altblock_keyimages_from_big_heap(it->second.bl, id); purge_alt_block_txs_hashs(it->second.bl); m_alternative_chains.erase(it); } diff --git a/src/currency_core/blockchain_storage.h b/src/currency_core/blockchain_storage.h index 7cb02c41..cad2ca91 100644 --- a/src/currency_core/blockchain_storage.h +++ b/src/currency_core/blockchain_storage.h @@ -396,7 +396,7 @@ namespace currency else { CHECK_AND_ASSERT_MES(*block_ind_ptr < m_db_blocks.size(), false, "Internal error: bl_id=" << string_tools::pod_to_hex(bl_id) - << " have index record with offset=" << *block_ind_ptr << ", bigger then m_blocks.size()=" << m_db_blocks.size()); + << " have index record with offset=" << *block_ind_ptr << ", bigger then m_db_blocks.size()=" << m_db_blocks.size()); blocks.push_back(m_db_blocks[*block_ind_ptr]->bl); } } diff --git a/src/currency_core/checkpoints.cpp b/src/currency_core/checkpoints.cpp index 2d997691..bbce9815 100644 --- a/src/currency_core/checkpoints.cpp +++ b/src/currency_core/checkpoints.cpp @@ -36,11 +36,11 @@ namespace currency if(height > blockchain_last_block_height) return false; - auto it = m_points.lower_bound(height); + auto it = m_points.lower_bound(height); // if found, it->first >= height if(it == m_points.end()) return false; if(it->first <= blockchain_last_block_height) - return true; + return true; // this is the case only if height <= it->first <= blockchain_last_block_height else return false; } @@ -68,4 +68,27 @@ namespace currency return false; } } + //--------------------------------------------------------------------------- + uint64_t checkpoints::get_checkpoint_before_height(uint64_t height) const + { + // returns height of the leftmost CP with height that is LESS than the given height + // ex: + // If there are two CP at 11 and 15: + // get_checkpoint_before_height(10) = 0 + // get_checkpoint_before_height(11) = 0 + // get_checkpoint_before_height(12) = 11 + // get_checkpoint_before_height(13) = 11 + // get_checkpoint_before_height(14) = 11 + // get_checkpoint_before_height(15) = 11 + // get_checkpoint_before_height(16) = 15 + + uint64_t top_cp = get_top_checkpoint_height(); + if (height > top_cp) + return top_cp; + + auto it = m_points.lower_bound(height); // if found, it->first >= height + if (it == m_points.end() || it == m_points.begin()) + return 0; + return (--it)->first; + } } diff --git a/src/currency_core/checkpoints.h b/src/currency_core/checkpoints.h index a845e6eb..76675e67 100644 --- a/src/currency_core/checkpoints.h +++ b/src/currency_core/checkpoints.h @@ -20,6 +20,8 @@ namespace currency bool is_height_passed_zone(uint64_t height, uint64_t blockchain_last_block_height) const; bool check_block(uint64_t height, const crypto::hash& h) const; uint64_t get_top_checkpoint_height() const; + + uint64_t get_checkpoint_before_height(uint64_t height) const; private: std::map m_points; }; 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..083c2afd 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); @@ -548,15 +548,29 @@ void MainWindow::restore_pos(bool consider_showed) } else { - - QPoint pos; - QSize sz; - pos.setX(m_config.m_window_position.first); - pos.setY(m_config.m_window_position.second); - sz.setHeight(m_config.m_window_size.first); - sz.setWidth(m_config.m_window_size.second); - this->move(pos); - this->resize(sz); + QPoint point = QApplication::desktop()->screenGeometry().bottomRight(); + if (m_config.m_window_position.first + m_config.m_window_size.second > point.x() || + m_config.m_window_position.second + m_config.m_window_size.first > point.y() + ) + { + QSize sz = AUTO_VAL_INIT(sz); + sz.setHeight(770); + sz.setWidth(1200); + this->resize(sz); + store_window_pos(); + //reset position(screen changed or other reason) + } + else + { + QPoint pos = AUTO_VAL_INIT(pos); + QSize sz = AUTO_VAL_INIT(sz); + pos.setX(m_config.m_window_position.first); + pos.setY(m_config.m_window_position.second); + sz.setHeight(m_config.m_window_size.first); + sz.setWidth(m_config.m_window_size.second); + this->move(pos); + this->resize(sz); + } } if (consider_showed) @@ -643,14 +657,16 @@ bool MainWindow::show_inital() restore_pos(true); else { + m_config = AUTO_VAL_INIT(m_config); this->show(); - QSize sz; + QSize sz = AUTO_VAL_INIT(sz); sz.setHeight(770); sz.setWidth(1200); this->resize(sz); 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 +743,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 +839,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 +879,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 +901,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 +918,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..93802cb8 160000 --- a/src/gui/qt-daemon/layout +++ b/src/gui/qt-daemon/layout @@ -1 +1 @@ -Subproject commit b028252d30a32e80c12fa890c306d3c5842e937a +Subproject commit 93802cb8fe1c79daf320aa4b72ba454d27da31d7 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/version.h.in b/src/version.h.in index 2f4959ae..04925058 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -4,10 +4,10 @@ #define BUILD_COMMIT_ID "@VERSION@" #define PROJECT_MAJOR_VERSION "1" -#define PROJECT_MINOR_VERSION "3" -#define PROJECT_REVISION "2" +#define PROJECT_MINOR_VERSION "4" +#define PROJECT_REVISION "0" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 135 +#define PROJECT_VERSION_BUILD_NO 138 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]" 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..3e9cbc80 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -57,7 +57,7 @@ bool clean_data_directory() { std::string config_folder = command_line::get_arg(g_vm, command_line::arg_data_dir); - static const std::set files = { CURRENCY_POOLDATA_FOLDERNAME_OLD, CURRENCY_BLOCKCHAINDATA_FOLDERNAME_OLD, P2P_NET_DATA_FILENAME, MINER_CONFIG_FILENAME, GUI_SECURE_CONFIG_FILENAME, GUI_CONFIG_FILENAME, GUI_INTERNAL_CONFIG }; + static const std::set files = { CURRENCY_POOLDATA_FOLDERNAME_OLD, CURRENCY_BLOCKCHAINDATA_FOLDERNAME_OLD, P2P_NET_DATA_FILENAME, MINER_CONFIG_FILENAME, GUI_SECURE_CONFIG_FILENAME, GUI_CONFIG_FILENAME, GUI_INTERNAL_CONFIG2 }; static const std::set prefixes = { CURRENCY_POOLDATA_FOLDERNAME_PREFIX, CURRENCY_BLOCKCHAINDATA_FOLDERNAME_PREFIX }; std::vector entries_to_remove; @@ -743,6 +743,9 @@ int main(int argc, char* argv[]) #undef MARK_TEST_AS_POSTPONED + + // TODO // GENERATE_AND_PLAY(wallet_spend_form_auditable_and_track); + GENERATE_AND_PLAY(pos_minting_tx_packing); GENERATE_AND_PLAY(multisig_wallet_test); @@ -814,6 +817,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_checkpoints_reorganize); GENERATE_AND_PLAY(gen_checkpoints_pos_validation_on_altchain); GENERATE_AND_PLAY(gen_checkpoints_and_invalid_tx_to_pool); + GENERATE_AND_PLAY(gen_checkpoints_set_after_switching_to_altchain); GENERATE_AND_PLAY(gen_no_attchments_in_coinbase); GENERATE_AND_PLAY(gen_no_attchments_in_coinbase_gentime); @@ -867,6 +871,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/checkpoints_tests.cpp b/tests/core_tests/checkpoints_tests.cpp index da68a5fd..254a05e6 100644 --- a/tests/core_tests/checkpoints_tests.cpp +++ b/tests/core_tests/checkpoints_tests.cpp @@ -36,13 +36,18 @@ bool checkpoints_test::set_checkpoint(currency::core& c, size_t ev_index, const { if (pcp.hash != null_hash && pcp.hash != get_block_hash(b)) continue; - currency::checkpoints cp; - cp.add_checkpoint(currency::get_block_height(b), epee::string_tools::pod_to_hex(currency::get_block_hash(b))); - c.set_checkpoints(std::move(cp)); + m_local_checkpoints.add_checkpoint(pcp.height, epee::string_tools::pod_to_hex(currency::get_block_hash(b))); + c.set_checkpoints(currency::checkpoints(m_local_checkpoints)); + LOG_PRINT_YELLOW("CHECKPOINT set at height " << pcp.height, LOG_LEVEL_0); + + //for(uint64_t h = 0; h <= pcp.height + 1; ++h) + // LOG_PRINT_MAGENTA("%% " << h << " : " << m_local_checkpoints.get_checkpoint_before_height(h), LOG_LEVEL_0); return true; } } + LOG_ERROR("set_checkpoint failed trying to set checkpoint at height " << pcp.height); + return false; } @@ -395,8 +400,13 @@ bool gen_checkpoints_prun_txs_after_blockchain_load::generate(std::vector attach; + attach.push_back(tx_comment{"jokes are funny"}); + + // tx pool won't accept the tx, because it cannot be verified in CP zone + // set kept_by_block flag, so tx_0 be accepted + events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, true)); + MAKE_TX_ATTACH(events, tx_0, miner_acc, alice, MK_TEST_COINS(1), blk_0r, attach); events.push_back(event_visitor_settings(event_visitor_settings::set_txs_kept_by_block, false)); MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0); @@ -407,11 +417,12 @@ bool gen_checkpoints_prun_txs_after_blockchain_load::generate(std::vector& events) const +{ + // Test outline: + // 0) no checkpoints are set; + // 1) core is in a subchain, that will become alternative; + // 2) checkpoint is set (in the furute), transaction pruning is executed; + // 3) core continues to sync, chain switching occurs + // Make sure that chain switching is still possible after pruning. + + // 0 ... N N+1 N+2 N+3 N+4 N+5 N+6 <- height (N = CURRENCY_MINED_MONEY_UNLOCK_WINDOW) + // tx1 + // (0 )- (0r)- (1 )- (2a)- (3a)- <- alt chain + // \ + // \- (2 )- <- main chain + + bool r = false; + GENERATE_ACCOUNT(miner_acc); + GENERATE_ACCOUNT(alice_acc); + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); + + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + DO_CALLBACK(events, "check_not_being_in_cp_zone"); + + MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc); + MAKE_TX(events, tx1, miner_acc, alice_acc, MK_TEST_COINS(1), blk_1); + MAKE_NEXT_BLOCK_TX1(events, blk_2a, blk_1, miner_acc, tx1); + MAKE_NEXT_BLOCK(events, blk_3a, blk_2a, miner_acc); + + DO_CALLBACK(events, "check_not_being_in_cp_zone"); + + // 0 ... N N+1 N+2 N+3 N+4 N+5 N+6 <- height (N = CURRENCY_MINED_MONEY_UNLOCK_WINDOW) + // +-----------> CP <- checkpoint + // tx1 | + // (0 )- (0r)- (1 )- (2a)- (3a)- <- alt chain + // \ | <- when CP set up + // \- (2 )- (3 )- (4 )- (5 )- (6 )- <- main chain + + DO_CALLBACK_PARAMS(events, "set_checkpoint", params_checkpoint(CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5)); + + MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc); + MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc); + MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_acc); // <-- this should trigger the switching + + DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(get_block_height(blk_4), get_block_hash(blk_4))); + DO_CALLBACK(events, "check_being_in_cp_zone"); + + MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_acc); // <-- CHECKPOINT + MAKE_NEXT_BLOCK(events, blk_6, blk_5, miner_acc); + + DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(get_block_height(blk_6), get_block_hash(blk_6))); + DO_CALLBACK(events, "check_not_being_in_cp_zone"); + + return true; +} diff --git a/tests/core_tests/checkpoints_tests.h b/tests/core_tests/checkpoints_tests.h index 67f84a12..672d9b42 100644 --- a/tests/core_tests/checkpoints_tests.h +++ b/tests/core_tests/checkpoints_tests.h @@ -23,6 +23,9 @@ protected: uint64_t height; crypto::hash hash; }; + +private: + currency::checkpoints m_local_checkpoints; }; struct gen_checkpoints_attachments_basic : public checkpoints_test @@ -109,3 +112,9 @@ struct gen_checkpoints_and_invalid_tx_to_pool : public checkpoints_test bool generate(std::vector& events) const; bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; + +struct gen_checkpoints_set_after_switching_to_altchain : public checkpoints_test +{ + gen_checkpoints_set_after_switching_to_altchain(); + bool generate(std::vector& events) const; +}; 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/tests/core_tests/wallet_tests.cpp b/tests/core_tests/wallet_tests.cpp index ab62563f..f4f2e027 100644 --- a/tests/core_tests/wallet_tests.cpp +++ b/tests/core_tests/wallet_tests.cpp @@ -3553,5 +3553,87 @@ bool wallet_watch_only_and_chain_switch::c1(currency::core& c, size_t ev_index, CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, MK_TEST_COINS(7), false, UINT64_MAX, 0, 0, MK_TEST_COINS(7), 0), false, ""); + return true; +} + +//------------------------------------------------------------------------------ + +wallet_spend_form_auditable_and_track::wallet_spend_form_auditable_and_track() +{ + REGISTER_CALLBACK_METHOD(wallet_spend_form_auditable_and_track, c1); +} + +bool wallet_spend_form_auditable_and_track::generate(std::vector& events) const +{ + bool r = false; + + 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(true); // Alice has auditable wallet + account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc = alice_acc; bob_acc.make_account_watch_only(); // Bob has Alice's tracking wallet + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time()); + REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + MAKE_TX(events, tx_1, miner_acc, alice_acc, MK_TEST_COINS(5), blk_0r); + + MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_1); + REWIND_BLOCKS_N(events, blk_1r, blk_1, miner_acc, WALLET_DEFAULT_TX_SPENDABLE_AGE); + + std::vector attachments; + tx_comment comment_attachment = AUTO_VAL_INIT(comment_attachment); + m_comment = "Jokes are funny!"; + comment_attachment.comment = m_comment; + attachments.push_back(comment_attachment); + MAKE_TX_ATTACH(events, tx_2, alice_acc, miner_acc, MK_TEST_COINS(1), blk_1r, attachments); + + MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_2); + + DO_CALLBACK(events, "c1"); + + return true; +} + +bool wallet_spend_form_auditable_and_track::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); + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX); + + // make sure Alice's wallet is autibale and not watch-only + CHECK_AND_ASSERT_MES(!alice_wlt->is_watch_only() && alice_wlt->is_auditable(), false, "incorrect type of Alice's wallet"); + // make sure Bob's wallet is a tracking wallet + CHECK_AND_ASSERT_MES(bob_wlt->is_watch_only() && bob_wlt->is_auditable(), false, "incorrect type of Bob's wallet"); + + const account_public_address& bob_addr = bob_wlt->get_account().get_public_address(); + const account_public_address& alice_addr = alice_wlt->get_account().get_public_address(); + + // make sure their addresses are linked indeed + CHECK_AND_ASSERT_MES(bob_addr.view_public_key == alice_addr.view_public_key && bob_addr.spend_public_key == alice_addr.spend_public_key, false, + "Bob's tracking wallet address is not linked with Alice's one"); + + CHECK_AND_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count()); + + bob_wlt->refresh(); + + // check the balances + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Alice", alice_wlt, MK_TEST_COINS(3), false, UINT64_MAX, 0, 0, 0, 0), false, ""); + CHECK_AND_ASSERT_MES(refresh_wallet_and_check_balance("", "Bob", bob_wlt, MK_TEST_COINS(3), false, UINT64_MAX, 0, 0, 0, 0), false, ""); + + r = false; + bool r_comment = false; + bob_wlt->enumerate_transfers_history([&](const tools::wallet_public::wallet_transfer_info& wti) { + if (wti.amount == MK_TEST_COINS(5)) + { + r_comment = (wti.comment == m_comment); + if (!r_comment) + return false; // stop + } + return true; // continue + }, true); + CHECK_AND_ASSERT_MES(r, false, "cannot get comment from tx"); + CHECK_AND_ASSERT_MES(r_comment, false, "wrong comment got from tx"); + return true; } diff --git a/tests/core_tests/wallet_tests.h b/tests/core_tests/wallet_tests.h index 17c83bcd..eb54960b 100644 --- a/tests/core_tests/wallet_tests.h +++ b/tests/core_tests/wallet_tests.h @@ -276,3 +276,12 @@ struct wallet_watch_only_and_chain_switch : public wallet_test mutable crypto::hash m_split_point_block_id; mutable uint64_t m_split_point_block_height; }; + +struct wallet_spend_form_auditable_and_track : public wallet_test +{ + wallet_spend_form_auditable_and_track(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); + + mutable std::string m_comment; +}; diff --git a/tests/unit_tests/check_points_test.cpp b/tests/unit_tests/check_points_test.cpp index 543e2ac6..ab2372c1 100644 --- a/tests/unit_tests/check_points_test.cpp +++ b/tests/unit_tests/check_points_test.cpp @@ -39,3 +39,87 @@ TEST(checkpoints_test, test_checkpoints_for_alternative) r = cp.is_height_passed_zone(11, 12); ASSERT_FALSE(r); } + + +TEST(checkpoints_test, get_checkpoint_before_height_1) +{ + currency::checkpoints cp; + cp.add_checkpoint(15, "0000000000000000000000000000000000000000000000000000000000000000"); + + for (uint64_t h = 0; h <= 15; ++h) + ASSERT_TRUE(cp.get_checkpoint_before_height(h) == 0); + + ASSERT_TRUE(cp.get_checkpoint_before_height(16) == 15); + + for (uint64_t h = 17; h < 100; ++h) + ASSERT_TRUE(cp.get_checkpoint_before_height(h) == 15); +} + + +TEST(checkpoints_test, get_checkpoint_before_height_2) +{ + currency::checkpoints cp; + cp.add_checkpoint(11, "0000000000000000000000000000000000000000000000000000000000000000"); + cp.add_checkpoint(15, "0000000000000000000000000000000000000000000000000000000000000000"); + + for (uint64_t h = 0; h < 11; ++h) + ASSERT_TRUE(cp.get_checkpoint_before_height(h) == 0); + + ASSERT_TRUE(cp.get_checkpoint_before_height(11) == 0); + + ASSERT_TRUE(cp.get_checkpoint_before_height(12) == 11); + ASSERT_TRUE(cp.get_checkpoint_before_height(13) == 11); + ASSERT_TRUE(cp.get_checkpoint_before_height(14) == 11); + ASSERT_TRUE(cp.get_checkpoint_before_height(15) == 11); + + ASSERT_TRUE(cp.get_checkpoint_before_height(16) == 15); + + for (uint64_t h = 17; h < 100; ++h) + ASSERT_TRUE(cp.get_checkpoint_before_height(h) == 15); +} + + +TEST(checkpoints_test, get_checkpoint_before_height_3) +{ + currency::checkpoints cp; + cp.add_checkpoint(11, "0000000000000000000000000000000000000000000000000000000000000000"); + cp.add_checkpoint(15, "0000000000000000000000000000000000000000000000000000000000000000"); + cp.add_checkpoint(21, "0000000000000000000000000000000000000000000000000000000000000000"); + + for (uint64_t h = 0; h < 11; ++h) + ASSERT_TRUE(cp.get_checkpoint_before_height(h) == 0); + + ASSERT_TRUE(cp.get_checkpoint_before_height(11) == 0); + + ASSERT_TRUE(cp.get_checkpoint_before_height(12) == 11); + ASSERT_TRUE(cp.get_checkpoint_before_height(13) == 11); + ASSERT_TRUE(cp.get_checkpoint_before_height(14) == 11); + ASSERT_TRUE(cp.get_checkpoint_before_height(15) == 11); + + ASSERT_TRUE(cp.get_checkpoint_before_height(16) == 15); + ASSERT_TRUE(cp.get_checkpoint_before_height(17) == 15); + ASSERT_TRUE(cp.get_checkpoint_before_height(18) == 15); + ASSERT_TRUE(cp.get_checkpoint_before_height(19) == 15); + ASSERT_TRUE(cp.get_checkpoint_before_height(20) == 15); + ASSERT_TRUE(cp.get_checkpoint_before_height(21) == 15); + + ASSERT_TRUE(cp.get_checkpoint_before_height(22) == 21); + + for (uint64_t h = 22; h < 100; ++h) + ASSERT_TRUE(cp.get_checkpoint_before_height(h) == 21); +} + + +TEST(checkpoints_test, is_in_checkpoint_zone) +{ + currency::checkpoints cp; + cp.add_checkpoint(11, "0000000000000000000000000000000000000000000000000000000000000000"); + cp.add_checkpoint(15, "0000000000000000000000000000000000000000000000000000000000000000"); + cp.add_checkpoint(21, "0000000000000000000000000000000000000000000000000000000000000000"); + + for (uint64_t h = 0; h < 22; ++h) + ASSERT_TRUE(cp.is_in_checkpoint_zone(h)); + + for (uint64_t h = 22; h < 100; ++h) + ASSERT_FALSE(cp.is_in_checkpoint_zone(h)); +} 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]