From 8ac5011a6cdd0e00ebb19607c0d07e5287e8584e Mon Sep 17 00:00:00 2001 From: Jean Pierre Dudey Date: Tue, 13 Feb 2018 20:51:37 -0400 Subject: [PATCH 01/47] Use `genesis_tx` parameter in `generate_genesis_block`. /monero#3261 * src/cryptnote_config.h: The constant `config::testnet::GENESIS_TX` was changed to be the same as `config::GENESIS_TX` (the mainnet's transaction) because the mainnet's transaction was being used for both networks. * src/cryptonote_core/cryptonote_tx_utils.cpp: The `generate_genesis_block` function was ignoring the `genesis_tx` parameter, and instead it was using the `config::GENESIS_TX` constant. That's why the testnet genesis transaction was changed. Also five lines of unused code were removed. Signed-off-by: Jean Pierre Dudey --- src/cryptonote_config.h | 2 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index e38e15cb22..844faec8f5 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -171,7 +171,7 @@ namespace config boost::uuids::uuid const NETWORK_ID = { { 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11 } }; // Bender's daydream - std::string const GENESIS_TX = "013c01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd0880712101168d0c4ca86fb55a4cf6a36d31431be1c53a3bd7411bb24e8832410289fa6f3b"; + std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1"; uint32_t const GENESIS_NONCE = 10001; } } diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 89f24a4d4d..0739e96c95 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -622,17 +622,8 @@ namespace cryptonote //genesis block bl = boost::value_initialized(); - - account_public_address ac = boost::value_initialized(); - std::vector sz; - construct_miner_tx(0, 0, 0, 0, 0, ac, bl.miner_tx); // zero fee in genesis - blobdata txb = tx_to_blob(bl.miner_tx); - std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); - - std::string genesis_coinbase_tx_hex = config::GENESIS_TX; - blobdata tx_bl; - bool r = string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); + bool r = string_tools::parse_hexstr_to_binbuff(genesis_tx, tx_bl); CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); From f0a263aba21510f095565a09a528a8374cd976df Mon Sep 17 00:00:00 2001 From: dEBRUYNE-1 Date: Sat, 23 Dec 2017 15:04:20 +0100 Subject: [PATCH 02/47] Fix Windows build /monero#2994 Fix no new line --- contrib/epee/include/misc_os_dependent.h | 4 ++++ external/easylogging++/ea_config.h | 1 + src/daemonizer/windows_service.cpp | 3 +++ 3 files changed, 8 insertions(+) diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h index 81cecf714c..99690b3014 100644 --- a/contrib/epee/include/misc_os_dependent.h +++ b/contrib/epee/include/misc_os_dependent.h @@ -23,6 +23,10 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#ifdef _WIN32 +#include +#endif + #ifdef WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN diff --git a/external/easylogging++/ea_config.h b/external/easylogging++/ea_config.h index 2524d34776..6215e67dec 100644 --- a/external/easylogging++/ea_config.h +++ b/external/easylogging++/ea_config.h @@ -8,3 +8,4 @@ #endif #define ELPP_DISABLE_DEFAULT_CRASH_HANDLING #define ELPP_NO_CHECK_MACROS +#define ELPP_WINSOCK2 diff --git a/src/daemonizer/windows_service.cpp b/src/daemonizer/windows_service.cpp index d540f5bf8f..9b8e46615c 100644 --- a/src/daemonizer/windows_service.cpp +++ b/src/daemonizer/windows_service.cpp @@ -26,6 +26,9 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include +#include + #undef UNICODE #undef _UNICODE From 4aec10e402603f7cd25eda2bc130ce543d437fc8 Mon Sep 17 00:00:00 2001 From: stoffu Date: Fri, 9 Feb 2018 21:11:47 +0900 Subject: [PATCH 03/47] unit_tests: added gtest utility ASSERT_EQ_MAP for easily testing key-value map /monero#3245 --- tests/unit_tests/unit_tests_utils.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit_tests/unit_tests_utils.h b/tests/unit_tests/unit_tests_utils.h index a07eaf02bd..3eb8cf496f 100644 --- a/tests/unit_tests/unit_tests_utils.h +++ b/tests/unit_tests/unit_tests_utils.h @@ -65,3 +65,10 @@ namespace unit_test std::atomic m_counter; }; } + +# define ASSERT_EQ_MAP(val, map, key) \ + do { \ + auto found = map.find(key); \ + ASSERT_TRUE(found != map.end()); \ + ASSERT_EQ(val, found->second); \ + } while (false) From f32e89af06a097dc3f5dcd769e835c5418adc86a Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 8 Feb 2018 01:48:24 +0900 Subject: [PATCH 04/47] rpc: default do_not_relay to false in sendrawtransaction /monero#3136 --- src/rpc/core_rpc_server_commands_defs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index ad0bff0779..2230141d56 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -836,7 +836,7 @@ namespace cryptonote BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(tx_as_hex) - KV_SERIALIZE(do_not_relay) + KV_SERIALIZE_OPT(do_not_relay, false) END_KV_SERIALIZE_MAP() }; From b83b81b160779ced263be09b88c4eca8db71e15a Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 8 Feb 2018 16:04:50 +0900 Subject: [PATCH 05/47] epee get_ns_count: cast to uint64_t before multiplying 10^9 to avoid overflow /monero#3243 --- contrib/epee/include/misc_os_dependent.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h index 99690b3014..ffe575501e 100644 --- a/contrib/epee/include/misc_os_dependent.h +++ b/contrib/epee/include/misc_os_dependent.h @@ -75,13 +75,13 @@ namespace misc_utils clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); - return (mts.tv_sec * 1000000000) + (mts.tv_nsec); + return ((uint64_t)mts.tv_sec * 1000000000) + (mts.tv_nsec); #else struct timespec ts; if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { return 0; } - return (ts.tv_sec * 1000000000) + (ts.tv_nsec); + return ((uint64_t)ts.tv_sec * 1000000000) + (ts.tv_nsec); #endif } From cfbe05e1b36fa1fe8b93772037d462d0b3b64d97 Mon Sep 17 00:00:00 2001 From: stoffu Date: Thu, 21 Dec 2017 15:22:25 +0000 Subject: [PATCH 06/47] fix some link errors in debug mode for macos /monero#2985 --- src/crypto/CMakeLists.txt | 1 + src/mnemonics/CMakeLists.txt | 1 + src/p2p/CMakeLists.txt | 1 + src/rpc/CMakeLists.txt | 2 ++ 4 files changed, 5 insertions(+) diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 1e06a0dfd0..7902ddc6bd 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -76,6 +76,7 @@ monero_add_library(cncrypto ${crypto_private_headers}) target_link_libraries(cncrypto PUBLIC + epee ${Boost_SYSTEM_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/mnemonics/CMakeLists.txt b/src/mnemonics/CMakeLists.txt index 5ce2198ae9..79964e8731 100644 --- a/src/mnemonics/CMakeLists.txt +++ b/src/mnemonics/CMakeLists.txt @@ -57,6 +57,7 @@ monero_add_library(mnemonics ${mnemonics_private_headers}) target_link_libraries(mnemonics PUBLIC + epee easylogging ${Boost_SYSTEM_LIBRARY} PRIVATE diff --git a/src/p2p/CMakeLists.txt b/src/p2p/CMakeLists.txt index 123b0a272c..3fc053dc7f 100644 --- a/src/p2p/CMakeLists.txt +++ b/src/p2p/CMakeLists.txt @@ -46,5 +46,6 @@ target_link_libraries(p2p ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} + ${Boost_SERIALIZATION_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 748c6b8c13..19ea939029 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -101,6 +101,7 @@ target_link_libraries(rpc_base epee ${Boost_REGEX_LIBRARY} ${Boost_THREAD_LIBRARY} + ${Boost_PROGRAM_OPTIONS_LIBRARY} PRIVATE ${EXTRA_LIBRARIES}) @@ -125,6 +126,7 @@ target_link_libraries(daemon_messages target_link_libraries(daemon_rpc_server LINK_PRIVATE + rpc cryptonote_core cryptonote_protocol daemon_messages From 8caef32dc341a2cea64088762d703e0a3cb122d9 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sat, 10 Feb 2018 12:59:25 +0900 Subject: [PATCH 07/47] wallet2: don't throw when spent amount is inconsistent /monero#3246 --- src/wallet/wallet2.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 04c6ee2366..d579b5aa2e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1227,11 +1227,21 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote uint64_t amount = boost::get(in).amount; if (amount > 0) { - THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error, - std::string("Inconsistent amount in tx input: got ") + print_money(amount) + - std::string(", expected ") + print_money(td.amount())); + if(amount != td.amount()) + { + MERROR("Inconsistent amount in tx input: got " << print_money(amount) << + ", expected " << print_money(td.amount())); + // this means: + // 1) the same output pub key was used as destination multiple times, + // 2) the wallet set the highest amount among them to transfer_details::m_amount, and + // 3) the wallet somehow spent that output with an amount smaller than the above amount, causing inconsistency + td.m_amount = amount; + } + } + else + { + amount = td.amount(); } - amount = td.amount(); tx_money_spent_in_ins += amount; if (subaddr_account && *subaddr_account != td.m_subaddr_index.major) LOG_ERROR("spent funds are from different subaddress accounts; count of incoming/outgoing payments will be incorrect"); From dcd8dea7b558c22f9973d5783f098dd2ef1e1f77 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 12 Feb 2018 20:36:15 +0900 Subject: [PATCH 08/47] wallet2: don't store invalid key image when watch-only /monero#3246 --- src/wallet/wallet2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d579b5aa2e..33f273d408 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1133,7 +1133,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote td.m_rct = false; } set_unspent(m_transfers.size()-1); - if (!m_multisig) + if (!m_multisig && !m_watch_only) m_key_images[td.m_key_image] = m_transfers.size()-1; m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1; if (m_multisig) From 290ba396e725d53c6c22e7031edf6873fffc312b Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 18 Dec 2017 15:09:51 +0900 Subject: [PATCH 09/47] wallet-api: added Utils::onStartup /monero#2952 --- src/wallet/api/utils.cpp | 5 +++++ src/wallet/api/wallet2_api.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/wallet/api/utils.cpp b/src/wallet/api/utils.cpp index a9646e0385..e54dd9f1c2 100644 --- a/src/wallet/api/utils.cpp +++ b/src/wallet/api/utils.cpp @@ -48,6 +48,11 @@ bool isAddressLocal(const std::string &address) } } +void onStartup() +{ + tools::on_startup(); +} + } diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 8593bd1f9a..ab1a48d6e5 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -42,6 +42,7 @@ namespace Monero { namespace Utils { bool isAddressLocal(const std::string &hostaddr); + void onStartup(); } template From ca7feb769d39caf414d3fe6455c7b00d6e9c6e3d Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 15 Jan 2018 15:53:38 +0000 Subject: [PATCH 10/47] wallet2: fix sweep_all sending an atomic unit /monero#3130 --- src/wallet/wallet2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 33f273d408..b7c5087472 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6897,7 +6897,7 @@ std::vector wallet2::create_transactions_from(const crypton THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself"); - while (needed_fee > test_ptx.fee) { + do { LOG_PRINT_L2("We made a tx, adjusting fee and saving it"); tx.dsts[0].amount = available_for_fee - needed_fee; if (use_rct) @@ -6910,7 +6910,7 @@ std::vector wallet2::create_transactions_from(const crypton needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); LOG_PRINT_L2("Made an attempt at a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change"); - } + } while (needed_fee > test_ptx.fee); LOG_PRINT_L2("Made a final " << ((txBlob.size() + 1023)/1024) << " kB tx, with " << print_money(test_ptx.fee) << " fee and " << print_money(test_ptx.change_dts.amount) << " change"); From ba5560fad958d062eac432e5b4da22588991c260 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 15 Jan 2018 18:47:11 +0000 Subject: [PATCH 11/47] simplewallet: single out 0 amount destinations as dummy ones /monero#3130 Avoids surprising the user with "sending 0 to..." --- src/simplewallet/simplewallet.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 64e665fb3a..54d6bc8430 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -4622,12 +4622,24 @@ bool simple_wallet::accept_loaded_tx(const std::function get_num_txes, payment_id_string = "no payment ID"; std::string dest_string; + size_t n_dummy_outputs = 0; for (auto i = dests.begin(); i != dests.end(); ) { - dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second.second) % i->second.first).str(); + if (i->second.second > 0) + { + if (!dest_string.empty()) + dest_string += ", "; + dest_string += (boost::format(tr("sending %s to %s")) % print_money(i->second.second) % i->second.first).str(); + } + else + ++n_dummy_outputs; ++i; - if (i != dests.end()) + } + if (n_dummy_outputs > 0) + { + if (!dest_string.empty()) dest_string += ", "; + dest_string += std::to_string(n_dummy_outputs) + tr(" dummy output(s)"); } if (dest_string.empty()) dest_string = tr("with no destinations"); From f1b7bbe103a51a475134671c398031a098bc89c4 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sun, 25 Feb 2018 12:44:10 +0900 Subject: [PATCH 12/47] simplewallet: set seed language when restoring from english-old seed /monero#3312 --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 54d6bc8430..181f0c1b33 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2593,7 +2593,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, // a seed language is not already specified AND // (it is not a wallet restore OR if it was a deprecated wallet // that was earlier used before this restore) - if ((!two_random) && (mnemonic_language.empty()) && (!m_restore_deterministic_wallet || was_deprecated_wallet)) + if ((!two_random) && (mnemonic_language.empty() || mnemonic_language == crypto::ElectrumWords::old_language_name) && (!m_restore_deterministic_wallet || was_deprecated_wallet)) { if (was_deprecated_wallet) { From 19ad61fcb21d221b0047fefb8bdcdaa7f3c57df1 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sun, 25 Feb 2018 22:04:36 +0900 Subject: [PATCH 13/47] wallet api: when restoring from EnglishOld, set language to English /monero#3314 --- src/wallet/api/wallet.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index fd0b658663..b229a9f9d6 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -40,6 +40,7 @@ #include "common/util.h" #include "mnemonics/electrum-words.h" +#include "mnemonics/english.h" #include #include #include @@ -588,6 +589,9 @@ bool WalletImpl::recover(const std::string &path, const std::string &seed) return false; } + if (old_language == crypto::ElectrumWords::old_language_name) + old_language = Language::English().get_language_name(); + try { m_wallet->set_seed_language(old_language); m_wallet->generate(path, "", recovery_key, true, false); From ce9ff2ef202ee71a8e8a03eb7a1a025dc265a476 Mon Sep 17 00:00:00 2001 From: stoffu Date: Sat, 20 Jan 2018 19:38:14 +0900 Subject: [PATCH 14/47] Bootstrap daemon /monero#3165 --- src/daemon/rpc_command_executor.cpp | 25 ++- src/rpc/core_rpc_server.cpp | 272 ++++++++++++++++++++++++ src/rpc/core_rpc_server.h | 14 +- src/rpc/core_rpc_server_commands_defs.h | 64 ++++++ 4 files changed, 370 insertions(+), 5 deletions(-) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index d7ee28baa6..84d7543fef 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -353,15 +353,18 @@ static std::string get_fork_extra_info(uint64_t t, uint64_t now, uint64_t block_ return ""; } -static float get_sync_percentage(const cryptonote::COMMAND_RPC_GET_INFO::response &ires) +static float get_sync_percentage(uint64_t height, uint64_t target_height) { - uint64_t height = ires.height; - uint64_t target_height = ires.target_height ? ires.target_height < ires.height ? ires.height : ires.target_height : ires.height; + target_height = target_height ? target_height < height ? height : target_height : height; float pc = 100.0f * height / target_height; if (height < target_height && pc > 99.9f) return 99.9f; // to avoid 100% when not fully synced return pc; } +static float get_sync_percentage(const cryptonote::COMMAND_RPC_GET_INFO::response &ires) +{ + return get_sync_percentage(ires.height, ires.target_height); +} bool t_rpc_command_executor::show_status() { cryptonote::COMMAND_RPC_GET_INFO::request ireq; @@ -421,12 +424,26 @@ bool t_rpc_command_executor::show_status() { std::time_t uptime = std::time(nullptr) - ires.start_time; uint64_t net_height = ires.target_height > ires.height ? ires.target_height : ires.height; + std::string bootstrap_msg; + if (ires.was_bootstrap_ever_used) + { + bootstrap_msg = ", bootstrapping from " + ires.bootstrap_daemon_address; + if (ires.untrusted) + { + bootstrap_msg += (boost::format(", local height: %llu (%.1f%%)") % ires.height_without_bootstrap % get_sync_percentage(ires.height_without_bootstrap, net_height)).str(); + } + else + { + bootstrap_msg += " was used before"; + } + } - tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us") + tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us") % (unsigned long long)ires.height % (unsigned long long)net_height % get_sync_percentage(ires) % (ires.testnet ? "testnet" : "mainnet") + % bootstrap_msg % (!has_mining_info ? "mining info unavailable" : mining_busy ? "syncing" : mres.active ? ( ( mres.is_background_mining_enabled ? "smart " : "" ) + std::string("mining at ") + get_mining_speed(mres.speed) ) : "not mining") % get_mining_speed(ires.difficulty / ires.target) % (unsigned)hfres.version diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 803588cbda..c7179d0f33 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -42,6 +42,7 @@ using namespace epee; #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_basic_impl.h" #include "misc_language.h" +#include "storages/http_abstract_invoke.h" #include "crypto/hash.h" #include "rpc/rpc_args.h" #include "core_rpc_server_error_codes.h" @@ -75,6 +76,8 @@ namespace cryptonote command_line::add_arg(desc, arg_testnet_rpc_bind_port); command_line::add_arg(desc, arg_testnet_rpc_restricted_bind_port); command_line::add_arg(desc, arg_restricted_rpc); + command_line::add_arg(desc, arg_bootstrap_daemon_address); + command_line::add_arg(desc, arg_bootstrap_daemon_login); cryptonote::rpc_args::init_options(desc); } //------------------------------------------------------------------------------------------------------------------------------ @@ -101,6 +104,30 @@ namespace cryptonote if (!rpc_config) return false; + m_bootstrap_daemon_address = command_line::get_arg(vm, arg_bootstrap_daemon_address); + if (!m_bootstrap_daemon_address.empty()) + { + const std::string &bootstrap_daemon_login = command_line::get_arg(vm, arg_bootstrap_daemon_login); + const auto loc = bootstrap_daemon_login.find(':'); + if (!bootstrap_daemon_login.empty() && loc != std::string::npos) + { + epee::net_utils::http::login login; + login.username = bootstrap_daemon_login.substr(0, loc); + login.password = bootstrap_daemon_login.substr(loc + 1); + m_http_client.set_server(m_bootstrap_daemon_address, login, false); + } + else + { + m_http_client.set_server(m_bootstrap_daemon_address, boost::none, false); + } + m_should_use_bootstrap_daemon = true; + } + else + { + m_should_use_bootstrap_daemon = false; + } + m_was_bootstrap_ever_used = false; + boost::optional http_login{}; if (rpc_config->login) @@ -125,6 +152,10 @@ namespace cryptonote bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) { PERF_TIMER(on_get_height); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/getheight", req, res, r)) + return r; + res.height = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -133,6 +164,17 @@ namespace cryptonote bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) { PERF_TIMER(on_get_info); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/getinfo", req, res, r)) + { + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + crypto::hash top_hash; + m_core.get_blockchain_top(res.height_without_bootstrap, top_hash); + ++res.height_without_bootstrap; // turn top block height into blockchain height + res.was_bootstrap_ever_used = true; + return r; + } + crypto::hash top_hash; m_core.get_blockchain_top(res.height, top_hash); ++res.height; // turn top block height into blockchain height @@ -156,6 +198,12 @@ namespace cryptonote res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits::max() : m_core.get_free_space(); res.offline = m_core.offline(); + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + res.height_without_bootstrap = res.height; + { + boost::shared_lock lock(m_bootstrap_daemon_mutex); + res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; + } return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -179,6 +227,10 @@ namespace cryptonote bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) { PERF_TIMER(on_get_blocks); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getblocks.bin", req, res, r)) + return r; + std::list > > bs; if(!m_core.find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) @@ -238,6 +290,10 @@ namespace cryptonote bool core_rpc_server::on_get_alt_blocks_hashes(const COMMAND_RPC_GET_ALT_BLOCKS_HASHES::request& req, COMMAND_RPC_GET_ALT_BLOCKS_HASHES::response& res) { PERF_TIMER(on_get_alt_blocks_hashes); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_alt_blocks_hashes", req, res, r)) + return r; + std::list blks; if(!m_core.get_alternative_blocks(blks)) @@ -261,6 +317,10 @@ namespace cryptonote bool core_rpc_server::on_get_blocks_by_height(const COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCKS_BY_HEIGHT::response& res) { PERF_TIMER(on_get_blocks_by_height); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getblocks_by_height.bin", req, res, r)) + return r; + res.status = "Failed"; res.blocks.clear(); res.blocks.reserve(req.heights.size()); @@ -291,6 +351,10 @@ namespace cryptonote bool core_rpc_server::on_get_hashes(const COMMAND_RPC_GET_HASHES_FAST::request& req, COMMAND_RPC_GET_HASHES_FAST::response& res) { PERF_TIMER(on_get_hashes); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/gethashes.bin", req, res, r)) + return r; + NOTIFY_RESPONSE_CHAIN_ENTRY::request resp; resp.start_height = req.start_height; @@ -310,6 +374,10 @@ namespace cryptonote bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) { PERF_TIMER(on_get_random_outs); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getrandom_outs.bin", req, res, r)) + return r; + res.status = "Failed"; if (m_restricted) @@ -349,6 +417,10 @@ namespace cryptonote bool core_rpc_server::on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res) { PERF_TIMER(on_get_outs_bin); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/get_outs.bin", req, res, r)) + return r; + res.status = "Failed"; if (m_restricted) @@ -372,6 +444,10 @@ namespace cryptonote bool core_rpc_server::on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) { PERF_TIMER(on_get_outs); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_outs", req, res, r)) + return r; + res.status = "Failed"; if (m_restricted) @@ -410,6 +486,10 @@ namespace cryptonote bool core_rpc_server::on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res) { PERF_TIMER(on_get_random_rct_outs); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/getrandom_rctouts.bin", req, res, r)) + return r; + res.status = "Failed"; if(!m_core.get_random_rct_outs(req, res)) { @@ -434,6 +514,10 @@ namespace cryptonote bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) { PERF_TIMER(on_get_indexes); + bool ok; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::BIN, "/get_o_indexes.bin", req, res, ok)) + return ok; + bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); if(!r) { @@ -448,6 +532,10 @@ namespace cryptonote bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) { PERF_TIMER(on_get_transactions); + bool ok; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/gettransactions", req, res, ok)) + return ok; + std::vector vh; for(const auto& tx_hex_str: req.txs_hashes) { @@ -593,6 +681,10 @@ namespace cryptonote bool core_rpc_server::on_is_key_image_spent(const COMMAND_RPC_IS_KEY_IMAGE_SPENT::request& req, COMMAND_RPC_IS_KEY_IMAGE_SPENT::response& res, bool request_has_rpc_origin) { PERF_TIMER(on_is_key_image_spent); + bool ok; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/is_key_image_spent", req, res, ok)) + return ok; + std::vector key_images; for(const auto& ki_hex_str: req.key_images) { @@ -656,6 +748,10 @@ namespace cryptonote bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) { PERF_TIMER(on_send_raw_tx); + bool ok; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/sendrawtransaction", req, res, ok)) + return ok; + CHECK_CORE_READY(); std::string tx_blob; @@ -879,6 +975,10 @@ namespace cryptonote bool core_rpc_server::on_get_transaction_pool(const COMMAND_RPC_GET_TRANSACTION_POOL::request& req, COMMAND_RPC_GET_TRANSACTION_POOL::response& res, bool request_has_rpc_origin) { PERF_TIMER(on_get_transaction_pool); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_transaction_pool", req, res, r)) + return r; + m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; @@ -887,6 +987,10 @@ namespace cryptonote bool core_rpc_server::on_get_transaction_pool_hashes(const COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_HASHES::response& res, bool request_has_rpc_origin) { PERF_TIMER(on_get_transaction_pool_hashes); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_transaction_pool_hashes.bin", req, res, r)) + return r; + m_core.get_pool_transaction_hashes(res.tx_hashes, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; @@ -895,6 +999,10 @@ namespace cryptonote bool core_rpc_server::on_get_transaction_pool_stats(const COMMAND_RPC_GET_TRANSACTION_POOL_STATS::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_STATS::response& res, bool request_has_rpc_origin) { PERF_TIMER(on_get_transaction_pool_stats); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_transaction_pool_stats", req, res, r)) + return r; + m_core.get_pool_transaction_stats(res.pool_stats, !request_has_rpc_origin || !m_restricted); res.status = CORE_RPC_STATUS_OK; return true; @@ -913,6 +1021,14 @@ namespace cryptonote bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) { PERF_TIMER(on_getblockcount); + { + boost::shared_lock lock(m_bootstrap_daemon_mutex); + if (m_should_use_bootstrap_daemon) + { + res.status = "This command is unsupported for bootstrap daemon"; + return false; + } + } res.count = m_core.get_current_blockchain_height(); res.status = CORE_RPC_STATUS_OK; return true; @@ -921,6 +1037,14 @@ namespace cryptonote bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_getblockhash); + { + boost::shared_lock lock(m_bootstrap_daemon_mutex); + if (m_should_use_bootstrap_daemon) + { + res = "This command is unsupported for bootstrap daemon"; + return false; + } + } if(req.size() != 1) { error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; @@ -957,6 +1081,10 @@ namespace cryptonote bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_getblocktemplate); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "getblocktemplate", req, res, r)) + return r; + if(!check_core_ready()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; @@ -1032,6 +1160,14 @@ namespace cryptonote bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_submitblock); + { + boost::shared_lock lock(m_bootstrap_daemon_mutex); + if (m_should_use_bootstrap_daemon) + { + res.status = "This command is unsupported for bootstrap daemon"; + return false; + } + } CHECK_CORE_READY(); if(req.size()!=1) { @@ -1105,9 +1241,80 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + template + bool core_rpc_server::use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r) + { + res.untrusted = false; + if (m_bootstrap_daemon_address.empty()) + return false; + + boost::unique_lock lock(m_bootstrap_daemon_mutex); + if (!m_should_use_bootstrap_daemon) + { + MINFO("The local daemon is fully synced. Not switching back to the bootstrap daemon"); + return false; + } + + auto current_time = std::chrono::system_clock::now(); + if (current_time - m_bootstrap_height_check_time > std::chrono::seconds(30)) // update every 30s + { + m_bootstrap_height_check_time = current_time; + + uint64_t top_height; + crypto::hash top_hash; + m_core.get_blockchain_top(top_height, top_hash); + ++top_height; // turn top block height into blockchain height + + // query bootstrap daemon's height + cryptonote::COMMAND_RPC_GET_HEIGHT::request getheight_req; + cryptonote::COMMAND_RPC_GET_HEIGHT::response getheight_res; + bool ok = epee::net_utils::invoke_http_json("/getheight", getheight_req, getheight_res, m_http_client); + ok = ok && getheight_res.status == CORE_RPC_STATUS_OK; + + m_should_use_bootstrap_daemon = ok && top_height + 10 < getheight_res.height; + MINFO((m_should_use_bootstrap_daemon ? "Using" : "Not using") << " the bootstrap daemon (our height: " << top_height << ", bootstrap daemon's height: " << getheight_res.height << ")"); + } + if (!m_should_use_bootstrap_daemon) + return false; + + if (mode == invoke_http_mode::JON) + { + r = epee::net_utils::invoke_http_json(command_name, req, res, m_http_client); + } + else if (mode == invoke_http_mode::BIN) + { + r = epee::net_utils::invoke_http_bin(command_name, req, res, m_http_client); + } + else if (mode == invoke_http_mode::JON_RPC) + { + epee::json_rpc::request json_req = AUTO_VAL_INIT(json_req); + epee::json_rpc::response json_resp = AUTO_VAL_INIT(json_resp); + json_req.jsonrpc = "2.0"; + json_req.id = epee::serialization::storage_entry(0); + json_req.method = command_name; + json_req.params = req; + r = net_utils::invoke_http_json("/json_rpc", json_req, json_resp, m_http_client); + if (r) + res = json_resp.result; + } + else + { + MERROR("Unknown invoke_http_mode: " << mode); + return false; + } + m_was_bootstrap_ever_used = true; + r = r && res.status == CORE_RPC_STATUS_OK; + res.untrusted = true; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_last_block_header(const COMMAND_RPC_GET_LAST_BLOCK_HEADER::request& req, COMMAND_RPC_GET_LAST_BLOCK_HEADER::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_get_last_block_header); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "getlastblockheader", req, res, r)) + return r; + CHECK_CORE_READY(); uint64_t last_block_height; crypto::hash last_block_hash; @@ -1133,6 +1340,10 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_header_by_hash(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HASH::response& res, epee::json_rpc::error& error_resp){ PERF_TIMER(on_get_block_header_by_hash); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "getblockheaderbyhash", req, res, r)) + return r; + crypto::hash block_hash; bool hash_parsed = parse_hash256(req.hash, block_hash); if(!hash_parsed) @@ -1170,6 +1381,10 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_headers_range(const COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request& req, COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response& res, epee::json_rpc::error& error_resp){ PERF_TIMER(on_get_block_headers_range); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "getblockheadersrange", req, res, r)) + return r; + const uint64_t bc_height = m_core.get_current_blockchain_height(); if (req.start_height >= bc_height || req.end_height >= bc_height || req.start_height > req.end_height) { @@ -1216,6 +1431,10 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block_header_by_height(const COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::request& req, COMMAND_RPC_GET_BLOCK_HEADER_BY_HEIGHT::response& res, epee::json_rpc::error& error_resp){ PERF_TIMER(on_get_block_header_by_height); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "getblockheaderbyheight", req, res, r)) + return r; + if(m_core.get_current_blockchain_height() <= req.height) { error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; @@ -1244,6 +1463,10 @@ namespace cryptonote //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_block(const COMMAND_RPC_GET_BLOCK::request& req, COMMAND_RPC_GET_BLOCK::response& res, epee::json_rpc::error& error_resp){ PERF_TIMER(on_get_block); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "getblock", req, res, r)) + return r; + crypto::hash block_hash; if (!req.hash.empty()) { @@ -1313,6 +1536,16 @@ namespace cryptonote bool core_rpc_server::on_get_info_json(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_get_info_json); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_info", req, res, r)) + { + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + crypto::hash top_hash; + m_core.get_blockchain_top(res.height_without_bootstrap, top_hash); + ++res.height_without_bootstrap; // turn top block height into blockchain height + res.was_bootstrap_ever_used = true; + return r; + } crypto::hash top_hash; m_core.get_blockchain_top(res.height, top_hash); @@ -1337,12 +1570,21 @@ namespace cryptonote res.start_time = (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits::max() : m_core.get_free_space(); res.offline = m_core.offline(); + res.bootstrap_daemon_address = m_bootstrap_daemon_address; + res.height_without_bootstrap = res.height; + { + boost::shared_lock lock(m_bootstrap_daemon_mutex); + res.was_bootstrap_ever_used = m_was_bootstrap_ever_used; + } return true; } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_hard_fork_info(const COMMAND_RPC_HARD_FORK_INFO::request& req, COMMAND_RPC_HARD_FORK_INFO::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_hard_fork_info); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "hard_fork_info", req, res, r)) + return r; const Blockchain &blockchain = m_core.get_blockchain_storage(); uint8_t version = req.version > 0 ? req.version : blockchain.get_next_hard_fork_version(); @@ -1459,6 +1701,9 @@ namespace cryptonote bool core_rpc_server::on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_get_output_histogram); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_output_histogram", req, res, r)) + return r; std::map> histogram; try @@ -1486,6 +1731,10 @@ namespace cryptonote bool core_rpc_server::on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_get_version); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_version", req, res, r)) + return r; + res.version = CORE_RPC_VERSION; res.status = CORE_RPC_STATUS_OK; return true; @@ -1504,6 +1753,10 @@ namespace cryptonote bool core_rpc_server::on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_get_per_kb_fee_estimate); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r)) + return r; + res.fee = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.grace_blocks); res.status = CORE_RPC_STATUS_OK; return true; @@ -1531,6 +1784,10 @@ namespace cryptonote bool core_rpc_server::on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res) { PERF_TIMER(on_get_limit); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON, "/get_limit", req, res, r)) + return r; + res.limit_down = epee::net_utils::connection_basic::get_rate_down_limit(); res.limit_up = epee::net_utils::connection_basic::get_rate_up_limit(); res.status = CORE_RPC_STATUS_OK; @@ -1771,6 +2028,9 @@ namespace cryptonote bool core_rpc_server::on_get_txpool_backlog(const COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::request& req, COMMAND_RPC_GET_TRANSACTION_POOL_BACKLOG::response& res, epee::json_rpc::error& error_resp) { PERF_TIMER(on_get_txpool_backlog); + bool r; + if (use_bootstrap_daemon_if_necessary(invoke_http_mode::JON_RPC, "get_txpool_backlog", req, res, r)) + return r; if (!m_core.get_txpool_backlog(res.backlog)) { @@ -1813,4 +2073,16 @@ namespace cryptonote , "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls" , false }; + + const command_line::arg_descriptor core_rpc_server::arg_bootstrap_daemon_address = { + "bootstrap-daemon-address" + , "URL of a 'bootstrap' remote daemon that the connected wallets can use while this daemon is still not fully synced" + , "" + }; + + const command_line::arg_descriptor core_rpc_server::arg_bootstrap_daemon_login = { + "bootstrap-daemon-login" + , "Specify username:password for the bootstrap daemon login" + , "" + }; } // namespace cryptonote diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index bf4371a4e0..ed97365b29 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -34,6 +34,7 @@ #include #include "net/http_server_impl_base.h" +#include "net/http_client.h" #include "core_rpc_server_commands_defs.h" #include "cryptonote_core/cryptonote_core.h" #include "p2p/net_node.h" @@ -57,6 +58,8 @@ namespace cryptonote static const command_line::arg_descriptor arg_testnet_rpc_bind_port; static const command_line::arg_descriptor arg_testnet_rpc_restricted_bind_port; static const command_line::arg_descriptor arg_restricted_rpc; + static const command_line::arg_descriptor arg_bootstrap_daemon_address; + static const command_line::arg_descriptor arg_bootstrap_daemon_login; typedef epee::net_utils::connection_context_base connection_context; @@ -152,7 +155,7 @@ namespace cryptonote bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res); bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res); bool on_get_random_rct_outs(const COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::request& req, COMMAND_RPC_GET_RANDOM_RCT_OUTPUTS::response& res); - bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); + bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res); bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res); bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res); @@ -202,9 +205,18 @@ namespace cryptonote //utils uint64_t get_block_reward(const block& blk); bool fill_block_header_response(const block& blk, bool orphan_status, uint64_t height, const crypto::hash& hash, block_header_response& response); + enum invoke_http_mode { JON, BIN, JON_RPC }; + template + bool use_bootstrap_daemon_if_necessary(const invoke_http_mode &mode, const std::string &command_name, const typename COMMAND_TYPE::request& req, typename COMMAND_TYPE::response& res, bool &r); core& m_core; nodetool::node_server >& m_p2p; + std::string m_bootstrap_daemon_address; + epee::net_utils::http::http_simple_client m_http_client; + boost::shared_mutex m_bootstrap_daemon_mutex; + bool m_should_use_bootstrap_daemon; + std::chrono::system_clock::time_point m_bootstrap_height_check_time; + bool m_was_bootstrap_ever_used; bool m_testnet; bool m_restricted; }; diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 2230141d56..9797917e70 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -65,10 +65,12 @@ namespace cryptonote { uint64_t height; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(height) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -113,6 +115,7 @@ namespace cryptonote uint64_t current_height; std::string status; std::vector output_indices; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blocks) @@ -120,6 +123,7 @@ namespace cryptonote KV_SERIALIZE(current_height) KV_SERIALIZE(status) KV_SERIALIZE(output_indices) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -138,10 +142,12 @@ namespace cryptonote { std::vector blocks; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blocks) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -158,10 +164,12 @@ namespace cryptonote { std::vector blks_hashes; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(blks_hashes) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -184,12 +192,14 @@ namespace cryptonote uint64_t start_height; uint64_t current_height; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) KV_SERIALIZE(start_height) KV_SERIALIZE(current_height) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -595,6 +605,7 @@ namespace cryptonote // new style std::vector txs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txs_as_hex) @@ -602,6 +613,7 @@ namespace cryptonote KV_SERIALIZE(txs) KV_SERIALIZE(missed_tx) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -629,10 +641,12 @@ namespace cryptonote { std::vector spent_status; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(spent_status) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -653,9 +667,11 @@ namespace cryptonote { std::vector o_indexes; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(o_indexes) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -695,9 +711,11 @@ namespace cryptonote { std::vector outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -745,10 +763,12 @@ namespace cryptonote { std::vector outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -785,10 +805,12 @@ namespace cryptonote { std::vector outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -817,9 +839,11 @@ namespace cryptonote { std::list outs; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -854,6 +878,7 @@ namespace cryptonote bool overspend; bool fee_too_low; bool not_rct; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -867,6 +892,7 @@ namespace cryptonote KV_SERIALIZE(overspend) KV_SERIALIZE(fee_too_low) KV_SERIALIZE(not_rct) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -929,6 +955,10 @@ namespace cryptonote uint64_t start_time; uint64_t free_space; bool offline; + bool untrusted; + std::string bootstrap_daemon_address; + uint64_t height_without_bootstrap; + bool was_bootstrap_ever_used; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) @@ -951,6 +981,10 @@ namespace cryptonote KV_SERIALIZE(start_time) KV_SERIALIZE(free_space) KV_SERIALIZE(offline) + KV_SERIALIZE(untrusted) + KV_SERIALIZE(bootstrap_daemon_address) + KV_SERIALIZE(height_without_bootstrap) + KV_SERIALIZE(was_bootstrap_ever_used) END_KV_SERIALIZE_MAP() }; }; @@ -1078,6 +1112,7 @@ namespace cryptonote blobdata blocktemplate_blob; blobdata blockhashing_blob; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(difficulty) @@ -1088,6 +1123,7 @@ namespace cryptonote KV_SERIALIZE(blocktemplate_blob) KV_SERIALIZE(blockhashing_blob) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1151,10 +1187,12 @@ namespace cryptonote { std::string status; block_header_response block_header; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1175,10 +1213,12 @@ namespace cryptonote { std::string status; block_header_response block_header; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1199,10 +1239,12 @@ namespace cryptonote { std::string status; block_header_response block_header; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1229,6 +1271,7 @@ namespace cryptonote std::vector tx_hashes; std::string blob; std::string json; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(block_header) @@ -1237,6 +1280,7 @@ namespace cryptonote KV_SERIALIZE(status) KV_SERIALIZE(blob) KV_SERIALIZE(json) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; @@ -1413,11 +1457,13 @@ namespace cryptonote std::string status; std::vector transactions; std::vector spent_key_images; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(transactions) KV_SERIALIZE(spent_key_images) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1434,10 +1480,12 @@ namespace cryptonote { std::string status; std::vector tx_hashes; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(tx_hashes) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1461,10 +1509,12 @@ namespace cryptonote { std::string status; std::vector backlog; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE_CONTAINER_POD_AS_BLOB(backlog) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1525,10 +1575,12 @@ namespace cryptonote { std::string status; txpool_stats pool_stats; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(pool_stats) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1571,10 +1623,12 @@ namespace cryptonote { std::string status; std::vector headers; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(headers) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1628,11 +1682,13 @@ namespace cryptonote std::string status; uint64_t limit_up; uint64_t limit_down; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(limit_up) KV_SERIALIZE(limit_down) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1742,6 +1798,7 @@ namespace cryptonote uint32_t state; uint64_t earliest_height; std::string status; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(version) @@ -1753,6 +1810,7 @@ namespace cryptonote KV_SERIALIZE(state) KV_SERIALIZE(earliest_height) KV_SERIALIZE(status) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1889,10 +1947,12 @@ namespace cryptonote { std::string status; std::vector histogram; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(histogram) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1909,10 +1969,12 @@ namespace cryptonote { std::string status; uint32_t version; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(version) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; @@ -1959,10 +2021,12 @@ namespace cryptonote { std::string status; uint64_t fee; + bool untrusted; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) KV_SERIALIZE(fee) + KV_SERIALIZE(untrusted) END_KV_SERIALIZE_MAP() }; }; From 2c6a45c161ddd2d63dec4568e27ea61a2b1c8ea1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 7 Dec 2017 13:27:11 +0000 Subject: [PATCH 15/47] Add a chacha20 variant to go with chacha8 /monero#2895 --- src/crypto/CMakeLists.txt | 4 +- src/crypto/{chacha8.c => chacha.c} | 16 ++++++-- src/crypto/{chacha8.h => chacha.h} | 29 ++++++++------ src/serialization/crypto.h | 4 +- src/wallet/wallet2.cpp | 42 ++++++++++---------- src/wallet/wallet2.h | 8 ++-- tests/unit_tests/CMakeLists.txt | 2 +- tests/unit_tests/{chacha8.cpp => chacha.cpp} | 2 +- tests/unit_tests/serialization.cpp | 8 ++-- 9 files changed, 65 insertions(+), 50 deletions(-) rename src/crypto/{chacha8.c => chacha.c} (91%) rename src/crypto/{chacha8.h => chacha.h} (68%) rename tests/unit_tests/{chacha8.cpp => chacha.cpp} (99%) diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt index 7902ddc6bd..764b30273b 100644 --- a/src/crypto/CMakeLists.txt +++ b/src/crypto/CMakeLists.txt @@ -29,7 +29,7 @@ set(crypto_sources aesb.c blake256.c - chacha8.c + chacha.c crypto-ops-data.c crypto-ops.c crypto.cpp @@ -51,7 +51,7 @@ set(crypto_headers) set(crypto_private_headers blake256.h - chacha8.h + chacha.h crypto-ops.h crypto.h generic-ops.h diff --git a/src/crypto/chacha8.c b/src/crypto/chacha.c similarity index 91% rename from src/crypto/chacha8.c rename to src/crypto/chacha.c index df135af594..f573083be4 100644 --- a/src/crypto/chacha8.c +++ b/src/crypto/chacha.c @@ -8,7 +8,7 @@ Public domain. #include #include -#include "chacha8.h" +#include "chacha.h" #include "common/int-util.h" #include "warnings.h" @@ -40,7 +40,7 @@ static const char sigma[] = "expand 32-byte k"; DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing) -void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) { +static void chacha(unsigned rounds, const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) { uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; char* ctarget = 0; @@ -89,7 +89,7 @@ void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* x13 = j13; x14 = j14; x15 = j15; - for (i = 8;i > 0;i -= 2) { + for (i = rounds;i > 0;i -= 2) { QUARTERROUND( x0, x4, x8,x12) QUARTERROUND( x1, x5, x9,x13) QUARTERROUND( x2, x6,x10,x14) @@ -168,3 +168,13 @@ void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* data = (uint8_t*)data + 64; } } + +void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) +{ + chacha(8, data, length, key, iv, cipher); +} + +void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) +{ + chacha(20, data, length, key, iv, cipher); +} diff --git a/src/crypto/chacha8.h b/src/crypto/chacha.h similarity index 68% rename from src/crypto/chacha8.h rename to src/crypto/chacha.h index dcbe6a933a..a9665030da 100644 --- a/src/crypto/chacha8.h +++ b/src/crypto/chacha.h @@ -33,8 +33,8 @@ #include #include -#define CHACHA8_KEY_SIZE 32 -#define CHACHA8_IV_SIZE 8 +#define CHACHA_KEY_SIZE 32 +#define CHACHA_IV_SIZE 8 #if defined(__cplusplus) #include @@ -46,33 +46,38 @@ namespace crypto { extern "C" { #endif void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); + void chacha20(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); #if defined(__cplusplus) } - using chacha8_key = tools::scrubbed_arr; + using chacha_key = tools::scrubbed_arr; #pragma pack(push, 1) - // MS VC 2012 doesn't interpret `class chacha8_iv` as POD in spite of [9.0.10], so it is a struct - struct chacha8_iv { - uint8_t data[CHACHA8_IV_SIZE]; + // MS VC 2012 doesn't interpret `class chacha_iv` as POD in spite of [9.0.10], so it is a struct + struct chacha_iv { + uint8_t data[CHACHA_IV_SIZE]; }; #pragma pack(pop) - static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size"); + static_assert(sizeof(chacha_key) == CHACHA_KEY_SIZE && sizeof(chacha_iv) == CHACHA_IV_SIZE, "Invalid structure size"); - inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { + inline void chacha8(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) { chacha8(data, length, key.data(), reinterpret_cast(&iv), cipher); } - inline void generate_chacha8_key(const void *data, size_t size, chacha8_key& key) { - static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key"); + inline void chacha20(const void* data, std::size_t length, const chacha_key& key, const chacha_iv& iv, char* cipher) { + chacha20(data, length, key.data(), reinterpret_cast(&iv), cipher); + } + + inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) { + static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); tools::scrubbed_arr pwd_hash; crypto::cn_slow_hash(data, size, pwd_hash.data()); memcpy(&key, pwd_hash.data(), sizeof(key)); } - inline void generate_chacha8_key(std::string password, chacha8_key& key) { - return generate_chacha8_key(password.data(), password.size(), key); + inline void generate_chacha_key(std::string password, chacha_key& key) { + return generate_chacha_key(password.data(), password.size(), key); } } diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h index 4213f2e58a..8083bdeb12 100644 --- a/src/serialization/crypto.h +++ b/src/serialization/crypto.h @@ -34,7 +34,7 @@ #include "serialization.h" #include "debug_archive.h" -#include "crypto/chacha8.h" +#include "crypto/chacha.h" #include "crypto/crypto.h" #include "crypto/hash.h" @@ -77,7 +77,7 @@ bool do_serialize(Archive &ar, std::vector &v) return true; } -BLOB_SERIALIZER(crypto::chacha8_iv); +BLOB_SERIALIZER(crypto::chacha_iv); BLOB_SERIALIZER(crypto::hash); BLOB_SERIALIZER(crypto::hash8); BLOB_SERIALIZER(crypto::public_key); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b7c5087472..c018653f3d 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2374,11 +2374,11 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable account_data = buffer.GetString(); // Encrypt the entire JSON object. - crypto::chacha8_key key; - crypto::generate_chacha8_key(password.data(), password.size(), key); + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key); std::string cipher; cipher.resize(account_data.size()); - keys_file_data.iv = crypto::rand(); + keys_file_data.iv = crypto::rand(); crypto::chacha8(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); keys_file_data.account_data = cipher; @@ -2415,8 +2415,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ // Decrypt the contents r = ::serialization::parse_binary(buf, keys_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); - crypto::chacha8_key key; - crypto::generate_chacha8_key(password.data(), password.size(), key); + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key); std::string account_data; account_data.resize(keys_file_data.account_data.size()); crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); @@ -2600,8 +2600,8 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip // Decrypt the contents r = ::serialization::parse_binary(buf, keys_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + keys_file_name + '\"'); - crypto::chacha8_key key; - crypto::generate_chacha8_key(password.data(), password.size(), key); + crypto::chacha_key key; + crypto::generate_chacha_key(password.data(), password.size(), key); std::string account_data; account_data.resize(keys_file_data.account_data.size()); crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); @@ -3293,7 +3293,7 @@ bool wallet2::check_connection(uint32_t *version, uint32_t timeout) return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const +bool wallet2::generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const { const account_keys &keys = m_account.get_keys(); const crypto::secret_key &view_key = keys.m_view_secret_key; @@ -3302,7 +3302,7 @@ bool wallet2::generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) co memcpy(data.data(), &view_key, sizeof(view_key)); memcpy(data.data() + sizeof(view_key), &spend_key, sizeof(spend_key)); data[sizeof(data) - 1] = CHACHA8_KEY_TAIL; - crypto::generate_chacha8_key(data.data(), sizeof(data), key); + crypto::generate_chacha_key(data.data(), sizeof(data), key); return true; } //---------------------------------------------------------------------------------------------------- @@ -3342,8 +3342,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass r = ::serialization::parse_binary(buf, cache_file_data); THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "internal error: failed to deserialize \"" + m_wallet_file + '\"'); - crypto::chacha8_key key; - generate_chacha8_key_from_secret_keys(key); + crypto::chacha_key key; + generate_chacha_key_from_secret_keys(key); std::string cache_data; cache_data.resize(cache_file_data.cache_data.size()); crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); @@ -3501,11 +3501,11 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas wallet2::cache_file_data cache_file_data = boost::value_initialized(); cache_file_data.cache_data = oss.str(); - crypto::chacha8_key key; - generate_chacha8_key_from_secret_keys(key); + crypto::chacha_key key; + generate_chacha_key_from_secret_keys(key); std::string cipher; cipher.resize(cache_file_data.cache_data.size()); - cache_file_data.iv = crypto::rand(); + cache_file_data.iv = crypto::rand(); crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]); cache_file_data.cache_data = cipher; @@ -8667,10 +8667,10 @@ size_t wallet2::import_multisig(std::vector blobs) //---------------------------------------------------------------------------------------------------- std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_key &skey, bool authenticated) const { - crypto::chacha8_key key; - crypto::generate_chacha8_key(&skey, sizeof(skey), key); + crypto::chacha_key key; + crypto::generate_chacha_key(&skey, sizeof(skey), key); std::string ciphertext; - crypto::chacha8_iv iv = crypto::rand(); + crypto::chacha_iv iv = crypto::rand(); ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0)); crypto::chacha8(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); memcpy(&ciphertext[0], &iv, sizeof(iv)); @@ -8693,13 +8693,13 @@ std::string wallet2::encrypt_with_view_secret_key(const std::string &plaintext, //---------------------------------------------------------------------------------------------------- std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) const { - const size_t prefix_size = sizeof(chacha8_iv) + (authenticated ? sizeof(crypto::signature) : 0); + const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0); THROW_WALLET_EXCEPTION_IF(ciphertext.size() < prefix_size, error::wallet_internal_error, "Unexpected ciphertext size"); - crypto::chacha8_key key; - crypto::generate_chacha8_key(&skey, sizeof(skey), key); - const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0]; + crypto::chacha_key key; + crypto::generate_chacha_key(&skey, sizeof(skey), key); + const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0]; std::string plaintext; plaintext.resize(ciphertext.size() - prefix_size); if (authenticated) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 399287c3e9..a02df080d9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -49,7 +49,7 @@ #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_core/cryptonote_tx_utils.h" #include "common/unordered_containers_boost_serialization.h" -#include "crypto/chacha8.h" +#include "crypto/chacha.h" #include "crypto/hash.h" #include "ringct/rctTypes.h" #include "ringct/rctOps.h" @@ -404,7 +404,7 @@ namespace tools struct keys_file_data { - crypto::chacha8_iv iv; + crypto::chacha_iv iv; std::string account_data; BEGIN_SERIALIZE_OBJECT() @@ -415,7 +415,7 @@ namespace tools struct cache_file_data { - crypto::chacha8_iv iv; + crypto::chacha_iv iv; std::string cache_data; BEGIN_SERIALIZE_OBJECT() @@ -975,7 +975,7 @@ namespace tools void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t amount_in, const std::vector &dests, const crypto::hash &payment_id, uint64_t change_amount, uint32_t subaddr_account, const std::set& subaddr_indices); void generate_genesis(cryptonote::block& b); void check_genesis(const crypto::hash& genesis_hash) const; //throws - bool generate_chacha8_key_from_secret_keys(crypto::chacha8_key &key) const; + bool generate_chacha_key_from_secret_keys(crypto::chacha_key &key) const; crypto::hash get_payment_id(const pending_tx &ptx) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index cfacd5688e..ba3acef0c6 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -36,7 +36,7 @@ set(unit_tests_sources block_reward.cpp bulletproofs.cpp canonical_amounts.cpp - chacha8.cpp + chacha.cpp checkpoints.cpp command_line.cpp crypto.cpp diff --git a/tests/unit_tests/chacha8.cpp b/tests/unit_tests/chacha.cpp similarity index 99% rename from tests/unit_tests/chacha8.cpp rename to tests/unit_tests/chacha.cpp index bf0699eba3..eadebf9d8c 100644 --- a/tests/unit_tests/chacha8.cpp +++ b/tests/unit_tests/chacha.cpp @@ -32,7 +32,7 @@ #include "gtest/gtest.h" -#include "crypto/chacha8.h" +#include "crypto/chacha.h" namespace { diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 9e76efadfe..2ef1097da2 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -802,12 +802,12 @@ TEST(Serialization, portability_outputs) // decrypt (copied from wallet2::decrypt) auto decrypt = [] (const std::string &ciphertext, const crypto::secret_key &skey, bool authenticated) -> string { - const size_t prefix_size = sizeof(chacha8_iv) + (authenticated ? sizeof(crypto::signature) : 0); + const size_t prefix_size = sizeof(chacha_iv) + (authenticated ? sizeof(crypto::signature) : 0); if(ciphertext.size() < prefix_size) return {}; - crypto::chacha8_key key; - crypto::generate_chacha8_key(&skey, sizeof(skey), key); - const crypto::chacha8_iv &iv = *(const crypto::chacha8_iv*)&ciphertext[0]; + crypto::chacha_key key; + crypto::generate_chacha_key(&skey, sizeof(skey), key); + const crypto::chacha_iv &iv = *(const crypto::chacha_iv*)&ciphertext[0]; std::string plaintext; plaintext.resize(ciphertext.size() - prefix_size); if (authenticated) From 42a1c1241da163a522c2c6d4a49ba91a5329d22b Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 7 Dec 2017 19:17:32 +0000 Subject: [PATCH 16/47] wallet2: switch to chacha20 instead of chacha8 /monero#2895 Wallet caches and keys files are loaded with chacha8 as needed, but only saved with chacha20. Other data (eg, cold wallet data files, etc) will be incompatible. --- src/wallet/wallet2.cpp | 55 +++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c018653f3d..a8725bff67 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -2379,7 +2379,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable std::string cipher; cipher.resize(account_data.size()); keys_file_data.iv = crypto::rand(); - crypto::chacha8(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); + crypto::chacha20(account_data.data(), account_data.size(), key, keys_file_data.iv, &cipher[0]); keys_file_data.account_data = cipher; std::string buf; @@ -2407,6 +2407,7 @@ namespace */ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_string& password) { + rapidjson::Document json; wallet2::keys_file_data keys_file_data; std::string buf; bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); @@ -2419,10 +2420,11 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ crypto::generate_chacha_key(password.data(), password.size(), key); std::string account_data; account_data.resize(keys_file_data.account_data.size()); - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); // The contents should be JSON if the wallet follows the new format. - rapidjson::Document json; if (json.Parse(account_data.c_str()).HasParseError()) { is_old_file_format = true; @@ -2592,6 +2594,7 @@ bool wallet2::verify_password(const epee::wipeable_string& password) const */ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key) { + rapidjson::Document json; wallet2::keys_file_data keys_file_data; std::string buf; bool r = epee::file_io_utils::load_file_to_string(keys_file_name, buf); @@ -2604,10 +2607,11 @@ bool wallet2::verify_password(const std::string& keys_file_name, const epee::wip crypto::generate_chacha_key(password.data(), password.size(), key); std::string account_data; account_data.resize(keys_file_data.account_data.size()); - crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + crypto::chacha20(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); + if (json.Parse(account_data.c_str()).HasParseError() || !json.IsObject()) + crypto::chacha8(keys_file_data.account_data.data(), keys_file_data.account_data.size(), key, keys_file_data.iv, &account_data[0]); // The contents should be JSON if the wallet follows the new format. - rapidjson::Document json; if (json.Parse(account_data.c_str()).HasParseError()) { // old format before JSON wallet key file format @@ -3346,30 +3350,42 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass generate_chacha_key_from_secret_keys(key); std::string cache_data; cache_data.resize(cache_file_data.cache_data.size()); - crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); + crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); - std::stringstream iss; - iss << cache_data; try { + std::stringstream iss; + iss << cache_data; boost::archive::portable_binary_iarchive ar(iss); ar >> *this; } catch (...) { - LOG_PRINT_L0("Failed to open portable binary, trying unportable"); - boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); - iss.str(""); - iss << cache_data; - boost::archive::binary_iarchive ar(iss); - ar >> *this; + crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cache_data[0]); + try + { + std::stringstream iss; + iss << cache_data; + boost::archive::portable_binary_iarchive ar(iss); + ar >> *this; + } + catch (...) + { + LOG_PRINT_L0("Failed to open portable binary, trying unportable"); + boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); + std::stringstream iss; + iss.str(""); + iss << cache_data; + boost::archive::binary_iarchive ar(iss); + ar >> *this; + } } } catch (...) { LOG_PRINT_L1("Failed to load encrypted cache, trying unencrypted"); - std::stringstream iss; - iss << buf; try { + std::stringstream iss; + iss << buf; boost::archive::portable_binary_iarchive ar(iss); ar >> *this; } @@ -3377,6 +3393,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass { LOG_PRINT_L0("Failed to open portable binary, trying unportable"); boost::filesystem::copy_file(m_wallet_file, m_wallet_file + ".unportable", boost::filesystem::copy_option::overwrite_if_exists); + std::stringstream iss; iss.str(""); iss << buf; boost::archive::binary_iarchive ar(iss); @@ -3506,7 +3523,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas std::string cipher; cipher.resize(cache_file_data.cache_data.size()); cache_file_data.iv = crypto::rand(); - crypto::chacha8(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]); + crypto::chacha20(cache_file_data.cache_data.data(), cache_file_data.cache_data.size(), key, cache_file_data.iv, &cipher[0]); cache_file_data.cache_data = cipher; const std::string new_file = same_file ? m_wallet_file + ".new" : path; @@ -8672,7 +8689,7 @@ std::string wallet2::encrypt(const std::string &plaintext, const crypto::secret_ std::string ciphertext; crypto::chacha_iv iv = crypto::rand(); ciphertext.resize(plaintext.size() + sizeof(iv) + (authenticated ? sizeof(crypto::signature) : 0)); - crypto::chacha8(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); + crypto::chacha20(plaintext.data(), plaintext.size(), key, iv, &ciphertext[sizeof(iv)]); memcpy(&ciphertext[0], &iv, sizeof(iv)); if (authenticated) { @@ -8712,7 +8729,7 @@ std::string wallet2::decrypt(const std::string &ciphertext, const crypto::secret THROW_WALLET_EXCEPTION_IF(!crypto::check_signature(hash, pkey, signature), error::wallet_internal_error, "Failed to authenticate ciphertext"); } - crypto::chacha8(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]); + crypto::chacha20(ciphertext.data() + sizeof(iv), ciphertext.size() - prefix_size, key, iv, &plaintext[0]); return plaintext; } //---------------------------------------------------------------------------------------------------- From 0f7b46c49189b6ec80ce0b9908eab7efd53996e0 Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Tue, 19 Dec 2017 20:30:02 -0500 Subject: [PATCH 17/47] Remove is_pod trait, and replace with is_standard_layout requirement /monero#2969 --- contrib/epee/include/span.h | 2 +- contrib/epee/include/string_tools.h | 9 ++++++++- src/common/memwipe.h | 16 ++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/contrib/epee/include/span.h b/contrib/epee/include/span.h index 5660c09d11..ba44f8f79f 100644 --- a/contrib/epee/include/span.h +++ b/contrib/epee/include/span.h @@ -108,7 +108,7 @@ namespace epee template constexpr bool has_padding() noexcept { - return !std::is_pod::value || alignof(T) != 1; + return !std::is_standard_layout() || alignof(T) != 1; } //! \return Cast data from `src` as `span`. diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h index 307323aa13..3889fd34c9 100644 --- a/contrib/epee/include/string_tools.h +++ b/contrib/epee/include/string_tools.h @@ -45,6 +45,7 @@ #include #include #include "hex.h" +#include "memwipe.h" #include "span.h" #include "warnings.h" @@ -330,7 +331,7 @@ POP_WARNINGS template std::string pod_to_hex(const t_pod_type& s) { - static_assert(std::is_pod::value, "expected pod type"); + static_assert(std::is_standard_layout(), "expected standard layout type"); return to_hex::string(as_byte_span(s)); } //---------------------------------------------------------------------------- @@ -351,6 +352,12 @@ POP_WARNINGS return true; } //---------------------------------------------------------------------------- + template + bool hex_to_pod(const std::string& hex_str, tools::scrubbed& s) + { + return hex_to_pod(hex_str, unwrap(s)); + } + //---------------------------------------------------------------------------- bool validate_hex(uint64_t length, const std::string& str); //---------------------------------------------------------------------------- inline std::string get_extension(const std::string& str) diff --git a/src/common/memwipe.h b/src/common/memwipe.h index c3b4ce8ab3..3864b3ebff 100644 --- a/src/common/memwipe.h +++ b/src/common/memwipe.h @@ -67,18 +67,14 @@ namespace tools { } }; + template + T& unwrap(scrubbed& src) { return src; } + + template + const T& unwrap(scrubbed const& src) { return src; } + template using scrubbed_arr = scrubbed>; } // namespace tools -// Partial specialization for std::is_pod> so that it can -// pretend to be the containted type in those contexts. -namespace std -{ - template - struct is_pod> { - static const bool value = is_pod::value; - }; -} - #endif // __cplusplus From 52c15d6c3feb5cc83b82e68c7f0dcf35c2893dd3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 21 Dec 2017 11:45:01 +0000 Subject: [PATCH 18/47] epee: remove dependency on common /monero#2985 --- contrib/epee/include/net/http_auth.h | 7 ++++-- .../epee/include/net/http_protocol_handler.h | 3 ++- .../epee/include/net/http_server_impl_base.h | 3 ++- contrib/epee/include/net/network_throttle.hpp | 3 --- contrib/epee/src/CMakeLists.txt | 1 - contrib/epee/src/connection_basic.cpp | 1 - contrib/epee/src/http_auth.cpp | 7 +++--- src/common/util.cpp | 1 + src/rpc/core_rpc_server.cpp | 3 ++- src/wallet/wallet_rpc_server.cpp | 3 ++- tests/unit_tests/http.cpp | 22 ++++++++++++------- 11 files changed, 31 insertions(+), 23 deletions(-) diff --git a/contrib/epee/include/net/http_auth.h b/contrib/epee/include/net/http_auth.h index 841cebc170..71f56b570d 100644 --- a/contrib/epee/include/net/http_auth.h +++ b/contrib/epee/include/net/http_auth.h @@ -71,8 +71,8 @@ namespace net_utils std::uint32_t counter; }; - http_server_auth() : user() {} - http_server_auth(login credentials); + http_server_auth() : user(), rng() {} + http_server_auth(login credentials, std::function r); //! \return Auth response, or `boost::none` iff `request` had valid auth. boost::optional get_response(const http_request_info& request) @@ -81,10 +81,13 @@ namespace net_utils return do_get_response(request); return boost::none; } + private: boost::optional do_get_response(const http_request_info& request); boost::optional user; + + std::function rng; }; //! Implements RFC 2617 digest auth. Digests from RFC 7616 can be added. diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h index 652d8ff6f5..b4485d1cd5 100644 --- a/contrib/epee/include/net/http_protocol_handler.h +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -160,6 +160,7 @@ namespace net_utils struct custum_handler_config: public http_server_config { i_http_server_handler* m_phandler; + std::function rng; }; /************************************************************************/ @@ -176,7 +177,7 @@ namespace net_utils : simple_http_connection_handler(psnd_hndlr, config), m_config(config), m_conn_context(conn_context), - m_auth(m_config.m_user ? http_server_auth{*m_config.m_user} : http_server_auth{}) + m_auth(m_config.m_user ? http_server_auth{*m_config.m_user, config.rng} : http_server_auth{}) {} inline bool handle_request(const http_request_info& query_info, http_response_info& response) { diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h index 8b8e31b514..1a97e610a6 100644 --- a/contrib/epee/include/net/http_server_impl_base.h +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -55,13 +55,14 @@ namespace epee : m_net_server(external_io_service) {} - bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", + bool init(std::function rng, const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0", std::vector access_control_origins = std::vector(), boost::optional user = boost::none) { //set self as callback handler m_net_server.get_config_object().m_phandler = static_cast(this); + m_net_server.get_config_object().rng = std::move(rng); //here set folder for hosting reqests m_net_server.get_config_object().m_folder = ""; diff --git a/contrib/epee/include/net/network_throttle.hpp b/contrib/epee/include/net/network_throttle.hpp index 464b347263..f6d06a1946 100644 --- a/contrib/epee/include/net/network_throttle.hpp +++ b/contrib/epee/include/net/network_throttle.hpp @@ -99,8 +99,6 @@ struct calculate_times_struct { typedef calculate_times_struct calculate_times_struct; -namespace cryptonote { class cryptonote_protocol_handler_base; } // a friend class // TODO friend not working - /*** @brief Access to simple throttles, with singlton to access global network limits */ @@ -117,7 +115,6 @@ class network_throttle_manager { static boost::mutex m_lock_get_global_throttle_inreq; static boost::mutex m_lock_get_global_throttle_out; - friend class cryptonote::cryptonote_protocol_handler_base; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS! friend class connection_basic; // FRIEND - to directly access global throttle-s. !! REMEMBER TO USE LOCKS! friend class connection_basic_pimpl; // ditto diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index b6967e8fce..ee118724da 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -49,7 +49,6 @@ endif() target_link_libraries(epee PUBLIC - cncrypto easylogging ${Boost_FILESYSTEM_LIBRARY} PRIVATE diff --git a/contrib/epee/src/connection_basic.cpp b/contrib/epee/src/connection_basic.cpp index 534044a79d..5848d1268d 100644 --- a/contrib/epee/src/connection_basic.cpp +++ b/contrib/epee/src/connection_basic.cpp @@ -78,7 +78,6 @@ // TODO: #include "net/network_throttle-detail.hpp" -#include "cryptonote_core/cryptonote_core.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.p2p" diff --git a/contrib/epee/src/http_auth.cpp b/contrib/epee/src/http_auth.cpp index f06f055287..5b8d892ffd 100644 --- a/contrib/epee/src/http_auth.cpp +++ b/contrib/epee/src/http_auth.cpp @@ -66,7 +66,6 @@ #include #include -#include "crypto/crypto.h" #include "hex.h" #include "md5_l.h" #include "string_coding.h" @@ -711,8 +710,8 @@ namespace epee { namespace http { - http_server_auth::http_server_auth(login credentials) - : user(session{std::move(credentials)}) { + http_server_auth::http_server_auth(login credentials, std::function r) + : user(session{std::move(credentials)}), rng(std::move(r)) { } boost::optional http_server_auth::do_get_response(const http_request_info& request) @@ -746,7 +745,7 @@ namespace epee user->counter = 0; { std::array rand_128bit{{}}; - crypto::rand(rand_128bit.size(), rand_128bit.data()); + rng(rand_128bit.size(), rand_128bit.data()); user->nonce = string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()); } return create_digest_response(user->nonce, is_stale); diff --git a/src/common/util.cpp b/src/common/util.cpp index d9e12ffe46..a73deafff7 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -39,6 +39,7 @@ #include "wipeable_string.h" using namespace epee; +#include "crypto/crypto.h" #include "util.h" #include "memwipe.h" #include "cryptonote_config.h" diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index c7179d0f33..a53a6d163e 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -133,8 +133,9 @@ namespace cryptonote if (rpc_config->login) http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password()); + auto rng = [](size_t len, uint8_t *ptr){ return crypto::rand(len, ptr); }; return epee::http_server_impl_base::init( - std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login) + rng, std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login) ); } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 0482b9dd63..302691581c 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -229,8 +229,9 @@ namespace tools m_http_client.set_server(walvars->get_daemon_address(), walvars->get_daemon_login()); m_net_server.set_threads_prefix("RPC"); + auto rng = [](size_t len, uint8_t *ptr) { return crypto::rand(len, ptr); }; return epee::http_server_impl_base::init( - std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login) + rng, std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login) ); } //------------------------------------------------------------------------------------------------------------------------------ diff --git a/tests/unit_tests/http.cpp b/tests/unit_tests/http.cpp index 5e427f0643..0e8f9f7479 100644 --- a/tests/unit_tests/http.cpp +++ b/tests/unit_tests/http.cpp @@ -60,12 +60,18 @@ #include "md5_l.h" #include "string_tools.h" +#include "crypto/crypto.h" namespace { namespace http = epee::net_utils::http; using fields = std::unordered_map; using auth_responses = std::vector; +void rng(size_t len, uint8_t *ptr) +{ + crypto::rand(len, ptr); +} + std::string quoted(std::string str) { str.insert(str.begin(), '"'); @@ -250,13 +256,13 @@ std::string get_nc(std::uint32_t count) TEST(HTTP_Server_Auth, NotRequired) { - http::http_server_auth auth{}; + http::http_server_auth auth{}; // no rng here EXPECT_FALSE(auth.get_response(http::http_request_info{})); } TEST(HTTP_Server_Auth, MissingAuth) { - http::http_server_auth auth{{"foo", "bar"}}; + http::http_server_auth auth{{"foo", "bar"}, rng}; EXPECT_TRUE(bool(auth.get_response(http::http_request_info{}))); { http::http_request_info request{}; @@ -267,7 +273,7 @@ TEST(HTTP_Server_Auth, MissingAuth) TEST(HTTP_Server_Auth, BadSyntax) { - http::http_server_auth auth{{"foo", "bar"}}; + http::http_server_auth auth{{"foo", "bar"}, rng}; EXPECT_TRUE(bool(auth.get_response(make_request({{u8"algorithm", "fo\xFF"}})))); EXPECT_TRUE(bool(auth.get_response(make_request({{u8"cnonce", "\"000\xFF\""}})))); EXPECT_TRUE(bool(auth.get_response(make_request({{u8"cnonce \xFF =", "\"000\xFF\""}})))); @@ -277,7 +283,7 @@ TEST(HTTP_Server_Auth, BadSyntax) TEST(HTTP_Server_Auth, MD5) { http::login user{"foo", "bar"}; - http::http_server_auth auth{user}; + http::http_server_auth auth{user, rng}; const auto response = auth.get_response(make_request(fields{})); ASSERT_TRUE(bool(response)); @@ -326,7 +332,7 @@ TEST(HTTP_Server_Auth, MD5_sess) constexpr const char cnonce[] = "not a good cnonce"; http::login user{"foo", "bar"}; - http::http_server_auth auth{user}; + http::http_server_auth auth{user, rng}; const auto response = auth.get_response(make_request(fields{})); ASSERT_TRUE(bool(response)); @@ -378,7 +384,7 @@ TEST(HTTP_Server_Auth, MD5_auth) constexpr const char qop[] = "auth"; http::login user{"foo", "bar"}; - http::http_server_auth auth{user}; + http::http_server_auth auth{user, rng}; const auto response = auth.get_response(make_request(fields{})); ASSERT_TRUE(bool(response)); @@ -446,7 +452,7 @@ TEST(HTTP_Server_Auth, MD5_sess_auth) constexpr const char qop[] = "auth"; http::login user{"foo", "bar"}; - http::http_server_auth auth{user}; + http::http_server_auth auth{user, rng}; const auto response = auth.get_response(make_request(fields{})); ASSERT_TRUE(bool(response)); @@ -523,7 +529,7 @@ TEST(HTTP_Auth, DogFood) const http::login user{"some_user", "ultimate password"}; - http::http_server_auth server{user}; + http::http_server_auth server{user, rng}; http::http_client_auth client{user}; http::http_request_info request{}; From 279b70756d5b6986f696f8a5c117b1387f3a2b96 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 21 Dec 2017 14:51:29 +0000 Subject: [PATCH 19/47] move memwipe to epee to avoid common<->crypto circular dependencies /monero#2985 --- {src/common => contrib/epee/include}/memwipe.h | 0 contrib/epee/src/CMakeLists.txt | 6 +++++- {src/common => contrib/epee/src}/memwipe.c | 0 src/common/CMakeLists.txt | 6 ------ src/common/password.cpp | 2 +- src/crypto/chacha.h | 2 +- src/crypto/crypto.h | 2 +- src/wallet/wallet2.cpp | 2 +- tests/unit_tests/memwipe.cpp | 2 +- 9 files changed, 10 insertions(+), 12 deletions(-) rename {src/common => contrib/epee/include}/memwipe.h (100%) rename {src/common => contrib/epee/src}/memwipe.c (100%) diff --git a/src/common/memwipe.h b/contrib/epee/include/memwipe.h similarity index 100% rename from src/common/memwipe.h rename to contrib/epee/include/memwipe.h diff --git a/contrib/epee/src/CMakeLists.txt b/contrib/epee/src/CMakeLists.txt index ee118724da..9d104ceeb4 100644 --- a/contrib/epee/src/CMakeLists.txt +++ b/contrib/epee/src/CMakeLists.txt @@ -26,12 +26,16 @@ # 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. -add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp +add_library(epee STATIC hex.cpp http_auth.cpp mlog.cpp net_utils_base.cpp string_tools.cpp wipeable_string.cpp memwipe.c connection_basic.cpp network_throttle.cpp network_throttle-detail.cpp) if (USE_READLINE AND GNU_READLINE_FOUND) add_library(epee_readline STATIC readline_buffer.cpp) endif() +if(HAVE_C11) +SET_PROPERTY(SOURCE memwipe.c PROPERTY COMPILE_FLAGS -std=c11) +endif() + # Build and install libepee if we're building for GUI if (BUILD_GUI_DEPS) if(IOS) diff --git a/src/common/memwipe.c b/contrib/epee/src/memwipe.c similarity index 100% rename from src/common/memwipe.c rename to contrib/epee/src/memwipe.c diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 7ad08ea83a..50887e35ca 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -35,7 +35,6 @@ set(common_sources download.cpp util.cpp i18n.cpp - memwipe.c password.cpp perf_timer.cpp threadpool.cpp @@ -64,7 +63,6 @@ set(common_private_headers util.h varint.h i18n.h - memwipe.h password.h perf_timer.h stack_trace.h @@ -92,9 +90,5 @@ target_link_libraries(common ${OPENSSL_LIBRARIES} ${EXTRA_LIBRARIES}) -if(HAVE_C11) -SET_PROPERTY(SOURCE memwipe.c PROPERTY COMPILE_FLAGS -std=c11) -endif() - #monero_install_headers(common # ${common_headers}) diff --git a/src/common/password.cpp b/src/common/password.cpp index dc08561607..0111233000 100644 --- a/src/common/password.cpp +++ b/src/common/password.cpp @@ -46,7 +46,7 @@ #include "readline_buffer.h" #endif -#include "common/memwipe.h" +#include "memwipe.h" namespace { diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index a9665030da..c11e4aa2fd 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -39,7 +39,7 @@ #if defined(__cplusplus) #include -#include "common/memwipe.h" +#include "memwipe.h" #include "hash.h" namespace crypto { diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 0ce5e6d7a9..a929302c1c 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -41,7 +41,7 @@ #include "common/pod-class.h" #include "common/util.h" -#include "common/memwipe.h" +#include "memwipe.h" #include "generic-ops.h" #include "hex.h" #include "span.h" diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a8725bff67..78f724e423 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -62,7 +62,7 @@ using namespace epee; #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" #include "common/json_util.h" -#include "common/memwipe.h" +#include "memwipe.h" #include "common/base58.h" #include "ringct/rctSigs.h" diff --git a/tests/unit_tests/memwipe.cpp b/tests/unit_tests/memwipe.cpp index b2b19fbf58..ebe9a76f73 100644 --- a/tests/unit_tests/memwipe.cpp +++ b/tests/unit_tests/memwipe.cpp @@ -30,7 +30,7 @@ #include #include "misc_log_ex.h" -#include "common/memwipe.h" +#include "memwipe.h" // Probably won't catch the optimized out case, but at least we test // it works in the normal case From e04099c1409ba1d6e60218b0c7de7ea534c1dd1a Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 09:52:16 +0900 Subject: [PATCH 20/47] hardfork: fix get_earliest_ideal_height_for_version() to support non-existent versions /monero#3444 --- src/cryptonote_basic/hardfork.cpp | 12 ++++++++---- tests/unit_tests/hardfork.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 546af20765..6c607592a2 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -379,11 +379,15 @@ uint8_t HardFork::get_ideal_version(uint64_t height) const uint64_t HardFork::get_earliest_ideal_height_for_version(uint8_t version) const { - for (unsigned int n = heights.size() - 1; n > 0; --n) { - if (heights[n].version <= version) - return heights[n].height; + uint64_t height = std::numeric_limits::max(); + for (auto i = heights.rbegin(); i != heights.rend(); ++i) { + if (i->version >= version) { + height = i->height; + } else { + break; + } } - return 0; + return height; } uint8_t HardFork::get_next_version() const diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index c235f49fdd..09e6b95d7e 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -550,3 +550,28 @@ TEST(get, higher) ASSERT_EQ(hf.get_ideal_version(7), 3); } +TEST(get, earliest_ideal_height) +{ + TestDB db; + HardFork hf(db, 1, 0, 1, 1, 4, 50); + + // v h t + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 2, 1)); + ASSERT_TRUE(hf.add_fork(5, 5, 2)); + ASSERT_TRUE(hf.add_fork(6, 10, 3)); + ASSERT_TRUE(hf.add_fork(9, 15, 4)); + hf.init(); + + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(1), 0); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(2), 2); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(3), 5); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(4), 5); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(5), 5); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(6), 10); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(7), 15); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(8), 15); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(9), 15); + ASSERT_EQ(hf.get_earliest_ideal_height_for_version(10), std::numeric_limits::max()); +} + From 311a12d42ebef5800f22f0236d2e14198f6f3c63 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 11:02:41 +0900 Subject: [PATCH 21/47] unit_tests/hardfork: add tests for check_for_height() /monero#3444 --- tests/unit_tests/hardfork.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index 09e6b95d7e..d20353f083 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -228,6 +228,30 @@ TEST(ordering, Success) ASSERT_FALSE(hf.add_fork(5, 5, 4)); } +TEST(check_for_height, Success) +{ + TestDB db; + HardFork hf(db, 1, 0, 0, 0, 1, 0); // no voting + + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 5, 1)); + hf.init(); + + for (uint64_t h = 0; h <= 4; ++h) { + ASSERT_TRUE(hf.check_for_height(mkblock(1, 1), h)); + ASSERT_FALSE(hf.check_for_height(mkblock(2, 2), h)); // block version is too high + db.add_block(mkblock(hf, h, 1), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + for (uint64_t h = 5; h <= 10; ++h) { + ASSERT_FALSE(hf.check_for_height(mkblock(1, 1), h)); // block version is too low + ASSERT_TRUE(hf.check_for_height(mkblock(2, 2), h)); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } +} + TEST(states, Success) { TestDB db; From 85e790a863e80181be8ab1a6bbada181ef09127a Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 11:09:58 +0900 Subject: [PATCH 22/47] hardfork: fix get_next_version() /monero#3444 --- src/cryptonote_basic/hardfork.cpp | 6 +++--- tests/unit_tests/hardfork.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/cryptonote_basic/hardfork.cpp b/src/cryptonote_basic/hardfork.cpp index 6c607592a2..6d97cd20fd 100644 --- a/src/cryptonote_basic/hardfork.cpp +++ b/src/cryptonote_basic/hardfork.cpp @@ -394,9 +394,9 @@ uint8_t HardFork::get_next_version() const { CRITICAL_REGION_LOCAL(lock); uint64_t height = db.height(); - for (unsigned int n = heights.size() - 1; n > 0; --n) { - if (height >= heights[n].height) { - return heights[n < heights.size() - 1 ? n + 1 : n].version; + for (auto i = heights.rbegin(); i != heights.rend(); ++i) { + if (height >= i->height) { + return (i == heights.rbegin() ? i : (i - 1))->version; } } return original_version; diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index d20353f083..bcf855b15c 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -252,6 +252,35 @@ TEST(check_for_height, Success) } } +TEST(get, next_version) +{ + TestDB db; + HardFork hf(db); + + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + ASSERT_TRUE(hf.add_fork(2, 5, 1)); + ASSERT_TRUE(hf.add_fork(4, 10, 2)); + hf.init(); + + for (uint64_t h = 0; h <= 4; ++h) { + ASSERT_EQ(2, hf.get_next_version()); + db.add_block(mkblock(hf, h, 1), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + for (uint64_t h = 5; h <= 9; ++h) { + ASSERT_EQ(4, hf.get_next_version()); + db.add_block(mkblock(hf, h, 2), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } + + for (uint64_t h = 10; h <= 15; ++h) { + ASSERT_EQ(4, hf.get_next_version()); + db.add_block(mkblock(hf, h, 4), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } +} + TEST(states, Success) { TestDB db; From f564131331495a12189e598e8553e0721208835c Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 12:01:09 +0900 Subject: [PATCH 23/47] unit_tests/hardfork: add tests for get_voting_info() /monero#3444 --- tests/unit_tests/hardfork.cpp | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/unit_tests/hardfork.cpp b/tests/unit_tests/hardfork.cpp index bcf855b15c..91a571eadc 100644 --- a/tests/unit_tests/hardfork.cpp +++ b/tests/unit_tests/hardfork.cpp @@ -488,6 +488,55 @@ TEST(voting, different_thresholds) } } +TEST(voting, info) +{ + TestDB db; + HardFork hf(db, 1, 0, 1, 1, 4, 50); // window size 4, default threshold 50% + + // v h ts + ASSERT_TRUE(hf.add_fork(1, 0, 0)); + // v h thr ts + ASSERT_TRUE(hf.add_fork(2, 5, 0, 1)); // asap + ASSERT_TRUE(hf.add_fork(3, 10, 100, 2)); // all votes + // v h ts + ASSERT_TRUE(hf.add_fork(4, 15, 3)); // default 50% votes + hf.init(); + + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + static const uint8_t block_versions[] = { 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4 }; + static const uint8_t expected_versions[] = { 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4 }; + static const uint8_t expected_thresholds[] = { 0, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 2, 2, 2, 2 }; + + for (uint64_t h = 0; h < sizeof(block_versions) / sizeof(block_versions[0]); ++h) { + uint32_t window, votes, threshold; + uint64_t earliest_height; + uint8_t voting; + + ASSERT_TRUE(hf.get_voting_info(1, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h, 4), votes); + ASSERT_EQ(0, earliest_height); + + ASSERT_EQ(hf.get_current_version() >= 2, hf.get_voting_info(2, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h <= 3 ? 0 : h - 3, 4), votes); + ASSERT_EQ(5, earliest_height); + + ASSERT_EQ(hf.get_current_version() >= 3, hf.get_voting_info(3, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h <= 8 ? 0 : h - 8, 4), votes); + ASSERT_EQ(10, earliest_height); + + ASSERT_EQ(hf.get_current_version() == 4, hf.get_voting_info(4, window, votes, threshold, earliest_height, voting)); + ASSERT_EQ(std::min(h <= 14 ? 0 : h - 14, 4), votes); + ASSERT_EQ(15, earliest_height); + + ASSERT_EQ(std::min(h, 4), window); + ASSERT_EQ(expected_thresholds[h], threshold); + ASSERT_EQ(4, voting); + + db.add_block(mkblock(hf, h, block_versions[h]), 0, 0, 0, crypto::hash()); + ASSERT_TRUE(hf.add(db.get_block_from_height(h), h)); + } +} + TEST(new_blocks, denied) { TestDB db; From 3b8bf934670072588041f1eff9a4561e652df350 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 20:30:09 +0900 Subject: [PATCH 24/47] core: add get_earliest_ideal_height_for_version() /monero#3450 --- src/cryptonote_core/blockchain.h | 7 +++++++ src/cryptonote_core/cryptonote_core.cpp | 5 +++++ src/cryptonote_core/cryptonote_core.h | 7 +++++++ tests/core_proxy/core_proxy.h | 1 + tests/unit_tests/ban.cpp | 1 + 5 files changed, 21 insertions(+) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index b76d0555fc..0cc6461d31 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -808,6 +808,13 @@ namespace cryptonote */ uint8_t get_hard_fork_version(uint64_t height) const { return m_hardfork->get(height); } + /** + * @brief returns the earliest block a given version may activate + * + * @return the height + */ + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return m_hardfork->get_earliest_ideal_height_for_version(version); } + /** * @brief get information about hardfork voting for a version * diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 415657f9c3..f3fbf622a3 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1399,6 +1399,11 @@ namespace cryptonote return get_blockchain_storage().get_hard_fork_version(height); } //----------------------------------------------------------------------------------------------- + uint64_t core::get_earliest_ideal_height_for_version(uint8_t version) const + { + return get_blockchain_storage().get_earliest_ideal_height_for_version(version); + } + //----------------------------------------------------------------------------------------------- bool core::check_updates() { static const char software[] = "monero"; diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h index 9f84ed303e..6234a80329 100644 --- a/src/cryptonote_core/cryptonote_core.h +++ b/src/cryptonote_core/cryptonote_core.h @@ -670,6 +670,13 @@ namespace cryptonote */ uint8_t get_hard_fork_version(uint64_t height) const; + /** + * @brief return the earliest block a given version may activate + * + * @return what it says above + */ + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const; + /** * @brief gets start_time * diff --git a/tests/core_proxy/core_proxy.h b/tests/core_proxy/core_proxy.h index 51f0f892cc..45277a62d7 100644 --- a/tests/core_proxy/core_proxy.h +++ b/tests/core_proxy/core_proxy.h @@ -99,6 +99,7 @@ namespace tests bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } diff --git a/tests/unit_tests/ban.cpp b/tests/unit_tests/ban.cpp index 5af5146434..ccc153f2e4 100644 --- a/tests/unit_tests/ban.cpp +++ b/tests/unit_tests/ban.cpp @@ -78,6 +78,7 @@ class test_core bool get_block_by_hash(const crypto::hash &h, cryptonote::block &blk, bool *orphan = NULL) const { return false; } uint8_t get_ideal_hard_fork_version(uint64_t height) const { return 0; } uint8_t get_hard_fork_version(uint64_t height) const { return 0; } + uint64_t get_earliest_ideal_height_for_version(uint8_t version) const { return 0; } cryptonote::difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return 0; } bool fluffy_blocks_enabled() const { return false; } uint64_t prevalidate_block_hashes(uint64_t height, const std::list &hashes) { return 0; } From 7e60452e1f26b14eb0d1a26eeceed6d6167b10b3 Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 7 Feb 2018 18:06:58 +0900 Subject: [PATCH 25/47] Change executable names --- src/blockchain_utilities/CMakeLists.txt | 4 ++-- src/daemon/CMakeLists.txt | 2 +- src/gen_multisig/CMakeLists.txt | 2 +- src/simplewallet/CMakeLists.txt | 2 +- src/wallet/CMakeLists.txt | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index bd32e0c552..b86183f03d 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -92,7 +92,7 @@ endif() set_property(TARGET blockchain_import PROPERTY - OUTPUT_NAME "monero-blockchain-import") + OUTPUT_NAME "aeon-blockchain-import") install(TARGETS blockchain_import DESTINATION bin) monero_add_executable(blockchain_export @@ -114,6 +114,6 @@ target_link_libraries(blockchain_export set_property(TARGET blockchain_export PROPERTY - OUTPUT_NAME "monero-blockchain-export") + OUTPUT_NAME "aeon-blockchain-export") install(TARGETS blockchain_export DESTINATION bin) diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index ad84db4500..19f6acf5b5 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -106,5 +106,5 @@ target_link_libraries(daemon ${EXTRA_LIBRARIES}) set_property(TARGET daemon PROPERTY - OUTPUT_NAME "monerod") + OUTPUT_NAME "aeond") install(TARGETS daemon DESTINATION bin) diff --git a/src/gen_multisig/CMakeLists.txt b/src/gen_multisig/CMakeLists.txt index ff3c468621..563f00679a 100644 --- a/src/gen_multisig/CMakeLists.txt +++ b/src/gen_multisig/CMakeLists.txt @@ -50,5 +50,5 @@ add_dependencies(gen_multisig version) set_property(TARGET gen_multisig PROPERTY - OUTPUT_NAME "monero-gen-trusted-multisig") + OUTPUT_NAME "aeon-gen-trusted-multisig") install(TARGETS gen_multisig DESTINATION bin) diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index beaacf0e95..134e01e0b0 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -60,5 +60,5 @@ target_link_libraries(simplewallet ${EXTRA_LIBRARIES}) set_property(TARGET simplewallet PROPERTY - OUTPUT_NAME "monero-wallet-cli") + OUTPUT_NAME "aeon-wallet-cli") install(TARGETS simplewallet DESTINATION bin) diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 2d664ba15d..61634d6d5c 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -96,7 +96,7 @@ target_link_libraries(wallet_rpc_server ${EXTRA_LIBRARIES}) set_property(TARGET wallet_rpc_server PROPERTY - OUTPUT_NAME "monero-wallet-rpc") + OUTPUT_NAME "aeon-wallet-rpc") install(TARGETS wallet_rpc_server DESTINATION bin) From 4f711db624d6402f5e459bdcf973065051aeb89c Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 27 Feb 2018 14:17:51 +0900 Subject: [PATCH 26/47] Replace string Monero with Aeon for user visible parts and some others, set donation address --- contrib/epee/src/mlog.cpp | 4 +- .../blockchain_export.cpp | 4 +- .../blockchain_import.cpp | 4 +- src/crypto/slow-hash.c | 2 +- src/cryptonote_core/cryptonote_core.cpp | 10 +-- .../cryptonote_protocol_handler.inl | 2 +- src/daemon/command_line_args.h | 2 +- src/daemon/command_server.cpp | 6 +- src/daemon/executor.cpp | 4 +- src/daemon/main.cpp | 6 +- src/daemon/rpc_command_executor.cpp | 4 +- src/daemonizer/posix_fork.cpp | 2 +- src/debug_utilities/cn_deserialize.cpp | 2 +- src/gen_multisig/gen_multisig.cpp | 4 +- src/p2p/net_node.cpp | 2 +- src/rpc/core_rpc_server.cpp | 2 +- src/simplewallet/simplewallet.cpp | 72 +++++++++---------- src/wallet/wallet2.cpp | 12 ++-- src/wallet/wallet_args.cpp | 10 +-- src/wallet/wallet_rpc_server.cpp | 16 ++--- 20 files changed, 85 insertions(+), 85 deletions(-) diff --git a/contrib/epee/src/mlog.cpp b/contrib/epee/src/mlog.cpp index a30efbc6a4..34b53f4c53 100644 --- a/contrib/epee/src/mlog.cpp +++ b/contrib/epee/src/mlog.cpp @@ -120,7 +120,7 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s el::Configurations c; c.setGlobally(el::ConfigurationType::Filename, filename_base); c.setGlobally(el::ConfigurationType::ToFile, "true"); - const char *log_format = getenv("MONERO_LOG_FORMAT"); + const char *log_format = getenv("AEON_LOG_FORMAT"); if (!log_format) log_format = MLOG_BASE_FORMAT; c.setGlobally(el::ConfigurationType::Format, log_format); @@ -138,7 +138,7 @@ void mlog_configure(const std::string &filename_base, bool console, const std::s rename(name, rname.c_str()); }); mlog_set_common_prefix(); - const char *monero_log = getenv("MONERO_LOGS"); + const char *monero_log = getenv("AEON_LOGS"); if (!monero_log) { monero_log = get_default_categories(0); diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index 78bb51ab66..f87e6b6be1 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -99,12 +99,12 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-export.log"), true); + mlog_configure(mlog_get_default_log_path("aeon-blockchain-export.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index 70d1dd6961..9edc8c218a 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -642,7 +642,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } @@ -675,7 +675,7 @@ int main(int argc, char* argv[]) m_config_folder = command_line::get_arg(vm, data_dir_arg); db_arg_str = command_line::get_arg(vm, arg_database); - mlog_configure(mlog_get_default_log_path("monero-blockchain-import.log"), true); + mlog_configure(mlog_get_default_log_path("aeon-blockchain-import.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index cc234713b8..870048f643 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -195,7 +195,7 @@ STATIC INLINE int force_software_aes(void) if (use != -1) return use; - const char *env = getenv("MONERO_USE_SOFTWARE_AES"); + const char *env = getenv("AEON_USE_SOFTWARE_AES"); if (!env) { use = 0; } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index f3fbf622a3..828c5bba83 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -124,7 +124,7 @@ namespace cryptonote }; static const command_line::arg_descriptor arg_check_updates = { "check-updates" - , "Check for new versions of monero: [disabled|notify|download|update]" + , "Check for new versions of Aeon: [disabled|notify|download|update]" , "notify" }; static const command_line::arg_descriptor arg_fluffy_blocks = { @@ -368,8 +368,8 @@ namespace cryptonote if (boost::filesystem::exists(old_files / "blockchain.bin")) { MWARNING("Found old-style blockchain.bin in " << old_files.string()); - MWARNING("Monero now uses a new format. You can either remove blockchain.bin to start syncing"); - MWARNING("the blockchain anew, or use monero-blockchain-export and monero-blockchain-import to"); + MWARNING("Aeon now uses a new format. You can either remove blockchain.bin to start syncing"); + MWARNING("the blockchain anew, or use aeon-blockchain-export and aeon-blockchain-import to"); MWARNING("convert your existing blockchain.bin to the new format. See README.md for instructions."); return false; } @@ -1343,7 +1343,7 @@ namespace cryptonote { std::string main_message; if (m_offline) - main_message = "The daemon is running offline and will not attempt to sync to the Monero network."; + main_message = "The daemon is running offline and will not attempt to sync to the Aeon network."; else main_message = "The daemon will start synchronizing with the network. This may take a long time to complete."; MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL @@ -1406,7 +1406,7 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- bool core::check_updates() { - static const char software[] = "monero"; + static const char software[] = "aeon"; #ifdef BUILD_TAG static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); static const char subdir[] = "cli"; // because it can never be simple diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 389e8ba848..258d3e30f9 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1547,7 +1547,7 @@ skip: if(m_synchronized.compare_exchange_strong(val_expected, true)) { MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL - << "You are now synchronized with the network. You may now start monero-wallet-cli." << ENDL + << "You are now synchronized with the network. You may now start aeon-wallet-cli." << ENDL << ENDL << "Use the \"help\" command to see the list of available commands." << ENDL << "**********************************************************************"); diff --git a/src/daemon/command_line_args.h b/src/daemon/command_line_args.h index 7fa58f9d86..9f6e05e98a 100644 --- a/src/daemon/command_line_args.h +++ b/src/daemon/command_line_args.h @@ -34,7 +34,7 @@ namespace daemon_args { - std::string const WINDOWS_SERVICE_NAME = "Monero Daemon"; + std::string const WINDOWS_SERVICE_NAME = "Aeon Daemon"; const command_line::arg_descriptor arg_config_file = { "config-file" diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index ecf58e22c2..564c978e08 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -199,12 +199,12 @@ t_command_server::t_command_server( m_command_lookup.set_handler( "start_save_graph" , std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1) - , "Start saving data for dr monero." + , "Start saving data for dr aeon." ); m_command_lookup.set_handler( "stop_save_graph" , std::bind(&t_command_parser_executor::stop_save_graph, &m_parser, p::_1) - , "Stop saving data for dr monero." + , "Stop saving data for dr aeon." ); m_command_lookup.set_handler( "hard_fork_info" @@ -323,7 +323,7 @@ bool t_command_server::help(const std::vector& args) std::string t_command_server::get_commands_str() { std::stringstream ss; - ss << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; + ss << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; ss << "Commands: " << std::endl; std::string usage = m_command_lookup.get_usage(); boost::replace_all(usage, "\n", "\n "); diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp index 6130bfa289..310dc580a6 100644 --- a/src/daemon/executor.cpp +++ b/src/daemon/executor.cpp @@ -41,7 +41,7 @@ namespace daemonize { - std::string const t_executor::NAME = "Monero Daemon"; + std::string const t_executor::NAME = "Aeon Daemon"; void t_executor::init_options( boost::program_options::options_description & configurable_options @@ -59,7 +59,7 @@ namespace daemonize boost::program_options::variables_map const & vm ) { - LOG_PRINT_L0("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ") Daemonised"); + LOG_PRINT_L0("Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ") Daemonised"); return t_daemon{vm}; } diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 6ac47fcb2b..f227973ac6 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -124,7 +124,7 @@ int main(int argc, char const * argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << "Usage: " + std::string{argv[0]} + " [options|settings] [daemon_command...]" << std::endl << std::endl; std::cout << visible_options << std::endl; return 0; @@ -133,7 +133,7 @@ int main(int argc, char const * argv[]) // Monero Version if (command_line::get_arg(vm, command_line::arg_version)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; + std::cout << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; return 0; } @@ -283,7 +283,7 @@ int main(int argc, char const * argv[]) tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency)); // logging is now set up - MGINFO("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); + MGINFO("Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); MINFO("Moving from main() into the daemonize now."); diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 84d7543fef..b90c5c5803 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1144,10 +1144,10 @@ bool t_rpc_command_executor::print_status() bool daemon_is_alive = m_rpc_client->check_connection(); if(daemon_is_alive) { - tools::success_msg_writer() << "monerod is running"; + tools::success_msg_writer() << "aeond is running"; } else { - tools::fail_msg_writer() << "monerod is NOT running"; + tools::fail_msg_writer() << "aeond is NOT running"; } return true; diff --git a/src/daemonizer/posix_fork.cpp b/src/daemonizer/posix_fork.cpp index 4dff04f3f9..0426fc62a9 100644 --- a/src/daemonizer/posix_fork.cpp +++ b/src/daemonizer/posix_fork.cpp @@ -120,7 +120,7 @@ void fork(const std::string & pidfile) if (!tmpdir) tmpdir = TMPDIR; std::string output = tmpdir; - output += "/bitmonero.daemon.stdout.stderr"; + output += "/aeon.daemon.stdout.stderr"; const int flags = O_WRONLY | O_CREAT | O_APPEND; const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; if (open(output.c_str(), flags, mode) < 0) diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index a59c04dc6c..0962704977 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -77,7 +77,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index a9bc7b8fde..05a2722000 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -172,12 +172,12 @@ int main(int argc, char* argv[]) const auto vm = wallet_args::main( argc, argv, - "monero-gen-multisig [--testnet] [--filename-base=] [--scheme=M/N] [--threshold=M] [--participants=N]", + "aeon-gen-multisig [--testnet] [--filename-base=] [--scheme=M/N] [--threshold=M] [--participants=N]", genms::tr("This program generates a set of multisig wallets - use this simpler scheme only if all the participants trust each other"), desc_params, boost::program_options::positional_options_description(), [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, - "monero-gen-multisig.log" + "aeon-gen-multisig.log" ); if (!vm) return 1; diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 4ea08a1f87..9c28ee8edb 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -61,5 +61,5 @@ namespace nodetool const command_line::arg_descriptor arg_limit_rate_down = {"limit-rate-down", "set limit-rate-down [kB/s]", -1}; const command_line::arg_descriptor arg_limit_rate = {"limit-rate", "set limit-rate [kB/s]", -1}; - const command_line::arg_descriptor arg_save_graph = {"save-graph", "Save data for dr monero", false}; + const command_line::arg_descriptor arg_save_graph = {"save-graph", "Save data for dr aeon", false}; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index a53a6d163e..7299a3199b 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1866,7 +1866,7 @@ namespace cryptonote bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res) { PERF_TIMER(on_update); - static const char software[] = "monero"; + static const char software[] = "aeon"; #ifdef BUILD_TAG static const char buildtag[] = BOOST_PP_STRINGIZE(BUILD_TAG); static const char subdir[] = "cli"; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 181f0c1b33..ff6bc96b90 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -84,7 +84,7 @@ typedef cryptonote::simple_wallet sw; #define MIN_RING_SIZE 5 // Used to inform user about min ring size -- does not track actual protocol -#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" +#define OUTPUT_EXPORT_FILE_MAGIC "Aeon output export\003" #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ @@ -125,7 +125,7 @@ namespace const command_line::arg_descriptor arg_trusted_daemon = {"trusted-daemon", sw::tr("Enable commands which rely on a trusted daemon"), false}; const command_line::arg_descriptor arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false}; const command_line::arg_descriptor arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; - const command_line::arg_descriptor arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false}; + const command_line::arg_descriptor arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the Aeon network"), false}; const command_line::arg_descriptor< std::vector > arg_command = {"command", ""}; @@ -300,7 +300,7 @@ namespace std::stringstream prompt; prompt << tr("For URL: ") << url << ", " << dnssec_str << std::endl - << tr(" Monero Address = ") << addresses[0] + << tr(" Aeon Address = ") << addresses[0] << std::endl << tr("Is this OK? (Y/n) ") ; @@ -652,7 +652,7 @@ bool simple_wallet::print_fee_info(const std::vector &args/* = std: } const uint64_t per_kb_fee = m_wallet->get_per_kb_fee(); const uint64_t typical_size_kb = 13; - message_writer() << (boost::format(tr("Current fee is %s monero per kB")) % print_money(per_kb_fee)).str(); + message_writer() << (boost::format(tr("Current fee is %s aeon per kB")) % print_money(per_kb_fee)).str(); std::vector fees; for (uint32_t priority = 1; priority <= 4; ++priority) @@ -1135,7 +1135,7 @@ bool simple_wallet::export_raw_multisig(const std::vector &args) for (auto &ptx: txs.m_ptx) { const crypto::hash txid = cryptonote::get_transaction_hash(ptx.tx); - const std::string filename = std::string("raw_multisig_monero_tx_") + epee::string_tools::pod_to_hex(txid); + const std::string filename = std::string("raw_multisig_aeon_tx_") + epee::string_tools::pod_to_hex(txid); if (!filenames.empty()) filenames += ", "; filenames += filename; @@ -1561,7 +1561,7 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [index=[,,...]] [] [] []"), - tr("Donate to the development team (donate.getmonero.org).")); + tr("Donate to the development team (donate.aeon.cash).")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("sign_transfer "), @@ -2633,7 +2633,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm, "To start synchronizing with the daemon, use \"refresh\" command.\n" "Use \"help\" command to see the list of available commands.\n" "Use \"help \" to see a command's documentation.\n" - "Always use \"exit\" command when closing monero-wallet-cli to save your\n" + "Always use \"exit\" command when closing aeon-wallet-cli to save your\n" "current session's state. Otherwise, you might need to synchronize \n" "your wallet again (your wallet keys are NOT at risk in any case).\n") ; @@ -3806,26 +3806,26 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectormultisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_aeon_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_aeon_tx"; } } else @@ -3914,26 +3914,26 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) // actually commit the transactions if (m_wallet->multisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_aeon_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_aeon_tx"; } } else @@ -4145,26 +4145,26 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a // actually commit the transactions if (m_wallet->multisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_aeon_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_aeon_tx"; } } else @@ -4348,26 +4348,26 @@ bool simple_wallet::sweep_single(const std::vector &args_) // actually commit the transactions if (m_wallet->multisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_aeon_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_aeon_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_aeon_tx"; } } else @@ -4500,8 +4500,8 @@ bool simple_wallet::donate(const std::vector &args_) fail_msg_writer() << tr("usage: donate [index=[,,...]] [] [] []"); return true; } - // Hardcode Monero's donation address (see #1447) - const std::string address_str = "44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A"; + // Hardcode Aeon's donation address + const std::string address_str = "WmsSWgtT1JPg5e3cK41hKXSHVpKW7e47bjgiKmWZkYrhSS5LhRemNyqayaSBtAQ6517eo5PtH9wxHVmM78JDZSUu2W8PqRiNs"; std::string amount_str; std::string payment_id_str; // get payment id and pop @@ -4521,7 +4521,7 @@ bool simple_wallet::donate(const std::vector &args_) local_args.push_back(amount_str); if (!payment_id_str.empty()) local_args.push_back(payment_id_str); - message_writer() << tr("Donating ") << amount_str << " to The Monero Project (donate.getmonero.org/44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A)."; + message_writer() << tr("Donating ") << amount_str << " to The Aeon Project (donate.aeon.cash or WmsSWgtT1JPg5e3cK41hKXSHVpKW7e47bjgiKmWZkYrhSS5LhRemNyqayaSBtAQ6517eo5PtH9wxHVmM78JDZSUu2W8PqRiNs)."; transfer_new(local_args); return true; } @@ -4697,7 +4697,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) std::vector ptx; try { - bool r = m_wallet->sign_tx("unsigned_monero_tx", "signed_monero_tx", ptx, [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); }, export_raw); + bool r = m_wallet->sign_tx("unsigned_aeon_tx", "signed_aeon_tx", ptx, [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); }, export_raw); if (!r) { fail_msg_writer() << tr("Failed to sign transaction"); @@ -4717,7 +4717,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) txids_as_text += (", "); txids_as_text += epee::string_tools::pod_to_hex(get_transaction_hash(t.tx)); } - success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_monero_tx" << ", txid " << txids_as_text; + success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_aeon_tx" << ", txid " << txids_as_text; if (export_raw) { std::string rawfiles_as_text; @@ -4725,7 +4725,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) { if (i > 0) rawfiles_as_text += ", "; - rawfiles_as_text += "signed_monero_tx_raw" + (ptx.size() == 1 ? "" : ("_" + std::to_string(i))); + rawfiles_as_text += "signed_aeon_tx_raw" + (ptx.size() == 1 ? "" : ("_" + std::to_string(i))); } success_msg_writer(true) << tr("Transaction raw hex data exported to ") << rawfiles_as_text; } @@ -4740,7 +4740,7 @@ bool simple_wallet::submit_transfer(const std::vector &args_) try { std::vector ptx_vector; - bool r = m_wallet->load_tx("signed_monero_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); }); + bool r = m_wallet->load_tx("signed_aeon_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); }); if (!r) { fail_msg_writer() << tr("Failed to load transaction from file"); @@ -4826,7 +4826,7 @@ bool simple_wallet::get_tx_proof(const std::vector &args) try { std::string sig_str = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, args.size() == 3 ? args[2] : ""); - const std::string filename = "monero_tx_proof"; + const std::string filename = "aeon_tx_proof"; if (epee::file_io_utils::save_string_to_file(filename, sig_str)) success_msg_writer() << tr("signature file saved to: ") << filename; else @@ -5036,7 +5036,7 @@ bool simple_wallet::get_spend_proof(const std::vector &args) try { const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] : ""); - const std::string filename = "monero_spend_proof"; + const std::string filename = "aeon_spend_proof"; if (epee::file_io_utils::save_string_to_file(filename, sig_str)) success_msg_writer() << tr("signature file saved to: ") << filename; else @@ -6405,7 +6405,7 @@ void simple_wallet::commit_or_save(std::vector& ptx_ cryptonote::blobdata blob; tx_to_blob(ptx.tx, blob); const std::string blob_hex = epee::string_tools::buff_to_hex_nodelimer(blob); - const std::string filename = "raw_monero_tx" + (ptx_vector.size() == 1 ? "" : ("_" + std::to_string(i++))); + const std::string filename = "raw_aeon_tx" + (ptx_vector.size() == 1 ? "" : ("_" + std::to_string(i++))); if (epee::file_io_utils::save_string_to_file(filename, blob_hex)) success_msg_writer(true) << tr("Transaction successfully saved to ") << filename << tr(", txid ") << txid; else @@ -6449,12 +6449,12 @@ int main(int argc, char* argv[]) const auto vm = wallet_args::main( argc, argv, - "monero-wallet-cli [--wallet-file=|--generate-new-wallet=] []", - sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly."), + "aeon-wallet-cli [--wallet-file=|--generate-new-wallet=] []", + sw::tr("This is the command line Aeon wallet. It needs to connect to a Aeon\ndaemon to work correctly."), desc_params, positional_options, [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, - "monero-wallet-cli.log" + "aeon-wallet-cli.log" ); if (!vm) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 78f724e423..7e4937d2a1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -87,9 +87,9 @@ using namespace cryptonote; // arbitrary, used to generate different hashes from the same input #define CHACHA8_KEY_TAIL 0x8c -#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004" -#define SIGNED_TX_PREFIX "Monero signed tx set\004" -#define MULTISIG_UNSIGNED_TX_PREFIX "Monero multisig unsigned tx set\001" +#define UNSIGNED_TX_PREFIX "Aeon unsigned tx set\004" +#define SIGNED_TX_PREFIX "Aeon signed tx set\004" +#define MULTISIG_UNSIGNED_TX_PREFIX "Aeon multisig unsigned tx set\001" #define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone #define RECENT_OUTPUT_ZONE ((time_t)(1.8 * 86400)) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al) @@ -101,9 +101,9 @@ using namespace cryptonote; #define SUBADDRESS_LOOKAHEAD_MAJOR 50 #define SUBADDRESS_LOOKAHEAD_MINOR 200 -#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" +#define KEY_IMAGE_EXPORT_FILE_MAGIC "Aeon key image export\002" -#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001" +#define MULTISIG_EXPORT_FILE_MAGIC "Aeon multisig export\001" namespace { @@ -8765,7 +8765,7 @@ std::string wallet2::make_uri(const std::string &address, const std::string &pay } } - std::string uri = "monero:" + address; + std::string uri = "aeon:" + address; unsigned int n_fields = 0; if (!payment_id.empty()) diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 2273f14adc..3da297238d 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -134,8 +134,8 @@ namespace wallet_args if (command_line::get_arg(vm, command_line::arg_help)) { - Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; - Print(print) << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n" + Print(print) << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; + Print(print) << wallet_args::tr("This is the command line Aeon wallet. It needs to connect to a Aeon\n" "daemon to work correctly.") << ENDL; Print(print) << wallet_args::tr("Usage:") << ENDL << " " << usage; Print(print) << desc_all; @@ -143,7 +143,7 @@ namespace wallet_args } else if (command_line::get_arg(vm, command_line::arg_version)) { - Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + Print(print) << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; return false; } @@ -186,12 +186,12 @@ namespace wallet_args if (!command_line::is_arg_defaulted(vm, arg_max_concurrency)) tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); - Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + Print(print) << "Aeon '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; if (!command_line::is_arg_defaulted(vm, arg_log_level)) MINFO("Setting log level = " << command_line::get_arg(vm, arg_log_level)); else - MINFO("Setting log levels = " << getenv("MONERO_LOGS")); + MINFO("Setting log levels = " << getenv("AEON_LOGS")); MINFO(wallet_args::tr("Logging to: ") << log_path); Print(print) << boost::format(wallet_args::tr("Logging to %s")) % log_path; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 302691581c..e4a5fa3fd9 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -62,7 +62,7 @@ namespace const command_line::arg_descriptor arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; - constexpr const char default_rpc_username[] = "monero"; + constexpr const char default_rpc_username[] = "aeon"; boost::optional password_prompter(const char *prompt, bool verify) { @@ -198,7 +198,7 @@ namespace tools string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()) ); - std::string temp = "monero-wallet-rpc." + bind_port + ".login"; + std::string temp = "aeon-wallet-rpc." + bind_port + ".login"; rpc_login_file = tools::private_file::create(temp); if (!rpc_login_file.handle()) { @@ -505,7 +505,7 @@ namespace tools } if (addresses.empty()) { - er.message = std::string("No Monero address found at ") + url; + er.message = std::string("No Aeon address found at ") + url; return {}; } return addresses[0]; @@ -1479,7 +1479,7 @@ namespace tools } if (addresses.empty()) { - er.message = std::string("No Monero address found at ") + url; + er.message = std::string("No Aeon address found at ") + url; return {}; } return addresses[0]; @@ -2117,7 +2117,7 @@ namespace tools } if (addresses.empty()) { - er.message = std::string("No Monero address found at ") + url; + er.message = std::string("No Aeon address found at ") + url; return {}; } return addresses[0]; @@ -2875,12 +2875,12 @@ int main(int argc, char** argv) { const auto vm = wallet_args::main( argc, argv, - "monero-wallet-rpc [--wallet-file=|--generate-from-json=|--wallet-dir=] [--rpc-bind-port=]", - tools::wallet_rpc_server::tr("This is the RPC monero wallet. It needs to connect to a monero\ndaemon to work correctly."), + "aeon-wallet-rpc [--wallet-file=|--generate-from-json=|--wallet-dir=] [--rpc-bind-port=]", + tools::wallet_rpc_server::tr("This is the RPC Aeon wallet. It needs to connect to an Aeon\ndaemon to work correctly."), desc_params, po::positional_options_description(), [](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); }, - "monero-wallet-rpc.log", + "aeon-wallet-rpc.log", true ); if (!vm) From c02b09eaecda627353865ca18a90ce73e7bd1188 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Feb 2018 21:22:00 +0900 Subject: [PATCH 27/47] Disable DNS checkpoints for now --- src/checkpoints/checkpoints.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index c66c4f5d6a..5d9508cc06 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -238,6 +238,7 @@ namespace cryptonote bool checkpoints::load_checkpoints_from_dns(bool testnet) { + return true; // TODO: setup DNS checkpoints for Aeon std::vector records; // All four MoneroPulse domains have DNSSEC on and valid From f42344c083b7e45f1039daab27830e499dc0685b Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 27 Dec 2017 19:11:23 +0900 Subject: [PATCH 28/47] Principal changes --- src/blocks/checkpoints.dat | Bin 173732 -> 115460 bytes src/checkpoints/checkpoints.cpp | 49 +-- src/crypto/chacha.h | 2 +- src/crypto/hash-ops.h | 2 +- src/crypto/hash.h | 12 +- src/crypto/slow-hash.c | 66 ++-- .../cryptonote_basic_impl.cpp | 23 +- src/cryptonote_basic/cryptonote_basic_impl.h | 2 +- .../cryptonote_format_utils.cpp | 13 +- src/cryptonote_basic/difficulty.cpp | 9 +- src/cryptonote_basic/difficulty.h | 2 +- src/cryptonote_config.h | 47 +-- src/cryptonote_core/blockchain.cpp | 64 +--- src/cryptonote_core/blockchain.h | 3 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 2 +- src/cryptonote_core/tx_pool.cpp | 6 +- src/cryptonote_core/tx_pool.h | 3 +- .../cryptonote_protocol_handler.inl | 2 +- src/p2p/net_node.inl | 19 +- src/rpc/core_rpc_server.cpp | 2 +- src/wallet/wallet2.cpp | 9 +- tests/CMakeLists.txt | 4 +- tests/core_tests/block_validation.cpp | 10 +- tests/core_tests/chaingen.cpp | 4 +- tests/core_tests/multisig.cpp | 4 +- tests/core_tests/rct.cpp | 34 +- tests/core_tests/v2_tests.cpp | 4 +- tests/data/wallet_9svHk1 | Bin 1971 -> 0 bytes tests/data/wallet_9svHk1.keys | Bin 759 -> 0 bytes tests/data/wallet_WmsMW3 | Bin 0 -> 8054 bytes tests/data/wallet_WmsMW3.keys | Bin 0 -> 925 bytes tests/difficulty/difficulty.cpp | 2 +- tests/hash/CMakeLists.txt | 2 +- tests/hash/main.cpp | 8 +- tests/hash/tests-slow-1m.txt | 4 + .../{tests-slow.txt => tests-slow-2m.txt} | 0 tests/performance_tests/cn_slow_hash.h | 2 +- tests/unit_tests/base58.cpp | 2 +- tests/unit_tests/block_reward.cpp | 10 +- tests/unit_tests/multisig.cpp | 6 +- tests/unit_tests/serialization.cpp | 317 ++++++++++++++---- tests/unit_tests/uri.cpp | 62 ++-- 42 files changed, 487 insertions(+), 325 deletions(-) delete mode 100644 tests/data/wallet_9svHk1 delete mode 100644 tests/data/wallet_9svHk1.keys create mode 100644 tests/data/wallet_WmsMW3 create mode 100644 tests/data/wallet_WmsMW3.keys create mode 100644 tests/hash/tests-slow-1m.txt rename tests/hash/{tests-slow.txt => tests-slow-2m.txt} (100%) diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index c12c3d8a02c3b8d2f4d6da6457b788c8244c8e70..8c62d3a8074ccf924cc0a0320d506482bee13401 100644 GIT binary patch literal 115460 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%0DFRYZK9= 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

@t~6KH#2}TB={< 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 z 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�u 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≪E%5oH#Ce;y+E*fb-{o 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

!^ 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 Iwyxc}+WkW!QUCw| diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 5d9508cc06..ea2cd2ca56 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -163,42 +163,23 @@ namespace cryptonote { if (testnet) { - ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); - ADD_CHECKPOINT(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258"); + ADD_CHECKPOINT(0, "d0ae70ae5f35b27b3a4b68ae4f78eeca9989174eebe8a1e55cd139fe9b797223"); + ADD_CHECKPOINT(1, "10d61dadc6f981bea93eea9d0c3ad8eb0c20e5fdc3db0e1722f18e60c83394f8"); + ADD_CHECKPOINT(10, "67860367465a01e3601ed7e9f01c5339e3cd955022809c6bce33c754a29bcb7d"); + ADD_CHECKPOINT(100, "960bb09aaa28230126796dc4e658cb8880ac679058203cd5880b8b853b9204d4"); return true; } - ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); - ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381"); - ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d"); - ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876"); - ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2"); - ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25"); - ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6"); - ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c"); - ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3"); - ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"); - ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2"); - ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb"); - ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063"); - ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553"); - ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61"); - ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf"); - ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131"); - ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8"); - ADD_CHECKPOINT(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d"); - ADD_CHECKPOINT(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831"); - ADD_CHECKPOINT(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02"); - ADD_CHECKPOINT(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5"); - ADD_CHECKPOINT(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd"); - ADD_CHECKPOINT(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60"); - ADD_CHECKPOINT(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337"); - ADD_CHECKPOINT(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f"); - ADD_CHECKPOINT(1000000, "a886ef5149902d8342475fee9bb296341b891ac67c4842f47a833f23c00ed721"); - ADD_CHECKPOINT(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903"); - ADD_CHECKPOINT(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2"); - ADD_CHECKPOINT(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f"); - ADD_CHECKPOINT(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0"); - ADD_CHECKPOINT(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c"); + ADD_CHECKPOINT(1, "1440a20f078bf3264822234b347f8382606577d73d4e9d3cb7296d73889bc421"); + ADD_CHECKPOINT(100, "6dd13aaab16679f49ee6b2b75c7dc99b1fd09ab2282b18cb4b55b73110655742"); + ADD_CHECKPOINT(1000, "bc6458452fd0575a314089bf302f6fd68ebaa2d689c42f3365293b96bbdf1f25"); + ADD_CHECKPOINT(10000, "1ac1ebd25baf0d6ec593daa3389f1aa7e860ff2cc29f3cf1be586d245b379da4"); + ADD_CHECKPOINT(15000, "15567af42afc1ed00538f53b5e3822d421e3ed6372ca79f4ea4e3e3bab709a87"); + ADD_CHECKPOINT(175500, "3f7dd748b3b863b04654d87a387f2b65a365f467188971f3192eab2368e64a35"); + ADD_CHECKPOINT(450000, "f69a6e57c4dd5df2f492c9d31c50f11aad2c25a64d540ce5f5d11b572aec8ab7"); + ADD_CHECKPOINT(540000, "94e19cf9d5a16ae90f67c321f8376b87da21d6d6c2cb0957b9ab558dca66c1dc"); + ADD_CHECKPOINT(592001, "e8bc936b287a9c426a15cf127624b064c88e6d37655cc87f9a62cf1623c62385"); + ADD_CHECKPOINT(798358, "804c7fe07511d9387e7cda534c9e8b644d406d8d0ff299799a8177850d4e75a0"); + ADD_CHECKPOINT(871000, "99f7e5460da3fb4e2b15214017b0a17ff0294823ad852259ff837f0ffeeb90f0"); return true; diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index c11e4aa2fd..9ec6a0fa7c 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -72,7 +72,7 @@ namespace crypto { inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); tools::scrubbed_arr pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data()); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0); memcpy(&key, pwd_hash.data(), sizeof(key)); } diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index 6e3a5c6c9b..f212b7bdd0 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -79,7 +79,7 @@ enum { }; void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash(const void *data, size_t length, char *hash); +void cn_slow_hash(const void *data, size_t length, char *hash, int light); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 610b4502f1..bf5a7f19a5 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -71,8 +71,16 @@ namespace crypto { return h; } - inline void cn_slow_hash(const void *data, std::size_t length, hash &hash) { - cn_slow_hash(data, length, reinterpret_cast(&hash)); + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int light) { + cn_slow_hash(data, length, reinterpret_cast(&hash), light); + } + + inline void cn_slow_hash_1m(const void *data, std::size_t length, hash &hash) { + cn_slow_hash(data, length, hash, 1); + } + + inline void cn_slow_hash_2m(const void *data, std::size_t length, hash &hash) { + cn_slow_hash(data, length, hash, 0); } inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 870048f643..7dab25d822 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -92,7 +92,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #define U64(x) ((uint64_t *) (x)) #define R128(x) ((__m128i *) (x)) -#define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4) +#define state_index(x,div) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS /(div) - 1)) << 4) #if defined(_MSC_VER) #if !defined(_WIN64) #define __mul() lo = mul128(c[0], b[0], &hi); @@ -108,7 +108,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #endif #define pre_aes() \ - j = state_index(a); \ + j = state_index(a,(light?2:1)); \ _c = _mm_load_si128(R128(&hp_state[j])); \ _a = _mm_load_si128(R128(a)); \ @@ -125,7 +125,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp _mm_store_si128(R128(c), _c); \ _b = _mm_xor_si128(_b, _c); \ _mm_store_si128(R128(&hp_state[j]), _b); \ - j = state_index(c); \ + j = state_index(c,(light?2:1)); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ __mul(); \ @@ -516,7 +516,7 @@ void slow_hash_free_state(void) * @param hash a pointer to a buffer in which the final 256 bit hash will be stored */ -void cn_slow_hash(const void *data, size_t length, char *hash) +void cn_slow_hash(const void *data, size_t length, char *hash, int light) { RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ @@ -554,7 +554,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) if(useAes) { aes_expand_key(state.hs.b, expandedKey); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { aes_pseudo_round(text, text, expandedKey, INIT_SIZE_BLK); memcpy(&hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); @@ -564,7 +564,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) { aes_ctx = (oaes_ctx *) oaes_alloc(); oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { for(j = 0; j < INIT_SIZE_BLK; j++) aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], aes_ctx->key->exp_data); @@ -588,7 +588,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) // the useAes test is only performed once, not every iteration. if(useAes) { - for(i = 0; i < ITER / 2; i++) + for(i = 0; i < ITER / (light?2:1) / 2; i++) { pre_aes(); _c = _mm_aesenc_si128(_c, _a); @@ -597,7 +597,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) } else { - for(i = 0; i < ITER / 2; i++) + for(i = 0; i < ITER / (light?2:1) / 2; i++) { pre_aes(); aesb_single_round((uint8_t *) &_c, (uint8_t *) &_c, (uint8_t *) &_a); @@ -613,7 +613,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) if(useAes) { aes_expand_key(&state.hs.b[32], expandedKey); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { // add the xor to the pseudo round aes_pseudo_round_xor(text, text, expandedKey, &hp_state[i * INIT_SIZE_BYTE], INIT_SIZE_BLK); @@ -622,7 +622,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) else { oaes_key_import_data(aes_ctx, &state.hs.b[32], AES_KEY_SIZE); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { for(j = 0; j < INIT_SIZE_BLK; j++) { @@ -693,12 +693,12 @@ union cn_slow_hash_state #define TOTALBLOCKS (MEMORY / AES_BLOCK_SIZE) -#define state_index(x) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS - 1)) << 4) +#define state_index(x,div) (((*((uint64_t *)x) >> 4) & (TOTALBLOCKS /(div) - 1)) << 4) #define __mul() __asm__("mul %0, %1, %2\n\t" : "=r"(lo) : "r"(c[0]), "r"(b[0]) ); \ __asm__("umulh %0, %1, %2\n\t" : "=r"(hi) : "r"(c[0]), "r"(b[0]) ); #define pre_aes() \ - j = state_index(a); \ + j = state_index(a,(light?2:1)); \ _c = vld1q_u8(&hp_state[j]); \ _a = vld1q_u8((const uint8_t *)a); \ @@ -706,7 +706,7 @@ union cn_slow_hash_state vst1q_u8((uint8_t *)c, _c); \ _b = veorq_u8(_b, _c); \ vst1q_u8(&hp_state[j], _b); \ - j = state_index(c); \ + j = state_index(c,(light?2:1)); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ __mul(); \ @@ -845,7 +845,7 @@ STATIC INLINE void aes_pseudo_round_xor(const uint8_t *in, uint8_t *out, const u } } -void cn_slow_hash(const void *data, size_t length, char *hash) +void cn_slow_hash(const void *data, size_t length, char *hash, int light) { RDATA_ALIGN16 uint8_t expandedKey[240]; RDATA_ALIGN16 uint8_t hp_state[MEMORY]; @@ -876,7 +876,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) */ aes_expand_key(state.hs.b, expandedKey); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { aes_pseudo_round(text, text, expandedKey, INIT_SIZE_BLK); memcpy(&hp_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); @@ -895,7 +895,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) _b = vld1q_u8((const uint8_t *)b); - for(i = 0; i < ITER / 2; i++) + for(i = 0; i < ITER / (light?2:1) / 2; i++) { pre_aes(); _c = vaeseq_u8(_c, zero); @@ -911,7 +911,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) memcpy(text, state.init, INIT_SIZE_BYTE); aes_expand_key(&state.hs.b[32], expandedKey); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { // add the xor to the pseudo round aes_pseudo_round_xor(text, text, expandedKey, &hp_state[i * INIT_SIZE_BYTE], INIT_SIZE_BLK); @@ -1039,7 +1039,7 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b) U64(a)[1] ^= U64(b)[1]; } -void cn_slow_hash(const void *data, size_t length, char *hash) +void cn_slow_hash(const void *data, size_t length, char *hash, int light) { uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; @@ -1073,7 +1073,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) // use aligned data memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { for(j = 0; j < INIT_SIZE_BLK; j++) aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], expandedKey); @@ -1085,13 +1085,13 @@ void cn_slow_hash(const void *data, size_t length, char *hash) U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0]; U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1]; - for(i = 0; i < ITER / 2; i++) + for(i = 0; i < ITER / (light?2:1) / 2; i++) { - #define MASK ((uint32_t)(((MEMORY / AES_BLOCK_SIZE) - 1) << 4)) - #define state_index(x) ((*(uint32_t *) x) & MASK) + #define MASK(div) ((uint32_t)(((MEMORY / AES_BLOCK_SIZE) / (div) - 1) << 4)) + #define state_index(x,div) ((*(uint32_t *) x) & MASK(div)) // Iteration 1 - p = &long_state[state_index(a)]; + p = &long_state[state_index(a,(light?2:1))]; aesb_single_round(p, p, a); xor_blocks(b, p); @@ -1099,7 +1099,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) swap_blocks(a, b); // Iteration 2 - p = &long_state[state_index(a)]; + p = &long_state[state_index(a,(light?2:1))]; mul(a, p, d); sum_half_blocks(b, d); @@ -1111,7 +1111,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) memcpy(text, state.init, INIT_SIZE_BYTE); oaes_key_import_data(aes_ctx, &state.hs.b[32], AES_KEY_SIZE); memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); - for(i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) + for(i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { for(j = 0; j < INIT_SIZE_BLK; j++) { @@ -1210,7 +1210,7 @@ union cn_slow_hash_state { }; #pragma pack(pop) -void cn_slow_hash(const void *data, size_t length, char *hash) { +void cn_slow_hash(const void *data, size_t length, char *hash, int light) { uint8_t long_state[MEMORY]; union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; @@ -1228,7 +1228,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash) { aes_ctx = (oaes_ctx *) oaes_alloc(); oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); - for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { + for (i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { for (j = 0; j < INIT_SIZE_BLK; j++) { aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], aes_ctx->key->exp_data); } @@ -1240,35 +1240,35 @@ void cn_slow_hash(const void *data, size_t length, char *hash) { b[i] = state.k[16 + i] ^ state.k[48 + i]; } - for (i = 0; i < ITER / 2; i++) { + for (i = 0; i < ITER / (light?2:1) / 2; i++) { /* Dependency chain: address -> read value ------+ * written value <-+ hard function (AES or MUL) <+ * next address <-+ */ /* Iteration 1 */ - j = e2i(a, MEMORY / AES_BLOCK_SIZE); + j = e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE); copy_block(c, &long_state[j * AES_BLOCK_SIZE]); aesb_single_round(c, c, a); xor_blocks(b, c); swap_blocks(b, c); copy_block(&long_state[j * AES_BLOCK_SIZE], c); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); + assert(j == e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE)); swap_blocks(a, b); /* Iteration 2 */ - j = e2i(a, MEMORY / AES_BLOCK_SIZE); + j = e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE); copy_block(c, &long_state[j * AES_BLOCK_SIZE]); mul(a, c, d); sum_half_blocks(b, d); swap_blocks(b, c); xor_blocks(b, c); copy_block(&long_state[j * AES_BLOCK_SIZE], c); - assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); + assert(j == e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE)); swap_blocks(a, b); } memcpy(text, state.init, INIT_SIZE_BYTE); oaes_key_import_data(aes_ctx, &state.hs.b[32], AES_KEY_SIZE); - for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { + for (i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { for (j = 0; j < INIT_SIZE_BLK; j++) { xor_blocks(&text[j * AES_BLOCK_SIZE], &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]); aesb_pseudo_round(&text[AES_BLOCK_SIZE * j], &text[AES_BLOCK_SIZE * j], aes_ctx->key->exp_data); diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 1183fda06a..f7dfc153fe 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -46,6 +46,23 @@ using namespace epee; #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "cn" +namespace tools { + inline int ilog2(int x) + { + assert(x > 0); + assert((x & (x - 1)) == 0); + int r = 0; + while (true) + { + x >>= 1; + if (x == 0) + break; + ++r; + } + return r; + } +} + namespace cryptonote { struct integrated_address { @@ -86,11 +103,11 @@ namespace cryptonote { return CRYPTONOTE_MAX_TX_SIZE; } //----------------------------------------------------------------------------------------------- - bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) { + bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version, uint64_t height) { static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60"); - const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + const int target = version < 2 && height < HARDFORK_1_HEIGHT ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; const int target_minutes = target / 60; - const int emission_speed_factor = EMISSION_SPEED_FACTOR_PER_MINUTE - (target_minutes-1); + const int emission_speed_factor = EMISSION_SPEED_FACTOR_PER_MINUTE - tools::ilog2(target_minutes); // we assume target_minutes to be power of 2 uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> emission_speed_factor; if (base_reward < FINAL_SUBSIDY_PER_MINUTE*target_minutes) diff --git a/src/cryptonote_basic/cryptonote_basic_impl.h b/src/cryptonote_basic/cryptonote_basic_impl.h index 08d966fede..fc8e918a7b 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.h +++ b/src/cryptonote_basic/cryptonote_basic_impl.h @@ -89,7 +89,7 @@ namespace cryptonote { size_t get_min_block_size(uint8_t version); size_t get_max_block_size(); size_t get_max_tx_size(); - bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); + bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version, uint64_t height); uint8_t get_account_address_checksum(const public_address_outer_blob& bl); uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl); diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 8f7ab94db9..12a69d9691 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -932,15 +932,8 @@ namespace cryptonote //--------------------------------------------------------------- bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { - // block 202612 bug workaround - const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"; - if (height == 202612) - { - string_tools::hex_to_pod(longhash_202612, res); - return true; - } blobdata bd = get_block_hashing_blob(b); - crypto::cn_slow_hash(bd.data(), bd.size(), res); + crypto::cn_slow_hash(bd.data(), bd.size(), res, height >= HARDFORK_1_HEIGHT); return true; } //--------------------------------------------------------------- @@ -1045,7 +1038,7 @@ namespace cryptonote crypto::secret_key encrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase) { crypto::hash hash; - crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); + crypto::cn_slow_hash_1m(passphrase.data(), passphrase.size(), hash); sc_add((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data); return key; } @@ -1053,7 +1046,7 @@ namespace cryptonote crypto::secret_key decrypt_key(crypto::secret_key key, const epee::wipeable_string &passphrase) { crypto::hash hash; - crypto::cn_slow_hash(passphrase.data(), passphrase.size(), hash); + crypto::cn_slow_hash_1m(passphrase.data(), passphrase.size(), hash); sc_sub((unsigned char*)key.data, (const unsigned char*)key.data, (const unsigned char*)hash.data); return key; } diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 863aa4359a..bb719ba60e 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -119,7 +119,7 @@ namespace cryptonote { return !carry; } - difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds) { + difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds, uint64_t height) { if(timestamps.size() > DIFFICULTY_WINDOW) { @@ -159,7 +159,12 @@ namespace cryptonote { if (high != 0 || low + time_span - 1 < low) { return 0; } - return (low + time_span - 1) / time_span; + + difficulty_type new_diff = (low + time_span - 1) / time_span; + if (height >= HARDFORK_1_HEIGHT && height < HARDFORK_1_HEIGHT+HARDFORK_1_DIFFADJ_WINDOW) { + new_diff += new_diff*(HARDFORK_1_HEIGHT+HARDFORK_1_DIFFADJ_WINDOW-height)*(HARDFORK_1_DIFFADJ-1)/HARDFORK_1_DIFFADJ_WINDOW; + } + return new_diff; } } diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index aeb1c030d3..33d65d82d9 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -52,5 +52,5 @@ namespace cryptonote * @return true if valid, else false */ bool check_hash(const crypto::hash &hash, difficulty_type difficulty); - difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); + difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds, uint64_t height); } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 844faec8f5..867e749088 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -35,6 +35,11 @@ #define CRYPTONOTE_DNS_TIMEOUT_MS 20000 +#define HARDFORK_1_HEIGHT 592000 +#define HARDFORK_1_POW_SPEED_MULTIPLIER 2 +#define HARDFORK_1_DIFFADJ HARDFORK_1_POW_SPEED_MULTIPLIER +#define HARDFORK_1_DIFFADJ_WINDOW 360 + #define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000 #define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used! #define CRYPTONOTE_GETBLOCKTEMPLATE_MAX_BLOCK_SIZE 196608 //size of block (bytes) that is the maximum that miners will produce @@ -63,16 +68,14 @@ // COIN - number of smallest units in one coin #define COIN ((uint64_t)1000000000000) // pow(10, 12) -#define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10) -#define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) +#define FEE_PER_KB ((uint64_t)1000000000) // 1 * pow(10, 9) #define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9) #define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12) -#define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5) #define ORPHANED_BLOCKS_MAX_COUNT 100 -#define DIFFICULTY_TARGET_V2 120 // seconds +#define DIFFICULTY_TARGET_V2 240 // seconds #define DIFFICULTY_TARGET_V1 60 // seconds - before first fork #define DIFFICULTY_WINDOW 720 // blocks #define DIFFICULTY_LAG 15 // !!! @@ -121,7 +124,7 @@ #define ALLOW_DEBUG_COMMANDS -#define CRYPTONOTE_NAME "bitmonero" +#define CRYPTONOTE_NAME "aeon" #define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" #define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb" #define CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME "lock.mdb" @@ -130,7 +133,7 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 -#define HF_VERSION_DYNAMIC_FEE 4 +// #define HF_VERSION_DYNAMIC_FEE 4 #define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_ENFORCE_RCT 6 @@ -148,30 +151,30 @@ namespace config uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8) std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000"; - uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; - uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; - uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 42; - uint16_t const P2P_DEFAULT_PORT = 18080; - uint16_t const RPC_DEFAULT_PORT = 18081; - uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082; + uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0xB2; // starts with "Wm" + uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0x2733; // starts with "Wz" + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0x06B8; // starts with "Xn" + uint16_t const P2P_DEFAULT_PORT = 11180; + uint16_t const RPC_DEFAULT_PORT = 11181; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 11182; boost::uuids::uuid const NETWORK_ID = { { - 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10 + 0x32 ,0x32, 0xF3, 0x91 , 0x81, 0x18 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10 } }; // Bender's nightmare - std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1"; + std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121012bf2d282da90cee9c7a28c16e81418101ee28607d9e50f706594ee144a453b68"; uint32_t const GENESIS_NONCE = 10000; namespace testnet { - uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 53; - uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54; - uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 63; - uint16_t const P2P_DEFAULT_PORT = 28080; - uint16_t const RPC_DEFAULT_PORT = 28081; - uint16_t const ZMQ_RPC_DEFAULT_PORT = 28082; + uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 0x0426; // starts with "Um" + uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 0x2C27; // starts with "Uz" + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 0x0AAC; // starts with "Vn" + uint16_t const P2P_DEFAULT_PORT = 21180; + uint16_t const RPC_DEFAULT_PORT = 21181; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 21182; boost::uuids::uuid const NETWORK_ID = { { - 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11 + 0x32 ,0x32, 0xF3, 0x91 , 0x81, 0x18 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11 } }; // Bender's daydream - std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1"; + std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd088071210102711ad6274bcf3fa49cf5a99ec02fd3bbfef2dc64fc388df55e7ed8dc310a6f"; uint32_t const GENESIS_NONCE = 10001; } } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 123bd194b0..de63fc7826 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -91,23 +91,7 @@ static const struct { } mainnet_hard_forks[] = { // version 1 from the start of the blockchain { 1, 1, 0, 1341378000 }, - - // version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork. - { 2, 1009827, 0, 1442763710 }, - - // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21. - { 3, 1141317, 0, 1458558528 }, - - // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18. - { 4, 1220516, 0, 1483574400 }, - - // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14. - { 5, 1288616, 0, 1489520158 }, - - // version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18. - { 6, 1400000, 0, 1503046577 }, }; -static const uint64_t mainnet_hard_fork_version_1_till = 1009826; static const struct { uint8_t version; @@ -117,19 +101,7 @@ static const struct { } testnet_hard_forks[] = { // version 1 from the start of the blockchain { 1, 1, 0, 1341378000 }, - - // version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork. - { 2, 624634, 0, 1445355000 }, - - // versions 3-5 were passed in rapid succession from September 18th, 2016 - { 3, 800500, 0, 1472415034 }, - { 4, 801219, 0, 1472415035 }, - { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 - - { 6, 971400, 0, 1501709789 }, - { 7, 1057028, 0, 1512211236 }, }; -static const uint64_t testnet_hard_fork_version_1_till = 624633; //------------------------------------------------------------------ Blockchain::Blockchain(tx_memory_pool& tx_pool) : @@ -330,12 +302,7 @@ bool Blockchain::init(BlockchainDB* db, const bool testnet, bool offline, const m_offline = offline; if (m_hardfork == nullptr) { - if (fakechain) - m_hardfork = new HardFork(*db, 1, 0); - else if (m_testnet) - m_hardfork = new HardFork(*db, 1, testnet_hard_fork_version_1_till); - else - m_hardfork = new HardFork(*db, 1, mainnet_hard_fork_version_1_till); + m_hardfork = new HardFork(*db, 1, 0); } if (fakechain) { @@ -781,7 +748,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block() m_difficulties = difficulties; } size_t target = get_difficulty_target(); - return next_difficulty(timestamps, difficulties, target); + return next_difficulty(timestamps, difficulties, target, height); } //------------------------------------------------------------------ // This function removes blocks from the blockchain until it gets to the @@ -981,10 +948,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: } // FIXME: This will fail if fork activation heights are subject to voting - size_t target = get_ideal_hard_fork_version(bei.height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + size_t target = get_ideal_hard_fork_version(bei.height) < 2 && bei.height < HARDFORK_1_HEIGHT ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; // calculate the difficulty target for the block and return it - return next_difficulty(timestamps, cumulative_difficulties, target); + return next_difficulty(timestamps, cumulative_difficulties, target, bei.height); } //------------------------------------------------------------------ // This function does a sanity check on basic things that all miner @@ -1019,7 +986,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height) } //------------------------------------------------------------------ // This function validates the miner transaction reward -bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version) +bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version, uint64_t height) { LOG_PRINT_L3("Blockchain::" << __func__); //validate reward @@ -1039,7 +1006,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl std::vector last_blocks_sizes; get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); - if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version)) + if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version, height)) { MERROR_VER("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); return false; @@ -1133,7 +1100,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m size_t txs_size; uint64_t fee; - if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version())) + if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version(), get_current_blockchain_height())) { return false; } @@ -2951,7 +2918,8 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const { const uint8_t version = get_current_hard_fork_version(); - uint64_t fee_per_kb; + uint64_t fee_per_kb = FEE_PER_KB; +#if 0 if (version < HF_VERSION_DYNAMIC_FEE) { fee_per_kb = FEE_PER_KB; @@ -2961,10 +2929,11 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const uint64_t median = m_current_block_cumul_sz_limit / 2; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t base_reward; - if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) + if (!get_block_reward(median, 1, already_generated_coins, base_reward, version, get_current_blockchain_height())) return false; fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version); } +#endif MDEBUG("Using " << print_money(fee) << "/kB fee"); uint64_t needed_fee = blob_size / 1024; @@ -2984,6 +2953,8 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons { const uint8_t version = get_current_hard_fork_version(); + return FEE_PER_KB; +#if 0 if (version < HF_VERSION_DYNAMIC_FEE) return FEE_PER_KB; @@ -3002,7 +2973,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t base_reward; - if (!get_block_reward(median, 1, already_generated_coins, base_reward, version)) + if (!get_block_reward(median, 1, already_generated_coins, base_reward, version, get_current_blockchain_height())) { MERROR("Failed to determine block reward, using placeholder " << print_money(BLOCK_REWARD_OVERESTIMATE) << " as a high bound"); base_reward = BLOCK_REWARD_OVERESTIMATE; @@ -3011,6 +2982,7 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version); MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB"); return fee; +#endif } //------------------------------------------------------------------ @@ -3467,7 +3439,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& TIME_MEASURE_START(vmt); uint64_t base_reward = 0; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; - if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version())) + if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version(), get_current_blockchain_height())) { MERROR_VER("Block with id: " << id << " has incorrect miner transaction"); bvc.m_verifivation_failed = true; @@ -4293,7 +4265,7 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui uint64_t Blockchain::get_difficulty_target() const { - return get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + return get_current_hard_fork_version() < 2 && get_current_blockchain_height() < HARDFORK_1_HEIGHT ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; } std::map> Blockchain:: get_output_histogram(const std::vector &amounts, bool unlocked, uint64_t recent_cutoff) const @@ -4339,7 +4311,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "4b553162ee4e7af3c53666506591489c68560b9175e6e941dc96c89f96f0e35c"; +static const char expected_block_hashes_hash[] = "61203f01a03fa50e50d394033b35d828a89f12bd1b8b07889e2d4ef68aa1007b"; void Blockchain::load_compiled_in_block_hashes() { if (m_fast_sync && get_blocks_dat_start(m_testnet) != nullptr && get_blocks_dat_size(m_testnet) > 0) diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 0cc6461d31..6168de959b 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1207,10 +1207,11 @@ namespace cryptonote * @param already_generated_coins the amount of currency generated prior to this block * @param partial_block_reward return-by-reference true if miner accepted only partial reward * @param version hard fork version for that transaction + * @param height block height for that transaction * * @return false if anything is found wrong with the miner transaction, otherwise true */ - bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version); + bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version, uint64_t height); /** * @brief reverts the blockchain to its previous state following a failed switch diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 0739e96c95..5d0cf9797d 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -88,7 +88,7 @@ namespace cryptonote in.height = height; uint64_t block_reward; - if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) + if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version, height)) { LOG_PRINT_L0("Block is too big"); return false; diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index e6f217463f..a5a8b8e11b 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -969,7 +969,7 @@ namespace cryptonote } //--------------------------------------------------------------------------------- //TODO: investigate whether boolean return is appropriate - bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version) + bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version, uint64_t height) { // Warning: This function takes already_generated_ // coins as an argument and appears to do nothing @@ -983,7 +983,7 @@ namespace cryptonote fee = 0; //baseline empty block - get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version); + get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version, height); size_t max_total_size_pre_v5 = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; @@ -1015,7 +1015,7 @@ namespace cryptonote // If we're getting lower coinbase tx, // stop including more tx uint64_t block_reward; - if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version)) + if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version, height)) { LOG_PRINT_L2(" would exceed maximum block size"); sorted_it++; diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h index d657c65543..d66dd73603 100644 --- a/src/cryptonote_core/tx_pool.h +++ b/src/cryptonote_core/tx_pool.h @@ -225,10 +225,11 @@ namespace cryptonote * @param fee return-by-reference the total of fees from the included transactions * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param version hard fork version to use for consensus rules + * @param height current top block height * * @return true */ - bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version); + bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version, uint64_t height); /** * @brief get a list of all transactions in the pool diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 258d3e30f9..0d41dafe6c 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -298,7 +298,7 @@ namespace cryptonote int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); uint64_t abs_diff = std::abs(diff); uint64_t max_block_height = std::max(hshd.current_height,m_core.get_current_blockchain_height()); - uint64_t last_block_v1 = m_core.get_testnet() ? 624633 : 1009826; + uint64_t last_block_v1 = std::min(HARDFORK_1_HEIGHT, m_core.get_earliest_ideal_height_for_version(2)) - 1; uint64_t diff_v2 = max_block_height > last_block_v1 ? std::min(abs_diff, max_block_height - last_block_v1) : 0; MCLOG(is_inital ? el::Level::Info : el::Level::Debug, "global", context << "Sync data returned a new top block candidate: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height << " [Your node is " << abs_diff << " blocks (" << ((abs_diff - diff_v2) / (24 * 60 * 60 / DIFFICULTY_TARGET_V1)) + (diff_v2 / (24 * 60 * 60 / DIFFICULTY_TARGET_V2)) << " days) " diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 55be7c2cb7..faf3ddd795 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -373,22 +373,15 @@ namespace nodetool std::set full_addrs; if (testnet) { - full_addrs.insert("212.83.175.67:28080"); - full_addrs.insert("5.9.100.248:28080"); - full_addrs.insert("163.172.182.165:28080"); - full_addrs.insert("195.154.123.123:28080"); - full_addrs.insert("212.83.172.165:28080"); + // full_addrs.insert("74.91.23.186:21180"); + // full_addrs.insert("192.187.114.114:21180"); + full_addrs.insert("162.210.173.150:21180"); + full_addrs.insert("162.210.173.151:21180"); } else { - full_addrs.insert("107.152.130.98:18080"); - full_addrs.insert("212.83.175.67:18080"); - full_addrs.insert("5.9.100.248:18080"); - full_addrs.insert("163.172.182.165:18080"); - full_addrs.insert("161.67.132.39:18080"); - full_addrs.insert("198.74.231.92:18080"); - full_addrs.insert("195.154.123.123:28080"); - full_addrs.insert("212.83.172.165:28080"); + full_addrs.insert("74.91.23.186:11180"); + full_addrs.insert("192.187.114.114:11180"); } return full_addrs; } diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 7299a3199b..45d573224d 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1554,7 +1554,7 @@ namespace cryptonote res.top_block_hash = string_tools::pod_to_hex(top_hash); res.target_height = m_core.get_target_blockchain_height(); res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); - res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + res.target = m_core.get_blockchain_storage().get_current_hard_fork_version() < 2 && res.height < HARDFORK_1_HEIGHT ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase res.tx_pool_size = m_core.get_pool_transactions_count(); res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7e4937d2a1..81c305ae54 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4830,11 +4830,14 @@ uint64_t wallet2::get_per_kb_fee() { if(m_light_wallet) return m_light_wallet_per_kb_fee; + return FEE_PER_KB; +#if 0 bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1); if (!use_dyn_fee) return FEE_PER_KB; return get_dynamic_per_kb_fee_estimate(); +#endif } //---------------------------------------------------------------------------------------------------- int wallet2::get_fee_algorithm() @@ -8794,13 +8797,13 @@ std::string wallet2::make_uri(const std::string &address, const std::string &pay //---------------------------------------------------------------------------------------------------- bool wallet2::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) { - if (uri.substr(0, 7) != "monero:") + if (uri.substr(0, 5) != "aeon:") { - error = std::string("URI has wrong scheme (expected \"monero:\"): ") + uri; + error = std::string("URI has wrong scheme (expected \"aeon:\"): ") + uri; return false; } - std::string remainder = uri.substr(7); + std::string remainder = uri.substr(5); const char *ptr = strchr(remainder.c_str(), '?'); address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d0c89db463..f1a393a8f8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -70,8 +70,8 @@ else () endif (GTest_FOUND) file(COPY - data/wallet_9svHk1.keys - data/wallet_9svHk1 + data/wallet_WmsMW3.keys + data/wallet_WmsMW3 data/outputs data/unsigned_monero_tx data/signed_monero_tx diff --git a/tests/core_tests/block_validation.cpp b/tests/core_tests/block_validation.cpp index 94b636f824..cbba1ca4c9 100644 --- a/tests/core_tests/block_validation.cpp +++ b/tests/core_tests/block_validation.cpp @@ -45,7 +45,7 @@ namespace for (size_t i = 0; i < new_block_count; ++i) { block blk_next; - difficulty_type diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1); + difficulty_type diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1,get_block_height(blk_prev)+1); if (!generator.construct_block_manually(blk_next, blk_prev, miner_account, test_generator::bf_timestamp | test_generator::bf_diffic, 0, 0, blk_prev.timestamp, crypto::hash(), diffic)) return false; @@ -175,9 +175,9 @@ bool gen_block_invalid_nonce::generate(std::vector& events) co return false; // Create invalid nonce - difficulty_type diffic = next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1); - assert(1 < diffic); const block& blk_last = boost::get(events.back()); + difficulty_type diffic = next_difficulty(timestamps, commulative_difficulties,DIFFICULTY_TARGET_V1,get_block_height(blk_last)+1); + assert(1 < diffic); uint64_t timestamp = blk_last.timestamp; block blk_3; do @@ -572,7 +572,7 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev do { blk_last = boost::get(events.back()); - diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1); + diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1,get_block_height(blk_last)+1); if (!lift_up_difficulty(events, timestamps, cummulative_difficulties, generator, 1, blk_last, miner_account)) return false; std::cout << "Block #" << events.size() << ", difficulty: " << diffic << std::endl; @@ -587,7 +587,7 @@ bool gen_block_invalid_binary_format::generate(std::vector& ev std::vector tx_hashes; tx_hashes.push_back(get_transaction_hash(tx_0)); size_t txs_size = get_object_blobsize(tx_0); - diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1); + diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1,get_block_height(blk_last)+1); if (!generator.construct_block_manually(blk_test, blk_last, miner_account, test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp, crypto::hash(), diffic, transaction(), tx_hashes, txs_size)) diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index 4c5dc6c9a8..4cb01b1c8d 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -99,7 +99,7 @@ void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, st { const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx); uint64_t block_reward; - get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version); + get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version, get_block_height(blk) + 1); m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size); } @@ -533,7 +533,7 @@ bool construct_miner_tx_manually(size_t height, uint64_t already_generated_coins // This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE uint64_t block_reward; - if (!get_block_reward(0, 0, already_generated_coins, block_reward, 1)) + if (!get_block_reward(0, 0, already_generated_coins, block_reward, 1, height)) { LOG_PRINT_L0("Block is too big"); return false; diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 79a3a7cf45..623b529675 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -95,7 +95,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vectortimestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + 4, 4, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long crypto::hash(), 0, transaction(), std::vector(), 0, 1, 4), false, "Failed to generate block"); events.push_back(blocks[n]); @@ -113,7 +113,7 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector(), 0, 1, 4), false, "Failed to generate block"); events.push_back(blk); diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index e5047baf21..25e77e9e58 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -57,7 +57,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev miner_accounts[n].generate(); CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n], test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, - 2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + 2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long crypto::hash(), 0, transaction(), std::vector(), 0, 0, 2), false, "Failed to generate block"); events.push_back(blocks[n]); @@ -74,7 +74,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev cryptonote::block blk; CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, - 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long crypto::hash(), 0, transaction(), std::vector(), 0, 0, 2), false, "Failed to generate block"); events.push_back(blk); @@ -95,8 +95,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev sources.resize(1); tx_source_entry& src = sources.back(); - const size_t index_in_tx = 5; - src.amount = 30000000000000; + const size_t index_in_tx = 4; + src.amount = 70000000000000; for (int m = 0; m < 4; ++m) { src.push_output(m, boost::get(blocks[m].miner_tx.vout[index_in_tx].target).key, src.amount); } @@ -109,12 +109,12 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev //fill outputs entry tx_destination_entry td; td.addr = miner_accounts[n].get_keys().m_account_address; - td.amount = 7390000000000; + td.amount = 17390000000000; std::vector destinations; destinations.push_back(td); destinations.push_back(td); destinations.push_back(td); - destinations.push_back(td); // 30 -> 7.39 * 4 + destinations.push_back(td); // 70 -> 17.39 * 4 crypto::secret_key tx_key; std::vector additional_tx_keys; @@ -140,8 +140,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs, - 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long - crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 6, 4), + 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long + crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 4, 4), false, "Failed to generate block"); events.push_back(blk_txes[n]); blk_last = blk_txes[n]; @@ -154,8 +154,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev cryptonote::block blk; CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs, - 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long - crypto::hash(), 0, transaction(), std::vector(), 0, 6, 4), + 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long + crypto::hash(), 0, transaction(), std::vector(), 0, 4, 4), false, "Failed to generate block"); events.push_back(blk); blk_last = blk; @@ -165,7 +165,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev // create a tx from the requested ouputs std::vector sources; - size_t global_rct_idx = 6; // skip first coinbase (6 outputs) + size_t global_rct_idx = 4; // skip first coinbase (4 outputs) size_t rct_idx = 0; size_t pre_rct_idx = 0; for (size_t out_idx_idx = 0; out_idx[out_idx_idx] >= 0; ++out_idx_idx) { @@ -175,7 +175,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev src.real_output = 0; if (out_idx[out_idx_idx]) { // rct - src.amount = 7390000000000; + src.amount = 17390000000000; src.real_out_tx_key = get_tx_pub_key_from_extra(rct_txes[rct_idx/4]); src.real_output_in_tx_index = rct_idx&3; src.mask = rct_tx_masks[rct_idx]; @@ -187,20 +187,20 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev src.outputs.push_back(std::make_pair(global_rct_idx, ctkey)); ++rct_idx; ++global_rct_idx; - if (global_rct_idx % 10 == 0) - global_rct_idx += 6; // skip the coinbase + if (global_rct_idx % 8 == 0) + global_rct_idx += 4; // skip the coinbase } } else { // pre rct - src.amount = 5000000000000; + src.amount = 300000000000; src.real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(blocks[pre_rct_idx].miner_tx); - src.real_output_in_tx_index = 4; + src.real_output_in_tx_index = 3; src.mask = rct::identity(); src.rct = false; for (int m = 0; m <= mixin; ++m) { - src.push_output(m, boost::get(blocks[pre_rct_idx].miner_tx.vout[4].target).key, src.amount); + src.push_output(m, boost::get(blocks[pre_rct_idx].miner_tx.vout[3].target).key, src.amount); ++pre_rct_idx; } } diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index 6c2f91fcf7..5b6a22b341 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -53,7 +53,7 @@ bool gen_v2_tx_validation_base::generate_with(std::vector& eve miner_accounts[n].generate(); CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n], test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp, - 2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + 2, 2, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long crypto::hash(), 0, transaction(), std::vector(), 0, 0), false, "Failed to generate block"); events.push_back(blocks[n]); @@ -69,7 +69,7 @@ bool gen_v2_tx_validation_base::generate_with(std::vector& eve cryptonote::block blk; CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp, - 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long + 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long crypto::hash(), 0, transaction(), std::vector(), 0, 0), false, "Failed to generate block"); events.push_back(blk); diff --git a/tests/data/wallet_9svHk1 b/tests/data/wallet_9svHk1 deleted file mode 100644 index a49b6b50ef8766f77da46798cf7c66f8418dd8a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1971 zcmV;k2Tb^yopU+uqUk274^Riw0%uWL)YVSE>V+sQ)=9O}ws-Sf*{U2aazNlT8g`b+ z|EHb%(oKJ1@vZUEIcfv83(*m94}WGI>SUZ?JhF5Yq@iO;6-fEpYhP@1X$+}tJ#35T zq_2NO?F%*Rz)zK4QCVL%t*p{3;JDJiJp(5#-k}f7lTSUuYPHU)#85i<{ZQD_SM9tPgVFw1T?DdjcK=dsyudR(DSDdxv6CGgL+xF^ld1O$3l`Vo)GvNqE$ zswiLt4&EUew4TRYQRqmVy_huDIT^)SE|*W{4A(|wdnhzP?oq}lU1JFZe$`OOZCOJPETF&GWnLWyp9LT<$4Q{E(oxc})gk6sHMWk|RMC zE6vK2i>E}uc5At3cYCTauhM7%6m2M4L?Fc?)oRADGXmY;(BoDh+)8!fa2@x#>P^7T zo*TNcl#{jA9Xt}sM@4~Wveja|9JxL6NNf&x4EUuw=E;=`zSe@}7y~<9!Y&n2rGVe! z9dWV?=D^5}j8!xMhH*=)9kNSsvfFg_2C2Cw;0=mDHP`N!D$CyB)@w3n1Sk^48k67K6c|8%8OL~S~i~5q@pq$P!^#K5N*p@dBVwEncm3*_cH3M$073wKmE`dz zdSUq3ec6EQaEC7cy=XvK6OMW%bZ=G_hEgxT1QLw2z185E8bl!LBaLeovKG_O^)fAZ zvLC3rF9*3Xfs5k+RxsAK9EOvXR%@m?{DRN0-yXcc(0)d@m1Axoc<6S?0hARke2CxJ z$4cV`KmX~8If+>pc8@dOWj5eSbUw?K;EdNGPe3lL5wqt0oa5SN;|jd#S2(p4UcYBU zOCdFbwH0}O4=e}%r6Y(5T>e(?QY_SrdI;;QH{R31y4G1eG$`7LCuV5SJEhnU0<_tUT6;26LVu6Bh?{X@J~x;&5>l4Hex zTxJL?kia;mp6d%9xKi~~PDhVBfIY`sb~?}_us79Y`SNZIi*$8wdrlc-W{vRk5AP6& zqAU8VUT3oN(7c9Z`k-wpCnId8s4X4=S$tzZ1+`}yuR{nbAUbfgA>-8$J`Xt%P|F`N z?J0)F;r?XOj_LN@_p#p0TkjLuneIQ}H?Sk}YFPMGGn*W|-Ryz-lX<`!*jR|cq*zL! zdYDl9$^9eN&+W%vyzAuZfO>RabN>)YbC0P(p3B>@<{|QCnm1LSIpFW2$gd?M1jE+Dh?h_w;R`0=j%3T{aII2J=M>D z4n9&O{I_w7z|N1xtYl4r@JVpPGFZgDmL6l=gw^as$@C`(h#hOFeHtz2&DZ zV8Q0-wx3sOhR&eG0AtV_cSggc)3Kv=maMA>4DEIqwMoXH{pmgjPLCfncOZ>FnoSzP z9ui{W_>iLkJU!3y{W(`=9(a$s*rYlQt~Wc*+!0hYkf9!-j96Ao(l%gV%Q&*gGkrV% zaM_4Gj*jJ%2%wm2;+y+tWuzcQ#da``~UVF6RUJWJzok$(*AYCy+O z;@()ebi~J(EkAnYcdfr)o@6hMZ-JdcDfJfXpBQ11m!q0i{O!me(Be8B7`m=qMfO*7 zM%&D-6tjioe7DX`pgDT|l;-iJHQ^(8rP8{LYC0!83GzY&%lEMtRQGuz8vaOdc?@`nSB0avnd zgr@nRqR1I|rxqCY5)B#DOP0kT54$N9l%?k=p38M7HQ4gA;!y!$;hd)|>L_N1}Lc2d-S9;V@$`qsIMQp-zNOHRu^F)(}Z FyHv~3)xrP( diff --git a/tests/data/wallet_9svHk1.keys b/tests/data/wallet_9svHk1.keys deleted file mode 100644 index 3159e200b22224c51f4d7bec8f6cac4c4b6e5aae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 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 EFwkSOx&QzG literal 0 HcmV?d00001 diff --git a/tests/data/wallet_WmsMW3.keys b/tests/data/wallet_WmsMW3.keys new file mode 100644 index 0000000000000000000000000000000000000000..6ffb378e55a647bc18323cc660313c06a90f2ff4 GIT binary patch literal 925 zcmV;O17iGUfhAs0#T8VO2R$yXtvkK}P!`}Ir=#!8qtFtBM~+z`E&N2vdyR1r%JM-uSgN0FDwnY~Nfp(=8e=evx+zUr>Gk30k7H>YIQH zaMy0CW`iY+2qsfg_ped(2I}K-eh{WHs-k(VI~asSm&<&RUa)wCt3m!uG_6O{$CIX2 z*N%6UcWbS!r`SIHr~uW?yga9_Y=gZ+xS+w}T|R#g4j!!?#wOvH5nqQ zb=kA#;UvW~yi3{+N*FAIFgxnnEy==^j~GJdADQms*Cd_m4^FvlVXQEE(@Ojt{i@#j z?B=*=)77X3m<6rs#uiaX!{-z~J|9E+&$OW{RAX?qzW$L5M- zz1=VReKHcen8jahrm@j2s5~(#JS*Dp5okLAPoh;)@!#(Q94!ltp@iCS+rxrbuJWJ0 zC@$;{RbEaSS+D@78hQF^akA;R+;${9XXm;E!iV*Ja7 zw{Xlr-4jGfe`9K*wr+Oz_aoZ1tbxf`R#4yaRRbj1p55bjXbgkYm~X#6IHkW2uS9QH z|9e3?2ZuFOJteAsfJ|%{N}!QrabY`=C})03`QA=vHW`fa3?y3B+|KP85*2Ic9;Vk0 zdVg|;VvUBBA<^mbk)D$cH$0Hjx~Rgfh}iRka6K$Jlw$jRLq%2$*fu{SSp*3P(timestamps.begin() + begin, timestamps.begin() + end), - vector(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET); + vector(cumulative_difficulties.begin() + begin, cumulative_difficulties.begin() + end), DEFAULT_TEST_DIFFICULTY_TARGET, 1); if (res != difficulty) { cerr << "Wrong difficulty for block " << n << endl << "Expected: " << difficulty << endl diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 5cc95efbb4..9cbdee2fd1 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -42,7 +42,7 @@ set_property(TARGET hash-tests PROPERTY FOLDER "tests") -foreach (hash IN ITEMS fast slow tree extra-blake extra-groestl extra-jh extra-skein) +foreach (hash IN ITEMS fast slow-2m slow-1m tree extra-blake extra-groestl extra-jh extra-skein) add_test( NAME "hash-${hash}" COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 5f8b7856dc..231d34c116 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -51,6 +51,12 @@ extern "C" { } tree_hash((const char (*)[32]) data, length >> 5, hash); } + static void cn_slow_hash_2m(const void *data, size_t length, char *hash) { + cn_slow_hash(data, length, hash, 0); + } + static void cn_slow_hash_1m(const void *data, size_t length, char *hash) { + cn_slow_hash(data, length, hash, 1); + } } POP_WARNINGS @@ -58,7 +64,7 @@ extern "C" typedef void hash_f(const void *, size_t, char *); struct hash_func { const string name; hash_f &f; -} hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash}, {"tree", hash_tree}, +} hashes[] = {{"fast", cn_fast_hash}, {"slow-2m", cn_slow_hash_2m}, {"slow-1m", cn_slow_hash_1m}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}}; diff --git a/tests/hash/tests-slow-1m.txt b/tests/hash/tests-slow-1m.txt new file mode 100644 index 0000000000..5ff9e94762 --- /dev/null +++ b/tests/hash/tests-slow-1m.txt @@ -0,0 +1,4 @@ +1b73647a792df8724ce28fddc1e4b6f348dc39e6aa47c434fe400cec98ec2b91 6465206f6d6e69627573206475626974616e64756d +058293a2279aa3e3816c9e06bef3c0b3e4de8850f251a195ca4c2e35a1eebf58 6162756e64616e732063617574656c61206e6f6e206e6f636574 +afffe6c3084b0de799f6851389619bfe36be4705e4fb9e5039f8044b0decf8ea 63617665617420656d70746f72 +e1af332e932c1eda3bd13c44ea9daba9b38e4b517b2ecd21b704b090d165430a 6578206e6968696c6f206e6968696c20666974 diff --git a/tests/hash/tests-slow.txt b/tests/hash/tests-slow-2m.txt similarity index 100% rename from tests/hash/tests-slow.txt rename to tests/hash/tests-slow-2m.txt diff --git a/tests/performance_tests/cn_slow_hash.h b/tests/performance_tests/cn_slow_hash.h index bf9eb7501b..3c9a6137c2 100644 --- a/tests/performance_tests/cn_slow_hash.h +++ b/tests/performance_tests/cn_slow_hash.h @@ -62,7 +62,7 @@ class test_cn_slow_hash bool test() { crypto::hash hash; - crypto::cn_slow_hash(&m_data, sizeof(m_data), hash); + crypto::cn_slow_hash_2m(&m_data, sizeof(m_data), hash); return hash == m_expected_hash; } diff --git a/tests/unit_tests/base58.cpp b/tests/unit_tests/base58.cpp index 75a1930b53..336d112c4e 100644 --- a/tests/unit_tests/base58.cpp +++ b/tests/unit_tests/base58.cpp @@ -467,7 +467,7 @@ namespace "\x22\x09\x39\x68\x9e\xdf\x1a\xbd\x5b\xc1\xd0\x31\xf7\x3e\xcd\x6c" "\x99\x3a\xdd\x66\xd6\x80\x88\x70\x45\x6a\xfe\xb8\xe7\xee\xb6\x8d"); // DON'T ever use this as a destination for funds, as the keys are right above this comment... - std::string test_keys_addr_str = "4AzKEX4gXdJdNeM6dfiBFL7kqund3HYGvMBF3ttsNd9SfzgYB6L7ep1Yg1osYJzLdaKAYSLVh6e6jKnAuzj3bw1oGy9kXCb"; + std::string test_keys_addr_str = "Wmu42dY9EtiY5yQcdApjMb8tVPik5BC3LFdaevfbGq7X1KY5vdsWmUi5UQgse2GBZFbMsb47TFqBmPpdFHDDwDxR2ZuaW93oR"; } TEST(get_account_address_as_str, works_correctly) diff --git a/tests/unit_tests/block_reward.cpp b/tests/unit_tests/block_reward.cpp index e7d6f2c38f..7a6e88cab9 100644 --- a/tests/unit_tests/block_reward.cpp +++ b/tests/unit_tests/block_reward.cpp @@ -47,7 +47,7 @@ namespace }; #define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \ - m_block_not_too_big = get_block_reward(0, current_block_size, already_generated_coins, m_block_reward,1); \ + m_block_not_too_big = get_block_reward(0, current_block_size, already_generated_coins, m_block_reward,1,1); \ ASSERT_TRUE(m_block_not_too_big); \ ASSERT_EQ(m_block_reward, expected_reward); @@ -79,14 +79,14 @@ namespace protected: virtual void SetUp() { - m_block_not_too_big = get_block_reward(0, 0, already_generated_coins, m_standard_block_reward, 1); + m_block_not_too_big = get_block_reward(0, 0, already_generated_coins, m_standard_block_reward, 1, 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); } void do_test(size_t median_block_size, size_t current_block_size) { - m_block_not_too_big = get_block_reward(median_block_size, current_block_size, already_generated_coins, m_block_reward, 1); + m_block_not_too_big = get_block_reward(median_block_size, current_block_size, already_generated_coins, m_block_reward, 1, 1); } static const uint64_t already_generated_coins = 0; @@ -170,14 +170,14 @@ namespace m_last_block_sizes_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; - m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), 0, already_generated_coins, m_standard_block_reward, 1); + m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), 0, already_generated_coins, m_standard_block_reward, 1, 1); ASSERT_TRUE(m_block_not_too_big); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); } void do_test(size_t current_block_size) { - m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), current_block_size, already_generated_coins, m_block_reward, 1); + m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), current_block_size, already_generated_coins, m_block_reward, 1, 1); } static const uint64_t already_generated_coins = 0; diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 8b2c7e5f8b..50cb7c6b24 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -39,15 +39,15 @@ static const struct } test_addresses[] = { { - "9uvjbU54ZJb8j7Dcq1h3F1DnBRkxXdYUX4pbJ7mE3ghM8uF3fKzqRKRNAKYZXcNLqMg7MxjVVD2wKC2PALUwEveGSC3YSWD", + "UmisRZazUipRnY5UAw3sMZLTExBSCLvpCAP8sdPuPhJ2BjfsUoBes3eGUJx2fZf7S8Fsa7urJ7LamRTt4uucAtqk1wiPcpu3g", "2dd6e34a234c3e8b5d29a371789e4601e96dee4ea6f7ef79224d1a2d91164c01" }, { - "9ywDBAyDbb6QKFiZxDJ4hHZqZEQXXCR5EaYNcndUpqPDeE7rEgs6neQdZnhcDrWbURYK8xUjhuG2mVjJdmknrZbcG7NnbaB", + "Umjn6SGcmGc1Fz3USjnSYJcb771AkkNmqgmoAiHAu5713pewPFyAA9wcmemSiYxWtqcvARA3r4L6QUD7VXodxC7n2WQRzVXN8", "fac47aecc948ce9d3531aa042abb18235b1df632087c55a361b632ffdd6ede0c" }, { - "9t6Hn946u3eah5cuncH1hB5hGzsTUoevtf4SY7MHN5NgJZh2SFWsyVt3vUhuHyRKyrCQvr71Lfc1AevG3BXE11PQFoXDtD8", + "UmiTJonYS5c6emvHeethY1JkPzMHbAAHjQ4hH3zpo5JZBwZX8UxmjPjQG8tyg5HJaKVK6eWHENbPA4fKPw9eEkG71JgghLzW5", "bbd3175ef9fd9f5eefdc43035f882f74ad14c4cf1799d8b6f9001bc197175d02" } }; diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 2ef1097da2..dfee2a2a18 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -670,10 +670,10 @@ TEST(Serialization, serializes_ringct_types) TEST(Serialization, portability_wallet) { - const bool testnet = true; + const bool testnet = false; const bool restricted = false; tools::wallet2 w(testnet, restricted); - const boost::filesystem::path wallet_file = unit_test::data_dir / "wallet_9svHk1"; + const boost::filesystem::path wallet_file = unit_test::data_dir / "wallet_WmsMW3"; string password = "test"; bool r = false; try @@ -701,92 +701,267 @@ TEST(Serialization, portability_wallet) */ // blockchain ASSERT_TRUE(w.m_blockchain.size() == 1); - ASSERT_TRUE(epee::string_tools::pod_to_hex(w.m_blockchain[0]) == "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); + ASSERT_TRUE(epee::string_tools::pod_to_hex(w.m_blockchain.genesis()) == "c4890ac26e2806f95859c7725d66a248643fbc6b8fefcb5b8f408c24aa3c6f56"); // transfers (TODO) - ASSERT_TRUE(w.m_transfers.size() == 3); + ASSERT_TRUE(w.m_transfers.size() == 9); // account public address - ASSERT_TRUE(epee::string_tools::pod_to_hex(w.m_account_public_address.m_view_public_key) == "e47d4b6df6ab7339539148c2a03ad3e2f3434e5ab2046848e1f21369a3937cad"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(w.m_account_public_address.m_spend_public_key) == "13daa2af00ad26a372d317195de0bdd716f7a05d33bc4d7aff1664b6ee93c060"); + ASSERT_TRUE(epee::string_tools::pod_to_hex(w.m_account_public_address.m_view_public_key) == "bce39f3187ea721f2382f6d869ec4355c9fc98b8b48447797603c8f67e529f0f"); + ASSERT_TRUE(epee::string_tools::pod_to_hex(w.m_account_public_address.m_spend_public_key) == "314919dff11bdd66b6ef9ab476bed945027899e5fe97ed05db73a981d9d8fe49"); // key images - ASSERT_TRUE(w.m_key_images.size() == 3); + ASSERT_TRUE(w.m_key_images.size() == 9); { - crypto::key_image ki[3]; - epee::string_tools::hex_to_pod("c5680d3735b90871ca5e3d90cd82d6483eed1151b9ab75c2c8c3a7d89e00a5a8", ki[0]); - epee::string_tools::hex_to_pod("d54cbd435a8d636ad9b01b8d4f3eb13bd0cf1ce98eddf53ab1617f9b763e66c0", ki[1]); - epee::string_tools::hex_to_pod("6c3cd6af97c4070a7aef9b1344e7463e29c7cd245076fdb65da447a34da3ca76", ki[2]); - ASSERT_TRUE(w.m_key_images.find(ki[0])->second == 0); - ASSERT_TRUE(w.m_key_images.find(ki[1])->second == 1); - ASSERT_TRUE(w.m_key_images.find(ki[2])->second == 2); + const std::vector ki_str = + { + "10dfc5688233609ff5cfca7c150c2567fa36c72687f1b6352a703657d8f6130d", + "8cdf30335036aff70bfac09199706ad0a1716f572edba95f46858df67e8aef8a", + "0f3cd94bb830262476314fcb4b46e2d7e6607f109efda6d709560f5f04607a85", + "0b368153f04042bc53aa69ec31e17e14432464b3d24804bbfa71e1747be32851", + "eaaf14fe0a0c4e11c03628484216c0e71c4580bbdcb27d5451b8201af2b33e25", + "57bc45a7e7e56c6aca04043ac39338cab16d0e17312587ae8a75dd35909d106f", + "719f399afa6af07743e1cfb7f20d28b7843ca0002f625130fc7cc3ea8cdec31d", + "080af77965131b626ac76501cfa76d8fead27090e8fd28b4960f7d65bcd56add", + "53fae2e3d35cfedc38c5054f36b0be28b85591cd1c2ed4e85a4206135a6c4f5b", + }; + for (size_t i = 0; i < ki_str.size(); ++i) + { + crypto::key_image ki; + epee::string_tools::hex_to_pod(ki_str[i], ki); + ASSERT_EQ_MAP(i, w.m_key_images, ki); + } } // unconfirmed txs - ASSERT_TRUE(w.m_unconfirmed_txs.size() == 0); + ASSERT_TRUE(w.m_unconfirmed_txs.size() == 1); + { + const crypto::hash &txid = w.m_unconfirmed_txs.begin()->first; + const tools::wallet2::unconfirmed_transfer_details &utd = w.m_unconfirmed_txs.begin()->second; + ASSERT_EQ("733a11a5138ae6c65ffd849b1b53e45d57b82a4696389e026dc2c825164995a3", epee::string_tools::pod_to_hex(txid)); + ASSERT_EQ(100000000000, utd.m_amount_in); + ASSERT_EQ(98000000000, utd.m_amount_out); + ASSERT_EQ(88000000000, utd.m_change); + ASSERT_EQ(1518138349, utd.m_sent_time); + ASSERT_EQ(1, utd.m_dests.size()); + if (utd.m_dests.size() == 1) + { + const cryptonote::tx_destination_entry &de = utd.m_dests[0]; + ASSERT_EQ(10000000000, de.amount); + ASSERT_EQ("XnY4kPxa5z6Gc6Dxjpy3u6XM4vPqTrWGg8J7RMNquDorZ5yRn7CwAmeRSLpExDLRLb1GBnVkQtunr1W5Y2NRrc4W2S7itBqWQ", cryptonote::get_account_address_as_str(false, de.is_subaddress, de.addr)); + } + ASSERT_EQ(null_hash, utd.m_payment_id); + ASSERT_EQ(tools::wallet2::unconfirmed_transfer_details::failed, utd.m_state); + ASSERT_EQ(1518138349, utd.m_timestamp); + ASSERT_EQ(0, utd.m_subaddr_account); + ASSERT_EQ(1, utd.m_subaddr_indices.size()); + if (utd.m_subaddr_indices.size() == 1) + ASSERT_EQ(0, *utd.m_subaddr_indices.begin()); + } // payments - ASSERT_TRUE(w.m_payments.size() == 2); + ASSERT_TRUE(w.m_payments.size() == 3); { - auto pd0 = w.m_payments.begin(); - auto pd1 = pd0; - ++pd1; - ASSERT_TRUE(epee::string_tools::pod_to_hex(pd0->first) == "0000000000000000000000000000000000000000000000000000000000000000"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(pd1->first) == "0000000000000000000000000000000000000000000000000000000000000000"); - if (epee::string_tools::pod_to_hex(pd0->second.m_tx_hash) == "ec34c9bb12b99af33d49691384eee5bed9171498ff04e59516505f35d1fc5efc") - swap(pd0, pd1); - ASSERT_TRUE(epee::string_tools::pod_to_hex(pd0->second.m_tx_hash) == "15024343b38e77a1a9860dfed29921fa17e833fec837191a6b04fa7cb9605b8e"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(pd1->second.m_tx_hash) == "ec34c9bb12b99af33d49691384eee5bed9171498ff04e59516505f35d1fc5efc"); - ASSERT_TRUE(pd0->second.m_amount == 13400845012231); - ASSERT_TRUE(pd1->second.m_amount == 1200000000000); - ASSERT_TRUE(pd0->second.m_block_height == 818424); - ASSERT_TRUE(pd1->second.m_block_height == 818522); - ASSERT_TRUE(pd0->second.m_unlock_time == 818484); - ASSERT_TRUE(pd1->second.m_unlock_time == 0); - ASSERT_TRUE(pd0->second.m_timestamp == 1483263366); - ASSERT_TRUE(pd1->second.m_timestamp == 1483272963); + const struct + { + const char *txid; + uint64_t amount; + uint64_t block_height; + uint64_t timestamp; + } payments[] = + { + { "7dab7ebeb69e14b1321e928da174e5ea83a413c521141035275c357ab497f10b", 100000000000, 921444, 1518020620 }, + { "d6ac8a0edd08a7c694f8d9cf768961f7fe987622d20e4e27e33032a7d3d18dc5", 200000000000, 921447, 1518021834 }, + { "be1f73ccf9ba14ee24cd9a39a008394aa145813c2c40ca708f360d516c165a90", 100000000000, 921461, 1518026459 }, + + }; + for (const auto &i : payments) + { + crypto::hash txid; + epee::string_tools::hex_to_pod(i.txid, txid); + size_t j = 0; + for (const auto &p : w.m_payments) + { + ASSERT_EQ(null_hash, p.first); + const tools::wallet2::payment_details &pd = p.second; + ASSERT_EQ(0, pd.m_unlock_time); + ASSERT_EQ(0, pd.m_subaddr_index.major); + ASSERT_EQ(0, pd.m_subaddr_index.minor); + if (pd.m_tx_hash == txid) + { + ASSERT_EQ(i.amount, pd.m_amount); + ASSERT_EQ(i.block_height, pd.m_block_height); + ASSERT_EQ(i.timestamp, pd.m_timestamp); + break; + } + ++j; + } + ASSERT_TRUE(j < w.m_payments.size()); + } } // tx keys - ASSERT_TRUE(w.m_tx_keys.size() == 2); + ASSERT_TRUE(w.m_tx_keys.size() == 6); { - auto tx_key0 = w.m_tx_keys.begin(); - auto tx_key1 = tx_key0; - ++tx_key1; - if (epee::string_tools::pod_to_hex(tx_key0->first) == "6e7013684d35820f66c6679197ded9329bfe0e495effa47e7b25258799858dba") - swap(tx_key0, tx_key1); - ASSERT_TRUE(epee::string_tools::pod_to_hex(tx_key0->first) == "b9aac8c020ab33859e0c0b6331f46a8780d349e7ac17b067116e2d87bf48daad"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(tx_key1->first) == "6e7013684d35820f66c6679197ded9329bfe0e495effa47e7b25258799858dba"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(tx_key0->second) == "bf3614c6de1d06c09add5d92a5265d8c76af706f7bc6ac830d6b0d109aa87701"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(tx_key1->second) == "e556884246df5a787def6732c6ea38f1e092fa13e5ea98f732b99c07a6332003"); + const struct + { + const char *txid; + const char *txkey; + } pairs[] = + { + { "f4eb7d1e1a30edd1dff5f6203eb2286cd1ce2d0f6286903d12dd2861898dc021", "6875cd9855683c9cd7eb344ad595163437b080429e2fc84f908a4046a0137501" }, + { "5e37f3b5e388db5a50aa66215a0d8e22db9e97599a3bf7085def2481f69c50be", "df96f639413e99c21072050037664e1260d51a994d646308a341347fdd223001" }, + { "6241edce6bcaa3d9a15e352cc439b98f736ac002078c913e34df42ec9485a4bc", "9617936b30a8a391b2273e6fa5df8c115a661efee8e75b48507aeadf0583620b" }, + { "2b529ebbf9ea9ac20924b0bafe0a2a4caa973e6c3258b1f4683f60acae3284db", "3b72a34fc842f0deba56d219d6b4c124f313a8a2c9ee3f014c70c85e178a800c" }, + { "2596faa2fb12e0ae3c9b8480617b95637bc8b99e0637760a94ae421ecea71262", "4625cd7f96312fab5424be2f774fae792c5810f91f36c48416be35fc8cee470f" }, + { "733a11a5138ae6c65ffd849b1b53e45d57b82a4696389e026dc2c825164995a3", "526c0645dec1251462e1de96da37903734b55515f619e179f96004f8d768330c" }, + }; + for (const auto &i : pairs) + { + crypto::hash txid; + crypto::secret_key txkey; + epee::string_tools::hex_to_pod(i.txid, txid); + epee::string_tools::hex_to_pod(i.txkey, txkey); + ASSERT_EQ_MAP(txkey, w.m_tx_keys, txid); + } } // confirmed txs - ASSERT_TRUE(w.m_confirmed_txs.size() == 1); + ASSERT_TRUE(w.m_confirmed_txs.size() == 5); + { + const struct + { + const char *txid; + std::pair dests; + uint64_t amount_in; + uint64_t amount_out; + uint64_t change; + uint64_t block_height; + uint64_t timestamp; + } confirmed_txs[] = + { + { + "f4eb7d1e1a30edd1dff5f6203eb2286cd1ce2d0f6286903d12dd2861898dc021", + { 10000000000, "Wmtvkx78QVH2VaMVeyCEudjaS9Vt7WE8WZjRgnGBsk8Q4RkKdpBWSUCNqxTuZ5Gi1Y3YcSnc8RoppNeQsZHheTXV34fLMesDY" }, + 200000000000, + 190000000000, + 180000000000, + 921458, + 1518025785 + }, + { + "6241edce6bcaa3d9a15e352cc439b98f736ac002078c913e34df42ec9485a4bc", + { 10000000000, "XnXQHQzk7LybGcHoZuGiXE74E4yeUBccxWT6Lvs69t5XBMfcdH17SXD2EwkwkjkQmxB2eTfqRUd1bTEf7WrXZY1a27wrcpdvJ" }, + 80000000000, + 70000000000, + 60000000000, + 921540, + 1518046039 + }, + { + "5e37f3b5e388db5a50aa66215a0d8e22db9e97599a3bf7085def2481f69c50be", + { 10000000000, "Wmtvkx78QVH2VaMVeyCEudjaS9Vt7WE8WZjRgnGBsk8Q4RkKdpBWSUCNqxTuZ5Gi1Y3YcSnc8RoppNeQsZHheTXV34fLMesDY" }, + 100000000000, + 90000000000, + 80000000000, + 921460, + 1518026191 + }, + { + "2b529ebbf9ea9ac20924b0bafe0a2a4caa973e6c3258b1f4683f60acae3284db", + { 20000000000, "WmtPL71Nan2dYKqzSPgMzhCohTLUtHsjCUWvq8yyYNhaPKKg9X48We7CfDkyMy9RadbLFcULjCadVBwnRXy6eLnj2gMkRspV6" }, + 80000000000, + 70000000000, + 50000000000, + 921541, + 1518047010 + }, + { + "2596faa2fb12e0ae3c9b8480617b95637bc8b99e0637760a94ae421ecea71262", + { 10000000000, "XnY4kPxa5z6Gc6Dxjpy3u6XM4vPqTrWGg8J7RMNquDorZ5yRn7CwAmeRSLpExDLRLb1GBnVkQtunr1W5Y2NRrc4W2S7itBqWQ" }, + 60000000000, + 50000000000, + 40000000000, + 921941, + 1518144449 + }, + }; + for (const auto &p : w.m_confirmed_txs) + { + const tools::wallet2::confirmed_transfer_details &td = p.second; + ASSERT_EQ(null_hash, td.m_payment_id); + ASSERT_EQ(0, td.m_unlock_time); + ASSERT_EQ(0, td.m_subaddr_account); + ASSERT_EQ(1, td.m_subaddr_indices.size()); + if (td.m_subaddr_indices.size() == 1) + ASSERT_EQ(0, *td.m_subaddr_indices.begin()); + ASSERT_EQ(1, td.m_dests.size()); + + auto i = std::begin(confirmed_txs); + while (i != std::end(confirmed_txs)) + { + if (epee::string_tools::pod_to_hex(p.first) == i->txid) + { + const cryptonote::tx_destination_entry &de = td.m_dests[0]; + ASSERT_EQ(i->dests.first, de.amount); + ASSERT_EQ(i->dests.second, cryptonote::get_account_address_as_str(false, de.is_subaddress, de.addr)); + ASSERT_EQ(i->amount_in, td.m_amount_in); + ASSERT_EQ(i->amount_out, td.m_amount_out); + ASSERT_EQ(i->change, td.m_change); + ASSERT_EQ(i->block_height, td.m_block_height); + ASSERT_EQ(i->timestamp, td.m_timestamp); + break; + } + ++i; + } + ASSERT_TRUE(i != std::end(confirmed_txs)); + } + } // tx notes ASSERT_TRUE(w.m_tx_notes.size() == 2); { crypto::hash h[2]; - epee::string_tools::hex_to_pod("15024343b38e77a1a9860dfed29921fa17e833fec837191a6b04fa7cb9605b8e", h[0]); - epee::string_tools::hex_to_pod("6e7013684d35820f66c6679197ded9329bfe0e495effa47e7b25258799858dba", h[1]); - ASSERT_TRUE(w.m_tx_notes.find(h[0])->second == "sample note"); - ASSERT_TRUE(w.m_tx_notes.find(h[1])->second == "sample note 2"); + epee::string_tools::hex_to_pod("f4eb7d1e1a30edd1dff5f6203eb2286cd1ce2d0f6286903d12dd2861898dc021", h[0]); + epee::string_tools::hex_to_pod("5e37f3b5e388db5a50aa66215a0d8e22db9e97599a3bf7085def2481f69c50be", h[1]); + ASSERT_EQ_MAP("sample note", w.m_tx_notes, h[0]); + ASSERT_EQ_MAP("sample note 2", w.m_tx_notes, h[1]); } // unconfirmed payments ASSERT_TRUE(w.m_unconfirmed_payments.size() == 0); // pub keys - ASSERT_TRUE(w.m_pub_keys.size() == 3); + ASSERT_TRUE(w.m_pub_keys.size() == 9); { - crypto::public_key pubkey[3]; - epee::string_tools::hex_to_pod("33f75f264574cb3a9ea5b24220a5312e183d36dc321c9091dfbb720922a4f7b0", pubkey[0]); - epee::string_tools::hex_to_pod("5066ff2ce9861b1d131cf16eeaa01264933a49f28242b97b153e922ec7b4b3cb", pubkey[1]); - epee::string_tools::hex_to_pod("0d8467e16e73d16510452b78823e082e05ee3a63788d40de577cf31eb555f0c8", pubkey[2]); - ASSERT_TRUE(w.m_pub_keys.find(pubkey[0])->second == 0); - ASSERT_TRUE(w.m_pub_keys.find(pubkey[1])->second == 1); - ASSERT_TRUE(w.m_pub_keys.find(pubkey[2])->second == 2); + const std::vector pubkey_str = + { + "293d4d17f3b16b56fa9aa634f05bfc0c995139fb4e7725246555ca11d412caf7", + "c0244f13fd3d215fb2e74b146f6f23d6bdf93e40949fd114af3fd23471bf3099", + "0aef2bf3d3f14f4754f385abe1dcfcb7d8e626105301269d5630eb7e1d88ad50", + "f938a6406a09de6b280d80ed4159ad8090e9460d41547893759e8cd3850a0875", + "bc79bd0fde68b398078f6c4c98223e9be89dab319ff99cf0b34df8c3008a3e4d", + "a8615506be09a3f27edb967a78ea7b5c5c9f360f49f8139c0921587d54c8b93a", + "27c55f695d69a6a297e0c2f1a3cf836e7a87f080015d96e1fbfe073927771886", + "14968bc3afb31503af6c8bef08e69a47058cc10ea7442c02595263df019feb40", + "517df7a97069791ac98ee6fab99c030897a3fa1921fdd9c49ee0cd1dbeb62981", + }; + for (size_t i = 0; i < pubkey_str.size(); ++i) + { + crypto::public_key pubkey; + epee::string_tools::hex_to_pod(pubkey_str[i], pubkey); + ASSERT_EQ_MAP(i, w.m_pub_keys, pubkey); + } } // address book ASSERT_TRUE(w.m_address_book.size() == 1); { - auto address_book_row = w.m_address_book.begin(); - ASSERT_TRUE(epee::string_tools::pod_to_hex(address_book_row->m_address.m_spend_public_key) == "9bc53a6ff7b0831c9470f71b6b972dbe5ad1e8606f72682868b1dda64e119fb3"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(address_book_row->m_address.m_view_public_key) == "49fece1ef97dc0c0f7a5e2106e75e96edd910f7e86b56e1e308cd0cf734df191"); - ASSERT_TRUE(epee::string_tools::pod_to_hex(address_book_row->m_payment_id) == "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); - ASSERT_TRUE(address_book_row->m_description == "testnet wallet 9y52S6"); + const tools::wallet2::address_book_row &x = w.m_address_book[0]; + ASSERT_EQ("XnXQHQzk7LybGcHoZuGiXE74E4yeUBccxWT6Lvs69t5XBMfcdH17SXD2EwkwkjkQmxB2eTfqRUd1bTEf7WrXZY1a27wrcpdvJ", cryptonote::get_account_address_as_str(false, x.m_is_subaddress, x.m_address)); + ASSERT_EQ(null_hash, x.m_payment_id); + ASSERT_EQ("John Doe", x.m_description); } + // subaddresses + ASSERT_EQ(2, w.m_subaddresses.size()); + ASSERT_EQ(2, w.m_subaddresses_inv.size()); + ASSERT_EQ(1, w.m_subaddress_labels.size()); + ASSERT_EQ(2, w.m_subaddress_labels[0].size()); + ASSERT_EQ_MAP((cryptonote::subaddress_index{0,0}), w.m_subaddresses, w.get_subaddress_spend_public_key({0,0})); + ASSERT_EQ_MAP((cryptonote::subaddress_index{0,1}), w.m_subaddresses, w.get_subaddress_spend_public_key({0,1})); + ASSERT_EQ_MAP(w.get_subaddress_spend_public_key({0,0}), w.m_subaddresses_inv, (cryptonote::subaddress_index{0,0})); + ASSERT_EQ_MAP(w.get_subaddress_spend_public_key({0,1}), w.m_subaddresses_inv, (cryptonote::subaddress_index{0,1})); + ASSERT_EQ("foo", w.m_subaddress_labels[0][0]); + ASSERT_EQ("bar", w.m_subaddress_labels[0][1]); } #define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" @@ -988,15 +1163,15 @@ TEST(Serialization, portability_unsigned_tx) ASSERT_TRUE(epee::string_tools::pod_to_hex(tse.mask) == "789bafff169ef206aa21219342c69ca52ce1d78d776c10b21d14bdd960fc7703"); // tcd.change_dts ASSERT_TRUE(tcd.change_dts.amount == 9631208773403); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, tcd.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, tcd.change_dts.addr) == "UmiR3Pk2Lkk7Tqp2wRdw8KYkgtP37b8DRDxf8irRXX4WZBK7aqmwdhULGpMc4dBpvmcSZkg1rP3qVJSh9ZSrd1Z4255z4xXSd"); // tcd.splitted_dsts ASSERT_TRUE(tcd.splitted_dsts.size() == 2); auto& splitted_dst0 = tcd.splitted_dsts[0]; auto& splitted_dst1 = tcd.splitted_dsts[1]; ASSERT_TRUE(splitted_dst0.amount == 1400000000000); ASSERT_TRUE(splitted_dst1.amount == 9631208773403); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst0.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA"); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst1.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst0.addr) == "UmjX2WchhNkfxFk3CMrk4MjiwDmYZdeFuMJzbQotTQgJE81u2JCxgHra8WT7KiZNyG14Xb5Yu7bERSuL41fTka4W2G4PQ7a6K"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst1.addr) == "UmiR3Pk2Lkk7Tqp2wRdw8KYkgtP37b8DRDxf8irRXX4WZBK7aqmwdhULGpMc4dBpvmcSZkg1rP3qVJSh9ZSrd1Z4255z4xXSd"); // tcd.selected_transfers ASSERT_TRUE(tcd.selected_transfers.size() == 1); ASSERT_TRUE(tcd.selected_transfers.front() == 2); @@ -1009,7 +1184,7 @@ TEST(Serialization, portability_unsigned_tx) ASSERT_TRUE(tcd.dests.size() == 1); auto& dest = tcd.dests[0]; ASSERT_TRUE(dest.amount == 1400000000000); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, dest.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, dest.addr) == "UmjX2WchhNkfxFk3CMrk4MjiwDmYZdeFuMJzbQotTQgJE81u2JCxgHra8WT7KiZNyG14Xb5Yu7bERSuL41fTka4W2G4PQ7a6K"); // transfers ASSERT_TRUE(exported_txs.transfers.size() == 3); auto& td0 = exported_txs.transfers[0]; @@ -1102,7 +1277,7 @@ TEST(Serialization, portability_signed_tx) ASSERT_FALSE(ptx.dust_added_to_fee); // ptx.change.{amount, addr} ASSERT_TRUE(ptx.change_dts.amount == 9631208773403); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, ptx.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, ptx.change_dts.addr) == "UmiR3Pk2Lkk7Tqp2wRdw8KYkgtP37b8DRDxf8irRXX4WZBK7aqmwdhULGpMc4dBpvmcSZkg1rP3qVJSh9ZSrd1Z4255z4xXSd"); // ptx.selected_transfers ASSERT_TRUE(ptx.selected_transfers.size() == 1); ASSERT_TRUE(ptx.selected_transfers.front() == 2); @@ -1112,7 +1287,7 @@ TEST(Serialization, portability_signed_tx) // ptx.dests ASSERT_TRUE(ptx.dests.size() == 1); ASSERT_TRUE(ptx.dests[0].amount == 1400000000000); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, ptx.dests[0].addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, ptx.dests[0].addr) == "UmjX2WchhNkfxFk3CMrk4MjiwDmYZdeFuMJzbQotTQgJE81u2JCxgHra8WT7KiZNyG14Xb5Yu7bERSuL41fTka4W2G4PQ7a6K"); // ptx.construction_data auto& tcd = ptx.construction_data; ASSERT_TRUE(tcd.sources.size() == 1); @@ -1143,15 +1318,15 @@ TEST(Serialization, portability_signed_tx) ASSERT_TRUE(epee::string_tools::pod_to_hex(tse.mask) == "789bafff169ef206aa21219342c69ca52ce1d78d776c10b21d14bdd960fc7703"); // ptx.construction_data.change_dts ASSERT_TRUE(tcd.change_dts.amount == 9631208773403); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, tcd.change_dts.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, tcd.change_dts.addr) == "UmiR3Pk2Lkk7Tqp2wRdw8KYkgtP37b8DRDxf8irRXX4WZBK7aqmwdhULGpMc4dBpvmcSZkg1rP3qVJSh9ZSrd1Z4255z4xXSd"); // ptx.construction_data.splitted_dsts ASSERT_TRUE(tcd.splitted_dsts.size() == 2); auto& splitted_dst0 = tcd.splitted_dsts[0]; auto& splitted_dst1 = tcd.splitted_dsts[1]; ASSERT_TRUE(splitted_dst0.amount == 1400000000000); ASSERT_TRUE(splitted_dst1.amount == 9631208773403); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst0.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA"); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst1.addr) == "9svHk1wHPo3ULf2AZykghzcye6sitaRE4MaDjPC6uanTHCynHjJHZaiAb922PojE1GexhhRt1LVf5DC43feyrRZMLXQr3mk"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst0.addr) == "UmjX2WchhNkfxFk3CMrk4MjiwDmYZdeFuMJzbQotTQgJE81u2JCxgHra8WT7KiZNyG14Xb5Yu7bERSuL41fTka4W2G4PQ7a6K"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, splitted_dst1.addr) == "UmiR3Pk2Lkk7Tqp2wRdw8KYkgtP37b8DRDxf8irRXX4WZBK7aqmwdhULGpMc4dBpvmcSZkg1rP3qVJSh9ZSrd1Z4255z4xXSd"); // ptx.construction_data.selected_transfers ASSERT_TRUE(tcd.selected_transfers.size() == 1); ASSERT_TRUE(tcd.selected_transfers.front() == 2); @@ -1164,7 +1339,7 @@ TEST(Serialization, portability_signed_tx) ASSERT_TRUE(tcd.dests.size() == 1); auto& dest = tcd.dests[0]; ASSERT_TRUE(dest.amount == 1400000000000); - ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, dest.addr) == "9xnhrMczQkPeoGi6dyu6BgKAYX4tZsDs6KHCkyTStDBKL4M4pM1gfCR3utmTAcSaKHGa1R5o266FbdnubErmij3oMdLyYgA"); + ASSERT_TRUE(cryptonote::get_account_address_as_str(testnet, false, dest.addr) == "UmjX2WchhNkfxFk3CMrk4MjiwDmYZdeFuMJzbQotTQgJE81u2JCxgHra8WT7KiZNyG14Xb5Yu7bERSuL41fTka4W2G4PQ7a6K"); // key_images ASSERT_TRUE(exported_txs.key_images.size() == 3); auto& ki0 = exported_txs.key_images[0]; diff --git a/tests/unit_tests/uri.cpp b/tests/unit_tests/uri.cpp index 0f727982bc..a3c4c3d368 100644 --- a/tests/unit_tests/uri.cpp +++ b/tests/unit_tests/uri.cpp @@ -29,8 +29,8 @@ #include "gtest/gtest.h" #include "wallet/wallet2.h" -#define TEST_ADDRESS "9tTLtauaEKSj7xoVXytVH32R1pLZBk4VV4mZFGEh4wkXhDWqw1soPyf3fGixf1kni31VznEZkWNEza9d5TvjWwq5PaohYHC" -#define TEST_INTEGRATED_ADDRESS "A4A1uPj4qaxj7xoVXytVH32R1pLZBk4VV4mZFGEh4wkXhDWqw1soPyf3fGixf1kni31VznEZkWNEza9d5TvjWwq5acaPMJfMbn3ReTsBpp" +#define TEST_ADDRESS "UmiQzYrNGovhTZNv9nFaU7EPKpdbqcHCFA4cf5R5acKCjJ5pbgtqqWFdYaVx3ixuSUfWyUAAikZywG5qxG6mcNk518cGzqkqe" +#define TEST_INTEGRATED_ADDRESS "UzSvSz3xCMAhTZNv9nFaU7EPKpdbqcHCFA4cf5R5acKCjJ5pbgtqqWFdYaVx3ixuSUfWyUAAikZywG5qxG6mcNk53ZE9jQDXFL21H8sxd77M" // included payment id: #define PARSE_URI(uri, expected) \ @@ -48,7 +48,7 @@ TEST(uri, empty_string) TEST(uri, no_scheme) { - PARSE_URI("monero", false); + PARSE_URI("aeon", false); } TEST(uri, bad_scheme) @@ -58,75 +58,75 @@ TEST(uri, bad_scheme) TEST(uri, scheme_not_first) { - PARSE_URI(" monero:", false); + PARSE_URI(" aeon:", false); } TEST(uri, no_body) { - PARSE_URI("monero:", false); + PARSE_URI("aeon:", false); } TEST(uri, no_address) { - PARSE_URI("monero:?", false); + PARSE_URI("aeon:?", false); } TEST(uri, bad_address) { - PARSE_URI("monero:44444", false); + PARSE_URI("aeon:44444", false); } TEST(uri, good_address) { - PARSE_URI("monero:" TEST_ADDRESS, true); + PARSE_URI("aeon:" TEST_ADDRESS, true); ASSERT_EQ(address, TEST_ADDRESS); } TEST(uri, good_integrated_address) { - PARSE_URI("monero:" TEST_INTEGRATED_ADDRESS, true); + PARSE_URI("aeon:" TEST_INTEGRATED_ADDRESS, true); } TEST(uri, parameter_without_inter) { - PARSE_URI("monero:" TEST_ADDRESS"&amount=1", false); + PARSE_URI("aeon:" TEST_ADDRESS"&amount=1", false); } TEST(uri, parameter_without_equals) { - PARSE_URI("monero:" TEST_ADDRESS"?amount", false); + PARSE_URI("aeon:" TEST_ADDRESS"?amount", false); } TEST(uri, parameter_without_value) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_amount=", false); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_amount=", false); } TEST(uri, negative_amount) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_amount=-1", false); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_amount=-1", false); } TEST(uri, bad_amount) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_amount=alphanumeric", false); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_amount=alphanumeric", false); } TEST(uri, duplicate_parameter) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_amount=1&tx_amount=1", false); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_amount=1&tx_amount=1", false); } TEST(uri, unknown_parameter) { - PARSE_URI("monero:" TEST_ADDRESS"?unknown=1", true); + PARSE_URI("aeon:" TEST_ADDRESS"?unknown=1", true); ASSERT_EQ(unknown_parameters.size(), 1); ASSERT_EQ(unknown_parameters[0], "unknown=1"); } TEST(uri, unknown_parameters) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_amount=1&unknown=1&tx_description=desc&foo=bar", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_amount=1&unknown=1&tx_description=desc&foo=bar", true); ASSERT_EQ(unknown_parameters.size(), 2); ASSERT_EQ(unknown_parameters[0], "unknown=1"); ASSERT_EQ(unknown_parameters[1], "foo=bar"); @@ -134,84 +134,84 @@ TEST(uri, unknown_parameters) TEST(uri, empty_payment_id) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_payment_id=", false); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_payment_id=", false); } TEST(uri, bad_payment_id) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_payment_id=1234567890", false); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_payment_id=1234567890", false); } TEST(uri, short_payment_id) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_payment_id=1234567890123456", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_payment_id=1234567890123456", true); ASSERT_EQ(address, TEST_ADDRESS); ASSERT_EQ(payment_id, "1234567890123456"); } TEST(uri, long_payment_id) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_payment_id=1234567890123456789012345678901234567890123456789012345678901234", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_payment_id=1234567890123456789012345678901234567890123456789012345678901234", true); ASSERT_EQ(address, TEST_ADDRESS); ASSERT_EQ(payment_id, "1234567890123456789012345678901234567890123456789012345678901234"); } TEST(uri, payment_id_with_integrated_address) { - PARSE_URI("monero:" TEST_INTEGRATED_ADDRESS"?tx_payment_id=1234567890123456", false); + PARSE_URI("aeon:" TEST_INTEGRATED_ADDRESS"?tx_payment_id=1234567890123456", false); } TEST(uri, empty_description) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_description=", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_description=", true); ASSERT_EQ(description, ""); } TEST(uri, empty_recipient_name) { - PARSE_URI("monero:" TEST_ADDRESS"?recipient_name=", true); + PARSE_URI("aeon:" TEST_ADDRESS"?recipient_name=", true); ASSERT_EQ(recipient_name, ""); } TEST(uri, non_empty_description) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_description=foo", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_description=foo", true); ASSERT_EQ(description, "foo"); } TEST(uri, non_empty_recipient_name) { - PARSE_URI("monero:" TEST_ADDRESS"?recipient_name=foo", true); + PARSE_URI("aeon:" TEST_ADDRESS"?recipient_name=foo", true); ASSERT_EQ(recipient_name, "foo"); } TEST(uri, url_encoding) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_description=foo%20bar", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_description=foo%20bar", true); ASSERT_EQ(description, "foo bar"); } TEST(uri, non_alphanumeric_url_encoding) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_description=foo%2x", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_description=foo%2x", true); ASSERT_EQ(description, "foo%2x"); } TEST(uri, truncated_url_encoding) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_description=foo%2", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_description=foo%2", true); ASSERT_EQ(description, "foo%2"); } TEST(uri, percent_without_url_encoding) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_description=foo%", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_description=foo%", true); ASSERT_EQ(description, "foo%"); } TEST(uri, url_encoded_once) { - PARSE_URI("monero:" TEST_ADDRESS"?tx_description=foo%2020", true); + PARSE_URI("aeon:" TEST_ADDRESS"?tx_description=foo%2020", true); ASSERT_EQ(description, "foo 20"); } From 04d3f27a18405a114901d16c8d35a4b9089c5903 Mon Sep 17 00:00:00 2001 From: stoffu Date: Wed, 7 Feb 2018 18:45:59 +0900 Subject: [PATCH 29/47] Lower mining priority for anon-impairing (<2 mixins) tx (c6432ac) + Mine below-std fee transactions only half the time (1e743dc) --- src/cryptonote_core/tx_pool.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index a5a8b8e11b..3231d3b486 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -981,6 +981,7 @@ namespace cryptonote uint64_t best_coinbase = 0, coinbase = 0; total_size = 0; fee = 0; + size_t n = 0; //baseline empty block get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version, height); @@ -1049,6 +1050,17 @@ namespace cryptonote continue; } + if (tx.vin.size() > 0) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[0], const txin_to_key, itk, false); + // discourage < 3-way-mix transactions by mining them only as the first tx in an empty block + if (n > 0 && itk.key_offsets.size() < 3) + { + sorted_it++; + continue; + } + } + // Skip transactions that are not ready to be // included into the blockchain or that are // missing key images @@ -1085,6 +1097,7 @@ namespace cryptonote best_coinbase = coinbase; append_key_images(k_images, tx); sorted_it++; + n++; LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); } From 6b45c52eb70c8fe43027135ea7d89151b14b521f Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 19 Feb 2018 16:12:11 +0900 Subject: [PATCH 30/47] Multisig for pre-ringct (unofficial, disabled) --- src/CMakeLists.txt | 2 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 16 ++- src/ringct/rctSigs.cpp | 98 +++++++++++++ src/ringct/rctSigs.h | 3 + src/simplewallet/simplewallet.cpp | 6 + src/wallet/wallet2.cpp | 144 +++++++++++++++++++- src/wallet/wallet2.h | 6 +- src/wallet/wallet_rpc_server.cpp | 8 ++ 8 files changed, 273 insertions(+), 10 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 79d2a232d3..eec7e7ebad 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -130,7 +130,7 @@ endif() add_subdirectory(cryptonote_protocol) if(NOT IOS) add_subdirectory(simplewallet) - add_subdirectory(gen_multisig) +# add_subdirectory(gen_multisig) # Multisig for pre-RingCT is not officially supported add_subdirectory(daemonizer) add_subdirectory(daemon) add_subdirectory(blockchain_utilities) diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 5d0cf9797d..b04e07c1d0 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -438,6 +438,11 @@ namespace cryptonote crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash); + if (msout) + { + msout->c.resize(sources.size()); + } + std::stringstream ss_ring_s; size_t i = 0; for(const tx_source_entry& src_entr: sources) @@ -458,7 +463,16 @@ namespace cryptonote std::vector& sigs = tx.signatures.back(); sigs.resize(src_entr.outputs.size()); if (!zero_secret_key) - crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + { + if (msout) + { + rct::gen_prerct_multisig(tx_prefix_hash, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, src_entr.multisig_kLRki, *msout, i, sigs.data()); + } + else + { + crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + } + } ss_ring_s << "signatures:" << ENDL; std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL; diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 24ab08778d..3bbb162c4e 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -779,6 +779,88 @@ namespace rct { return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, false); } + void gen_prerct_multisig(const crypto::hash &prefix_hash, const std::vector &pubs, const secret_key &sec, size_t sec_index, const multisig_kLRki &kLRki, multisig_out &msout, size_t msout_idx, crypto::signature *sig) { + CHECK_AND_ASSERT_THROW_MES(msout_idx < msout.c.size(), "Bad msout_idx"); + + // copied from crypto.cpp + struct ec_point_pair { + crypto::ec_point a, b; + }; + struct rs_comm { + crypto::hash h; + struct ec_point_pair ab[]; + }; + auto rs_comm_size = [](size_t pubs_count) { + return sizeof(rs_comm) + pubs_count * sizeof(ec_point_pair); + }; + auto hash_to_ec = [](const crypto::public_key &key, ge_p3 &res) { + crypto::hash h; + ge_p2 point; + ge_p1p1 point2; + cn_fast_hash(std::addressof(key), sizeof(crypto::public_key), h); + ge_fromfe_frombytes_vartime(&point, reinterpret_cast(&h)); + ge_mul8(&point2, &point); + ge_p1p1_to_p3(&res, &point2); + }; + + size_t i; + ge_p3 image_unp; + ge_dsmp image_pre; + crypto::ec_scalar sum, h; + const size_t pubs_count = pubs.size(); + boost::shared_ptr buf(reinterpret_cast(malloc(rs_comm_size(pubs_count))), free); + if (!buf) + abort(); + assert(sec_index < pubs_count); + #if !defined(NDEBUG) + { + ge_p3 t; + crypto::public_key t2; + crypto::key_image t3; + assert(sc_check(&sec) == 0); + ge_scalarmult_base(&t, &sec); + ge_p3_tobytes(&t2, &t); + assert(*pubs[sec_index] == t2); + generate_key_image(*pubs[sec_index], sec, t3); + assert(image == t3); + for (i = 0; i < pubs_count; i++) { + assert(check_key(*pubs[i])); + } + } + #endif + if (ge_frombytes_vartime(&image_unp, kLRki.ki.bytes) != 0) { + abort(); + } + ge_dsm_precomp(image_pre, &image_unp); + sc_0((unsigned char *)&sum); + buf->h = prefix_hash; + for (i = 0; i < pubs_count; i++) { + ge_p2 tmp2; + ge_p3 tmp3; + if (i == sec_index) { + buf->ab[i].a = (const ec_point &)kLRki.L; + buf->ab[i].b = (const ec_point &)kLRki.R; + } else { + sig[i].c = rct2sk(skGen()); + sig[i].r = rct2sk(skGen()); + if (ge_frombytes_vartime(&tmp3, (const unsigned char *)&*pubs[i]) != 0) { + abort(); + } + ge_double_scalarmult_base_vartime(&tmp2, (const unsigned char *)&sig[i].c, &tmp3, (const unsigned char *)&sig[i].r); + ge_tobytes((unsigned char *)&buf->ab[i].a, &tmp2); + hash_to_ec(*pubs[i], tmp3); + ge_double_scalarmult_precomp_vartime(&tmp2, (const unsigned char *)&sig[i].r, &tmp3, (const unsigned char *)&sig[i].c, image_pre); + ge_tobytes((unsigned char *)&buf->ab[i].b, &tmp2); + sc_add((unsigned char *)&sum, (const unsigned char *)&sum, (const unsigned char *)&sig[i].c); + } + } + hash_to_scalar(buf.get(), rs_comm_size(pubs_count), h); + sc_sub((unsigned char *)&sig[sec_index].c, (const unsigned char *)&h, (const unsigned char *)&sum); + sc_mulsub((unsigned char *)&sig[sec_index].r, (const unsigned char *)&sig[sec_index].c, (const unsigned char *)&sec, kLRki.k.bytes); + + msout.c[msout_idx] = (const rct::key&)sig[sec_index].c; + } + //RingCT protocol //genRct: // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the @@ -1050,4 +1132,20 @@ namespace rct { } return true; } + + bool signMultisig(std::vector> &signatures, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { + CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes"); + CHECK_AND_ASSERT_MES(k.size() == signatures.size(), false, "Mismatched k/signatures size"); + CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size"); + for (size_t n = 0; n < indices.size(); ++n) { + CHECK_AND_ASSERT_MES(indices[n] < signatures[n].size(), false, "Index out of range"); + } + + for (size_t n = 0; n < indices.size(); ++n) { + rct::key diff; + sc_mulsub(diff.bytes, msout.c[n].bytes, secret_key.bytes, k[n].bytes); + sc_add((unsigned char*)&signatures[n][indices[n]].r, (const unsigned char*)&signatures[n][indices[n]].r, diff.bytes); + } + return true; + } } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index e83083a981..35057d703e 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -122,6 +122,8 @@ namespace rct { rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, bool bulletproof); + void gen_prerct_multisig(const crypto::hash &prefix_hash, const std::vector &pubs, const crypto::secret_key &sec, size_t sec_index, const multisig_kLRki &kLRki, multisig_out &msout, size_t msout_idx, crypto::signature *sig); + bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSimple(const rctSig & rv, bool semantics); @@ -132,6 +134,7 @@ namespace rct { xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i); bool signMultisig(rctSig &rv, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key); + bool signMultisig(std::vector> &signatures, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key); } #endif /* RCTSIGS_H */ diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index ff6bc96b90..114617fb74 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -700,6 +700,12 @@ bool simple_wallet::print_fee_info(const std::vector &args/* = std: bool simple_wallet::prepare_multisig(const std::vector &args) { + // comment out below if you want to use multisig with pre-ringct outputs which is unofficial + { + fail_msg_writer() << tr("Multisig for pre-RingCT is not supported officially"); + return true; + } + if (m_wallet->multisig()) { fail_msg_writer() << tr("This wallet is already multisig"); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 81c305ae54..832101a606 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4669,6 +4669,8 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector multisig_signers; + size_t n_multisig_txes = 0; + if (m_multisig && !m_transfers.empty()) + { + const crypto::public_key local_signer = get_multisig_signer_public_key(); + size_t n_available_signers = 1; + for (const crypto::public_key &signer: m_multisig_signers) + { + if (signer == local_signer) + continue; + multisig_signers.push_front(signer); + for (const auto &i: m_transfers[0].m_multisig_info) + { + if (i.m_signer == signer) + { + multisig_signers.pop_front(); + multisig_signers.push_back(signer); + ++n_available_signers; + break; + } + } + } + multisig_signers.push_back(local_signer); + MDEBUG("We can use " << n_available_signers << "/" << m_multisig_signers.size() << " other signers"); + THROW_WALLET_EXCEPTION_IF(n_available_signers+1 < m_multisig_threshold, error::multisig_import_needed); + n_multisig_txes = n_available_signers == m_multisig_signers.size() ? m_multisig_threshold : 1; + MDEBUG("We will create " << n_multisig_txes << " txes"); + } + uint64_t found_money = 0; for(size_t idx: selected_transfers) { @@ -5370,6 +5435,7 @@ void wallet2::transfer_selected(const std::vector sources; + std::unordered_set used_L; for(size_t idx: selected_transfers) { sources.resize(sources.size()+1); @@ -5407,7 +5473,15 @@ void wallet2::transfer_selected(const std::vector additional_tx_keys; rct::multisig_out msout; LOG_PRINT_L2("constructing tx"); + auto sources_copy = sources; bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, false, m_multisig ? &msout : NULL); LOG_PRINT_L2("constructed tx, r="< ins_order; + for (size_t n = 0; n < sources.size(); ++n) + { + for (size_t idx = 0; idx < sources_copy.size(); ++idx) + { + THROW_WALLET_EXCEPTION_IF((size_t)sources_copy[idx].real_output >= sources_copy[idx].outputs.size(), + error::wallet_internal_error, "Invalid real_output"); + if (sources_copy[idx].outputs[sources_copy[idx].real_output].second.dest == sources[n].outputs[sources[n].real_output].second.dest) + ins_order.push_back(idx); + } + } + THROW_WALLET_EXCEPTION_IF(ins_order.size() != sources.size(), error::wallet_internal_error, "Failed to work out sources permutation"); + + std::vector multisig_sigs; + if (m_multisig) + { + crypto::public_key ignore = m_multisig_threshold == m_multisig_signers.size() ? crypto::null_pkey : multisig_signers.front(); + multisig_sigs.push_back({rct::rctSig{}, ignore, used_L, {}, msout, tx.signatures}); + + if (m_multisig_threshold < m_multisig_signers.size()) + { + const crypto::hash prefix_hash = cryptonote::get_transaction_prefix_hash(tx); + + // create the other versions, one for every other participant (the first one's already done above) + for (size_t signer_index = 1; signer_index < n_multisig_txes; ++signer_index) + { + std::unordered_set new_used_L; + size_t src_idx = 0; + THROW_WALLET_EXCEPTION_IF(selected_transfers.size() != sources.size(), error::wallet_internal_error, "mismatched selected_transfers and sources sixes"); + for(size_t idx: selected_transfers) + { + cryptonote::tx_source_entry& src = sources[src_idx]; + src.multisig_kLRki = get_multisig_composite_kLRki(idx, multisig_signers[signer_index], used_L, new_used_L); + ++src_idx; + } + + LOG_PRINT_L2("Creating supplementary multisig transaction"); + cryptonote::transaction ms_tx; + auto sources_copy_copy = sources_copy; + bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, false, false, &msout); + LOG_PRINT_L2("constructed tx, r="< bool { @@ -5464,13 +5592,15 @@ void wallet2::transfer_selected(const std::vector used_L; std::unordered_set signing_keys; rct::multisig_out msout; + std::vector> prerct_sigs; }; // The convention for destinations is: @@ -1091,7 +1092,7 @@ BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 0) BOOST_CLASS_VERSION(tools::wallet2::tx_construction_data, 2) BOOST_CLASS_VERSION(tools::wallet2::pending_tx, 3) -BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 0) +BOOST_CLASS_VERSION(tools::wallet2::multisig_sig, 1) namespace boost { @@ -1423,6 +1424,9 @@ namespace boost a & x.used_L; a & x.signing_keys; a & x.msout; + if (ver < 1) + return; + a & x.prerct_sigs; } template diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e4a5fa3fd9..fbadf06791 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -2495,6 +2495,14 @@ namespace tools bool wallet_rpc_server::on_prepare_multisig(const wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::request& req, wallet_rpc::COMMAND_RPC_PREPARE_MULTISIG::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); + + // comment out below if you want to use multisig with pre-ringct outputs which is unofficial + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Multisig for pre-RingCT is not supported officially"; + return false; + } + if (m_wallet->restricted()) { er.code = WALLET_RPC_ERROR_CODE_DENIED; From 8217da26e6a9eb0c186f7fac77ef1ae258e7d4c8 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 7 Feb 2018 19:07:36 +0000 Subject: [PATCH 31/47] Monero Cryptonight variants, and add one for v7 /monero#3253 This is the first variant of many, with the intent to improve Monero's resistance to ASICs and encourage mining decentralization. --- src/crypto/chacha.h | 4 +- src/crypto/hash-ops.h | 2 +- src/crypto/hash.h | 12 ++--- src/crypto/slow-hash.c | 49 +++++++++++++++++-- .../cryptonote_format_utils.cpp | 3 +- tests/hash/CMakeLists.txt | 2 +- tests/hash/main.cpp | 19 ++++--- tests/hash/tests-slow-1m-1.txt | 5 ++ tests/hash/tests-slow-2m-1.txt | 5 ++ 9 files changed, 80 insertions(+), 21 deletions(-) create mode 100644 tests/hash/tests-slow-1m-1.txt create mode 100644 tests/hash/tests-slow-2m-1.txt diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index 9ec6a0fa7c..b3ef29aca8 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -69,10 +69,10 @@ namespace crypto { chacha20(data, length, key.data(), reinterpret_cast(&iv), cipher); } - inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) { + inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, int cn_variant = 0) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); tools::scrubbed_arr pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data(), 0); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0, cn_variant); memcpy(&key, pwd_hash.data(), sizeof(key)); } diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index f212b7bdd0..16f13585d3 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -79,7 +79,7 @@ enum { }; void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash(const void *data, size_t length, char *hash, int light); +void cn_slow_hash(const void *data, size_t length, char *hash, int light, int variant); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index bf5a7f19a5..19a82fbd85 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -71,16 +71,16 @@ namespace crypto { return h; } - inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int light) { - cn_slow_hash(data, length, reinterpret_cast(&hash), light); + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int light, int variant = 0) { + cn_slow_hash(data, length, reinterpret_cast(&hash), light, variant); } - inline void cn_slow_hash_1m(const void *data, std::size_t length, hash &hash) { - cn_slow_hash(data, length, hash, 1); + inline void cn_slow_hash_1m(const void *data, std::size_t length, hash &hash, int variant = 0) { + cn_slow_hash(data, length, hash, 1, variant); } - inline void cn_slow_hash_2m(const void *data, std::size_t length, hash &hash) { - cn_slow_hash(data, length, hash, 0); + inline void cn_slow_hash_2m(const void *data, std::size_t length, hash &hash, int variant = 0) { + cn_slow_hash(data, length, hash, 0, variant); } inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index 7dab25d822..d76130218d 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include "common/int-util.h" #include "hash-ops.h" @@ -47,6 +49,29 @@ extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expandedKey); extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey); +#define VARIANT1_1(p) \ + do if (variant > 0) \ + { \ + uint8_t tmp = ((const uint8_t*)p)[11]; \ + uint8_t tmp1 = (tmp>>4)&1, tmp2 = (tmp>>5)&1, tmp3 = tmp1^tmp2; \ + uint8_t tmp0 = (((const uint8_t*)p)[11] & 1) ? tmp3 : ((((tmp2<<1)|tmp1) + 1)&3); \ + ((uint8_t*)p)[11] = (((const uint8_t*)p)[11] & 1) ? ((tmp & 0xef) | (tmp0<<4)):((tmp & 0xcf) | (tmp0<<4)); \ + } while(0) + +#define VARIANT1_2(p) \ + do if (variant > 0) \ + { \ + ((uint32_t*)p)[2] ^= nonce; \ + } while(0) + +#define VARIANT1_INIT() \ + if (variant > 0 && length < 43) \ + { \ + fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \ + _exit(1); \ + } \ + const uint32_t nonce = variant > 0 ? *(const uint32_t*)(((const uint8_t*)data)+39) : 0 + #if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI // Fall back to more portable code is down at the bottom @@ -125,6 +150,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp _mm_store_si128(R128(c), _c); \ _b = _mm_xor_si128(_b, _c); \ _mm_store_si128(R128(&hp_state[j]), _b); \ + VARIANT1_1(&hp_state[j]); \ j = state_index(c,(light?2:1)); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ @@ -134,6 +160,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ _b = _c; \ + VARIANT1_2(&hp_state[j]); \ #if defined(_MSC_VER) #define THREADV __declspec(thread) @@ -516,7 +543,7 @@ void slow_hash_free_state(void) * @param hash a pointer to a buffer in which the final 256 bit hash will be stored */ -void cn_slow_hash(const void *data, size_t length, char *hash, int light) +void cn_slow_hash(const void *data, size_t length, char *hash, int light, int variant) { RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ @@ -538,6 +565,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; + VARIANT1_INIT(); + // this isn't supposed to happen, but guard against it for now. if(hp_state == NULL) slow_hash_allocate_state(); @@ -706,6 +735,7 @@ union cn_slow_hash_state vst1q_u8((uint8_t *)c, _c); \ _b = veorq_u8(_b, _c); \ vst1q_u8(&hp_state[j], _b); \ + VARIANT1_1(&hp_state[j]); \ j = state_index(c,(light?2:1)); \ p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ @@ -714,6 +744,7 @@ union cn_slow_hash_state p = U64(&hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ + VARIANT1_2(p); \ _b = _c; \ @@ -845,7 +876,7 @@ STATIC INLINE void aes_pseudo_round_xor(const uint8_t *in, uint8_t *out, const u } } -void cn_slow_hash(const void *data, size_t length, char *hash, int light) +void cn_slow_hash(const void *data, size_t length, char *hash, int light, int variant) { RDATA_ALIGN16 uint8_t expandedKey[240]; RDATA_ALIGN16 uint8_t hp_state[MEMORY]; @@ -866,6 +897,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; + VARIANT1_INIT(); + /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ hash_process(&state.hs, data, length); @@ -1039,7 +1072,7 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b) U64(a)[1] ^= U64(b)[1]; } -void cn_slow_hash(const void *data, size_t length, char *hash, int light) +void cn_slow_hash(const void *data, size_t length, char *hash, int light, int variant) { uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; @@ -1058,6 +1091,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; + VARIANT1_INIT(); + #ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; #else @@ -1097,6 +1132,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) xor_blocks(b, p); swap_blocks(b, p); swap_blocks(a, b); + VARIANT1_1(p); // Iteration 2 p = &long_state[state_index(a,(light?2:1))]; @@ -1106,6 +1142,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) swap_blocks(b, p); xor_blocks(b, p); swap_blocks(a, b); + VARIANT1_2(p); } memcpy(text, state.init, INIT_SIZE_BYTE); @@ -1210,7 +1247,7 @@ union cn_slow_hash_state { }; #pragma pack(pop) -void cn_slow_hash(const void *data, size_t length, char *hash, int light) { +void cn_slow_hash(const void *data, size_t length, char *hash, int light, int variant) { uint8_t long_state[MEMORY]; union cn_slow_hash_state state; uint8_t text[INIT_SIZE_BYTE]; @@ -1222,6 +1259,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) { uint8_t aes_key[AES_KEY_SIZE]; oaes_ctx *aes_ctx; + VARIANT1_INIT(); + hash_process(&state.hs, data, length); memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(aes_key, state.hs.b, AES_KEY_SIZE); @@ -1254,6 +1293,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) { copy_block(&long_state[j * AES_BLOCK_SIZE], c); assert(j == e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE)); swap_blocks(a, b); + VARIANT1_1(&long_state[j * AES_BLOCK_SIZE]); /* Iteration 2 */ j = e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE); copy_block(c, &long_state[j * AES_BLOCK_SIZE]); @@ -1264,6 +1304,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light) { copy_block(&long_state[j * AES_BLOCK_SIZE], c); assert(j == e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE)); swap_blocks(a, b); + VARIANT1_2(&long_state[j * AES_BLOCK_SIZE]); } memcpy(text, state.init, INIT_SIZE_BYTE); diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 12a69d9691..691899effa 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -933,7 +933,8 @@ namespace cryptonote bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { blobdata bd = get_block_hashing_blob(b); - crypto::cn_slow_hash(bd.data(), bd.size(), res, height >= HARDFORK_1_HEIGHT); + const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; + crypto::cn_slow_hash(bd.data(), bd.size(), res, height >= HARDFORK_1_HEIGHT, cn_variant); return true; } //--------------------------------------------------------------- diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 9cbdee2fd1..c774fd397b 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -42,7 +42,7 @@ set_property(TARGET hash-tests PROPERTY FOLDER "tests") -foreach (hash IN ITEMS fast slow-2m slow-1m tree extra-blake extra-groestl extra-jh extra-skein) +foreach (hash IN ITEMS fast slow-2m slow-1m slow-2m-1 slow-1m-1 tree extra-blake extra-groestl extra-jh extra-skein) add_test( NAME "hash-${hash}" COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 231d34c116..d8dc04c052 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -51,11 +51,17 @@ extern "C" { } tree_hash((const char (*)[32]) data, length >> 5, hash); } - static void cn_slow_hash_2m(const void *data, size_t length, char *hash) { - cn_slow_hash(data, length, hash, 0); + static void cn_slow_hash_2m_0(const void *data, size_t length, char *hash) { + cn_slow_hash(data, length, hash, 0, 0); } - static void cn_slow_hash_1m(const void *data, size_t length, char *hash) { - cn_slow_hash(data, length, hash, 1); + static void cn_slow_hash_1m_0(const void *data, size_t length, char *hash) { + cn_slow_hash(data, length, hash, 1, 0); + } + static void cn_slow_hash_2m_1(const void *data, size_t length, char *hash) { + cn_slow_hash(data, length, hash, 0, 1); + } + static void cn_slow_hash_1m_1(const void *data, size_t length, char *hash) { + cn_slow_hash(data, length, hash, 1, 1); } } POP_WARNINGS @@ -64,9 +70,10 @@ extern "C" typedef void hash_f(const void *, size_t, char *); struct hash_func { const string name; hash_f &f; -} hashes[] = {{"fast", cn_fast_hash}, {"slow-2m", cn_slow_hash_2m}, {"slow-1m", cn_slow_hash_1m}, {"tree", hash_tree}, +} hashes[] = {{"fast", cn_fast_hash}, {"slow-2m", cn_slow_hash_2m_0}, {"slow-1m", cn_slow_hash_1m_0}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, - {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}}; + {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}, + {"slow-2m-1", cn_slow_hash_2m_1}, {"slow-1m-1", cn_slow_hash_1m_1}}; int main(int argc, char *argv[]) { hash_f *f; diff --git a/tests/hash/tests-slow-1m-1.txt b/tests/hash/tests-slow-1m-1.txt new file mode 100644 index 0000000000..5d7e65d44f --- /dev/null +++ b/tests/hash/tests-slow-1m-1.txt @@ -0,0 +1,5 @@ +aebeb19caa76e743c43246150f2f20066e6fc8b0d2aa254f166d05aada0dffce 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +a1c8283d6287e6e4287b8a4d752753c520f8bc1d79a90799ea29043cebdfc3ff 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +5563796ecde38329558a4146b46570253a5afd76fecd791c55058cd8c581ba64 8519e039172b0d70e5ca7b3383d6b3167315a422747b73f019cf9528f0fde341fd0f2a63030ba6450525cf6de31837669af6f1df8131faf50aaab8d3a7405589 +2288bc3f0ba11160046b896707608436d6aafe916827fac5dc294aff9eabc07e 37a636d7dafdf259b7287eddca2f58099e98619d2f99bdb8969d7b14498102cc065201c8be90bd777323f449848b215d2977c92c4c1c2da36ab46b2e389689ed97c18fec08cd3b03235c5e4c62a37ad88c7b67932495a71090e85dd4020a9300 +13efc225c7fff34352822faf01b9632eb1e8988b3e7f1b626896a5bf48bf71cf 38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb diff --git a/tests/hash/tests-slow-2m-1.txt b/tests/hash/tests-slow-2m-1.txt new file mode 100644 index 0000000000..0c571f86f6 --- /dev/null +++ b/tests/hash/tests-slow-2m-1.txt @@ -0,0 +1,5 @@ +b46aab7facce3eb4b4679d1d526f2d500d43736988b7881853c4c0c4af04ac0e 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +f0f0ab8a50a809ccd82c76996e494b20a1e01e90ed4814a27db2558511559091 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +13310922971e1112b382357aeaa8b0bf0cd941dc6f5e398979f3fa77d13749bd 8519e039172b0d70e5ca7b3383d6b3167315a422747b73f019cf9528f0fde341fd0f2a63030ba6450525cf6de31837669af6f1df8131faf50aaab8d3a7405589 +0d9f641d14a782748ed5548f72a20c83c4a2cfe606015ad9eda5b36a00947d72 37a636d7dafdf259b7287eddca2f58099e98619d2f99bdb8969d7b14498102cc065201c8be90bd777323f449848b215d2977c92c4c1c2da36ab46b2e389689ed97c18fec08cd3b03235c5e4c62a37ad88c7b67932495a71090e85dd4020a9300 +d74a0c9b603aa9f9af0bfec0c36d3d383c14930a0ee2f08dab44536fccda5bee 38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb From 235180052d4e4da7ed8c282136f16b592f50cab6 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Fri, 16 Feb 2018 09:11:54 +0000 Subject: [PATCH 32/47] slow-hash: optimized version /monero#3253 --- src/crypto/slow-hash.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index d76130218d..f0e4062e71 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -52,10 +52,10 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #define VARIANT1_1(p) \ do if (variant > 0) \ { \ - uint8_t tmp = ((const uint8_t*)p)[11]; \ - uint8_t tmp1 = (tmp>>4)&1, tmp2 = (tmp>>5)&1, tmp3 = tmp1^tmp2; \ - uint8_t tmp0 = (((const uint8_t*)p)[11] & 1) ? tmp3 : ((((tmp2<<1)|tmp1) + 1)&3); \ - ((uint8_t*)p)[11] = (((const uint8_t*)p)[11] & 1) ? ((tmp & 0xef) | (tmp0<<4)):((tmp & 0xcf) | (tmp0<<4)); \ + const uint8_t tmp = ((const uint8_t*)(p))[11]; \ + static const uint32_t table = 0x75310; \ + const uint8_t index = (((tmp >> 3) & 6) | (tmp & 1)) << 1; \ + ((uint8_t*)(p))[11] = tmp ^ ((table >> index) & 0x30); \ } while(0) #define VARIANT1_2(p) \ From 80de15a620874d54fa53f9d59b9027a3b3be000d Mon Sep 17 00:00:00 2001 From: Lee Clagett Date: Tue, 27 Feb 2018 18:45:32 -0500 Subject: [PATCH 33/47] tweaks to the monerov1 cryptonight algorithm /monero#3253 --- src/crypto/slow-hash.c | 68 ++++++++++++++++++++++++++-------- tests/hash/tests-slow-1m-1.txt | 10 ++--- tests/hash/tests-slow-2m-1.txt | 10 ++--- 3 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index f0e4062e71..c31f79cc0c 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -61,16 +61,33 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp #define VARIANT1_2(p) \ do if (variant > 0) \ { \ - ((uint32_t*)p)[2] ^= nonce; \ + xor64(p, tweak1_2); \ } while(0) -#define VARIANT1_INIT() \ - if (variant > 0 && length < 43) \ +#define VARIANT1_CHECK() \ + do if (length < 43) \ { \ fprintf(stderr, "Cryptonight variants need at least 43 bytes of data"); \ _exit(1); \ + } while(0) + +#define NONCE_POINTER (((const uint8_t*)data)+35) + +#define VARIANT1_PORTABLE_INIT() \ + uint8_t tweak1_2[8]; \ + do if (variant > 0) \ + { \ + VARIANT1_CHECK(); \ + memcpy(&tweak1_2, &state.hs.b[192], sizeof(tweak1_2)); \ + xor64(tweak1_2, NONCE_POINTER); \ + } while(0) + +#define VARIANT1_INIT64() \ + if (variant > 0) \ + { \ + VARIANT1_CHECK(); \ } \ - const uint32_t nonce = variant > 0 ? *(const uint32_t*)(((const uint8_t*)data)+39) : 0 + const uint64_t tweak1_2 = variant > 0 ? (state.hs.w[24] ^ (*((const uint64_t*)NONCE_POINTER))) : 0 #if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI @@ -159,8 +176,8 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp p = U64(&hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ + VARIANT1_2(p + 1); \ _b = _c; \ - VARIANT1_2(&hp_state[j]); \ #if defined(_MSC_VER) #define THREADV __declspec(thread) @@ -210,6 +227,11 @@ STATIC INLINE void xor_blocks(uint8_t *a, const uint8_t *b) U64(a)[1] ^= U64(b)[1]; } +STATIC INLINE void xor64(uint64_t *a, const uint64_t b) +{ + *a ^= b; +} + /** * @brief uses cpuid to determine if the CPU supports the AES instructions * @return true if the CPU supports AES, false otherwise @@ -565,8 +587,6 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; - VARIANT1_INIT(); - // this isn't supposed to happen, but guard against it for now. if(hp_state == NULL) slow_hash_allocate_state(); @@ -576,6 +596,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va hash_process(&state.hs, data, length); memcpy(text, state.init, INIT_SIZE_BYTE); + VARIANT1_INIT64(); + /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. */ @@ -699,6 +721,11 @@ void slow_hash_free_state(void) #define U64(x) ((uint64_t *) (x)) +STATIC INLINE void xor64(uint64 *a, const uint64 b) +{ + *a ^= b; +} + #pragma pack(push, 1) union cn_slow_hash_state { @@ -744,7 +771,7 @@ union cn_slow_hash_state p = U64(&hp_state[j]); \ p[0] = a[0]; p[1] = a[1]; \ a[0] ^= b[0]; a[1] ^= b[1]; \ - VARIANT1_2(p); \ + VARIANT1_2(p + 1); \ _b = _c; \ @@ -897,13 +924,13 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; - VARIANT1_INIT(); - /* CryptoNight Step 1: Use Keccak1600 to initialize the 'state' (and 'text') buffers from the data. */ hash_process(&state.hs, data, length); memcpy(text, state.init, INIT_SIZE_BYTE); + VARIANT1_INIT64(); + /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. */ @@ -1091,8 +1118,6 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein }; - VARIANT1_INIT(); - #ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; #else @@ -1103,6 +1128,8 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va hash_process(&state.hs, data, length); memcpy(text, state.init, INIT_SIZE_BYTE); + VARIANT1_INIT64(); + aes_ctx = (oaes_ctx *) oaes_alloc(); oaes_key_import_data(aes_ctx, state.hs.b, AES_KEY_SIZE); @@ -1142,7 +1169,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va swap_blocks(b, p); xor_blocks(b, p); swap_blocks(a, b); - VARIANT1_2(p); + VARIANT1_2(U64(p) + 1); } memcpy(text, state.init, INIT_SIZE_BYTE); @@ -1237,6 +1264,15 @@ static void xor_blocks(uint8_t* a, const uint8_t* b) { } } +static void xor64(uint8_t* left, const uint8_t* right) +{ + size_t i; + for (i = 0; i < 8; ++i) + { + left[i] ^= right[i]; + } +} + #pragma pack(push, 1) union cn_slow_hash_state { union hash_state hs; @@ -1259,13 +1295,13 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va uint8_t aes_key[AES_KEY_SIZE]; oaes_ctx *aes_ctx; - VARIANT1_INIT(); - hash_process(&state.hs, data, length); memcpy(text, state.init, INIT_SIZE_BYTE); memcpy(aes_key, state.hs.b, AES_KEY_SIZE); aes_ctx = (oaes_ctx *) oaes_alloc(); + VARIANT1_PORTABLE_INIT(); + oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); for (i = 0; i < MEMORY / (light?2:1) / INIT_SIZE_BYTE; i++) { for (j = 0; j < INIT_SIZE_BLK; j++) { @@ -1301,10 +1337,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int light, int va sum_half_blocks(b, d); swap_blocks(b, c); xor_blocks(b, c); + VARIANT1_2(c + 8); copy_block(&long_state[j * AES_BLOCK_SIZE], c); assert(j == e2i(a, MEMORY / (light?2:1) / AES_BLOCK_SIZE)); swap_blocks(a, b); - VARIANT1_2(&long_state[j * AES_BLOCK_SIZE]); } memcpy(text, state.init, INIT_SIZE_BYTE); diff --git a/tests/hash/tests-slow-1m-1.txt b/tests/hash/tests-slow-1m-1.txt index 5d7e65d44f..0a897ab011 100644 --- a/tests/hash/tests-slow-1m-1.txt +++ b/tests/hash/tests-slow-1m-1.txt @@ -1,5 +1,5 @@ -aebeb19caa76e743c43246150f2f20066e6fc8b0d2aa254f166d05aada0dffce 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -a1c8283d6287e6e4287b8a4d752753c520f8bc1d79a90799ea29043cebdfc3ff 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -5563796ecde38329558a4146b46570253a5afd76fecd791c55058cd8c581ba64 8519e039172b0d70e5ca7b3383d6b3167315a422747b73f019cf9528f0fde341fd0f2a63030ba6450525cf6de31837669af6f1df8131faf50aaab8d3a7405589 -2288bc3f0ba11160046b896707608436d6aafe916827fac5dc294aff9eabc07e 37a636d7dafdf259b7287eddca2f58099e98619d2f99bdb8969d7b14498102cc065201c8be90bd777323f449848b215d2977c92c4c1c2da36ab46b2e389689ed97c18fec08cd3b03235c5e4c62a37ad88c7b67932495a71090e85dd4020a9300 -13efc225c7fff34352822faf01b9632eb1e8988b3e7f1b626896a5bf48bf71cf 38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb +5655313715525895d2312bfba9b7f5e45f441b8b8d3957eaea0b6039d1bc0713 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +173c9db34c9643ba689e16044f5c273c4c5543b210a4d5248352ac536064a850 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +bb196c4c0c9dc1c4e44c2a6f9e61200fe3c8b4ef232134e65c3c7862c7d3df6a 8519e039172b0d70e5ca7b3383d6b3167315a422747b73f019cf9528f0fde341fd0f2a63030ba6450525cf6de31837669af6f1df8131faf50aaab8d3a7405589 +452103731dd8d70ce32f726b8e71fcd91005fb3cb2abd78f2b7357bb07f8c8bc 37a636d7dafdf259b7287eddca2f58099e98619d2f99bdb8969d7b14498102cc065201c8be90bd777323f449848b215d2977c92c4c1c2da36ab46b2e389689ed97c18fec08cd3b03235c5e4c62a37ad88c7b67932495a71090e85dd4020a9300 +4e785376ed2733262d83cc25321a9d0003f5395315de919acf1b97f0a84fbd2d 38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb diff --git a/tests/hash/tests-slow-2m-1.txt b/tests/hash/tests-slow-2m-1.txt index 0c571f86f6..ed8eccc22a 100644 --- a/tests/hash/tests-slow-2m-1.txt +++ b/tests/hash/tests-slow-2m-1.txt @@ -1,5 +1,5 @@ -b46aab7facce3eb4b4679d1d526f2d500d43736988b7881853c4c0c4af04ac0e 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -f0f0ab8a50a809ccd82c76996e494b20a1e01e90ed4814a27db2558511559091 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -13310922971e1112b382357aeaa8b0bf0cd941dc6f5e398979f3fa77d13749bd 8519e039172b0d70e5ca7b3383d6b3167315a422747b73f019cf9528f0fde341fd0f2a63030ba6450525cf6de31837669af6f1df8131faf50aaab8d3a7405589 -0d9f641d14a782748ed5548f72a20c83c4a2cfe606015ad9eda5b36a00947d72 37a636d7dafdf259b7287eddca2f58099e98619d2f99bdb8969d7b14498102cc065201c8be90bd777323f449848b215d2977c92c4c1c2da36ab46b2e389689ed97c18fec08cd3b03235c5e4c62a37ad88c7b67932495a71090e85dd4020a9300 -d74a0c9b603aa9f9af0bfec0c36d3d383c14930a0ee2f08dab44536fccda5bee 38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb +b5a7f63abb94d07d1a6445c36c07c7e8327fe61b1647e391b4c7edae5de57a3d 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +80563c40ed46575a9e44820d93ee095e2851aa22483fd67837118c6cd951ba61 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +5bb40c5880cef2f739bdb6aaaf16161eaae55530e7b10d7ea996b751a299e949 8519e039172b0d70e5ca7b3383d6b3167315a422747b73f019cf9528f0fde341fd0f2a63030ba6450525cf6de31837669af6f1df8131faf50aaab8d3a7405589 +613e638505ba1fd05f428d5c9f8e08f8165614342dac419adc6a47dce257eb3e 37a636d7dafdf259b7287eddca2f58099e98619d2f99bdb8969d7b14498102cc065201c8be90bd777323f449848b215d2977c92c4c1c2da36ab46b2e389689ed97c18fec08cd3b03235c5e4c62a37ad88c7b67932495a71090e85dd4020a9300 +ed082e49dbd5bbe34a3726a0d1dad981146062b39d36d62c71eb1ed8ab49459b 38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb From 32da5f8193cf51c6545092289fc837dd96730d79 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 00:17:49 +0900 Subject: [PATCH 34/47] Remove CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 Remove CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 Remove DYNAMIC_FEE_PER_KB_BASE_FEE_V5 --- .../cryptonote_basic_impl.cpp | 6 +- src/cryptonote_config.h | 2 - src/cryptonote_core/blockchain.cpp | 2 +- src/wallet/wallet2.cpp | 2 +- tests/unit_tests/fee.cpp | 60 +++++++++---------- 5 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index f7dfc153fe..4350ec90cd 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -86,11 +86,7 @@ namespace cryptonote { //----------------------------------------------------------------------------------------------- size_t get_min_block_size(uint8_t version) { - if (version < 2) - return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; - if (version < 5) - return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2; - return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5; + return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; } //----------------------------------------------------------------------------------------------- size_t get_max_block_size() diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 867e749088..5ea29873f1 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -60,9 +60,7 @@ #define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)300000000000) // 3 * pow(10, 11) #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 -#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 20000 //size of block (bytes) after which reward for block calculated using block size - before first fork -#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 300000 //size of block (bytes) after which reward for block calculated using block size - second change, from v5 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 #define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12 // COIN - number of smallest units in one coin diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index de63fc7826..8b47e4e439 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2890,7 +2890,7 @@ static uint64_t get_fee_quantization_mask() uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version) { const uint64_t min_block_size = get_min_block_size(version); - const uint64_t fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; + const uint64_t fee_per_kb_base = DYNAMIC_FEE_PER_KB_BASE_FEE; if (median_block_size < min_block_size) median_block_size = min_block_size; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 832101a606..60953e8890 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7128,7 +7128,7 @@ uint64_t wallet2::get_upper_transaction_size_limit() { if (m_upper_transaction_size_limit > 0) return m_upper_transaction_size_limit; - uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; + uint64_t full_reward_zone = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } //---------------------------------------------------------------------------------------------------- diff --git a/tests/unit_tests/fee.cpp b/tests/unit_tests/fee.cpp index 3013a8c0cd..c46e36eec7 100644 --- a/tests/unit_tests/fee.cpp +++ b/tests/unit_tests/fee.cpp @@ -57,47 +57,47 @@ namespace TEST_F(fee, 10xmr) { - // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000)); + // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 and lower are clamped + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2, 3), clamp_fee(2000000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 100, 3), clamp_fee(2000000000)); ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, 1, 3), 2000000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 2, 3), clamp_fee(2000000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 10, 3), clamp_fee(2000000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 1000, 3), clamp_fee(2000000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 20000ull, 3), clamp_fee(2000000000 / 20000)); } TEST_F(fee, 1xmr) { - // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000)); + // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 and lower are clamped + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2, 3), clamp_fee(200000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 100, 3), clamp_fee(200000000)); ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, 1, 3), 200000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 2, 3), clamp_fee(200000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 10, 3), clamp_fee(200000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 1000, 3), clamp_fee(200000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 20000ull, 3), clamp_fee(200000000 / 20000)); } TEST_F(fee, dot3xmr) { - // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000)); + // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 and lower are clamped + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2, 3), clamp_fee(60000000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 100, 3), clamp_fee(60000000)); ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, 1, 3), 60000000); // higher is inverse proportional - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000)); - ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 2, 3), clamp_fee(60000000 / 2)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 10, 3), clamp_fee(60000000 / 10)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 1000, 3), clamp_fee(60000000 / 1000)); + ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 20000ull, 3), clamp_fee(60000000 / 20000)); } static bool is_more_or_less(double x, double y) @@ -105,7 +105,7 @@ namespace return fabs(y - x) < 0.001; } - static const double MAX_MULTIPLIER = 166.f; + static const double MAX_MULTIPLIER = 498.f; TEST_F(fee, double_at_full) { @@ -117,12 +117,12 @@ namespace 300000000000ull, // .3 monero, minimum reward per block at 1min }; static const uint64_t median_block_sizes[] = { - CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, - CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, - CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, - CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, + CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, + CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 2, + CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 10, + CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 1000, // with clamping, the formula does not hold for such large blocks and small fees - // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull + // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 * 20000ull }; for (uint64_t block_reward: block_rewards) From 40dc8ab21b006c0d20a5397a179bf780537afd12 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 00:46:14 +0900 Subject: [PATCH 35/47] Revert /monero#2023: Remove 1.25x multiplier from tx_pool --- src/cryptonote_core/tx_pool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 3231d3b486..5239950ae5 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -82,7 +82,7 @@ namespace cryptonote uint64_t get_transaction_size_limit(uint8_t version) { - return get_min_block_size(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + return get_min_block_size(version) * 125 / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; } // This class is meant to create a batch when none currently exists. From 951660d10e755d33c31f628748aa47ed25cf321e Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 00:51:40 +0900 Subject: [PATCH 36/47] Remove BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 Set BLOCKS_SYNCHRONIZING_DEFAULT_COUNT to 200 --- src/cryptonote_config.h | 3 +-- src/cryptonote_core/cryptonote_core.cpp | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 5ea29873f1..70c0b1472f 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -90,8 +90,7 @@ #define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing -#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4 100 //by default, blocks count in blocks downloading -#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 20 //by default, blocks count in blocks downloading +#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading #define CRYPTONOTE_MEMPOOL_TX_LIVETIME 86400 //seconds, one day #define CRYPTONOTE_MEMPOOL_TX_FROM_ALT_BLOCK_LIVETIME 604800 //seconds, one week diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 828c5bba83..18e4e7dae4 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -886,12 +886,9 @@ namespace cryptonote //----------------------------------------------------------------------------------------------- size_t core::get_block_sync_size(uint64_t height) const { - static const uint64_t quick_height = m_testnet ? 801219 : 1220516; if (block_sync_size > 0) return block_sync_size; - if (height >= quick_height) - return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; - return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT_PRE_V4; + return BLOCKS_SYNCHRONIZING_DEFAULT_COUNT; } //----------------------------------------------------------------------------------------------- bool core::are_key_images_spent_in_pool(const std::vector& key_im, std::vector &spent) const From 735e9a4e112678179bce16ea37a1b3407fa923ea Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 01:49:50 +0900 Subject: [PATCH 37/47] blockchain: remove sorted ins --- src/cryptonote_core/blockchain.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8b47e4e439..66afe1a181 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2574,25 +2574,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - // from v7, sorted ins - if (hf_version >= 7) { - const crypto::key_image *last_key_image = NULL; - for (size_t n = 0; n < tx.vin.size(); ++n) - { - const txin_v &txin = tx.vin[n]; - if (txin.type() == typeid(txin_to_key)) - { - const txin_to_key& in_to_key = boost::get(txin); - if (last_key_image && memcmp(&in_to_key.k_image, last_key_image, sizeof(*last_key_image)) >= 0) - { - MERROR_VER("transaction has unsorted inputs"); - tvc.m_verifivation_failed = true; - return false; - } - last_key_image = &in_to_key.k_image; - } - } - } auto it = m_check_txin_table.find(tx_prefix_hash); if(it == m_check_txin_table.end()) { From 696d6e09a7ce9580b376f715751d697e06eebaad Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 01:58:26 +0900 Subject: [PATCH 38/47] Remove minimum mixin 4 --- src/cryptonote_config.h | 1 - src/cryptonote_core/blockchain.cpp | 2 +- src/wallet/wallet2.cpp | 10 +++------- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 70c0b1472f..4a657c17cf 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -131,7 +131,6 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 // #define HF_VERSION_DYNAMIC_FEE 4 -#define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_ENFORCE_RCT 6 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 66afe1a181..6c57e49afb 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2512,7 +2512,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { size_t n_unmixable = 0, n_mixable = 0; size_t mixin = std::numeric_limits::max(); - const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; + const size_t min_mixin = 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 60953e8890..e2f43e5cfa 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4886,11 +4886,7 @@ int wallet2::get_fee_algorithm() //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::adjust_mixin(uint64_t mixin) { - if (mixin < 4 && use_fork_rules(6, 10)) { - MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5"); - mixin = 4; - } - else if (mixin < 2 && use_fork_rules(2, 10)) { + if (mixin < 2 && use_fork_rules(2, 10)) { MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3"); mixin = 2; } @@ -7239,14 +7235,14 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const std::vector wallet2::select_available_unmixable_outputs(bool trusted_daemon) { // request all outputs with less than 3 instances - const size_t min_mixin = use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4 + const size_t min_mixin = 2; return select_available_outputs_from_histogram(min_mixin + 1, false, true, false, trusted_daemon); } //---------------------------------------------------------------------------------------------------- std::vector wallet2::select_available_mixable_outputs(bool trusted_daemon) { // request all outputs with at least 3 instances, so we can use mixin 2 with - const size_t min_mixin = use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4 + const size_t min_mixin = 2; return select_available_outputs_from_histogram(min_mixin + 1, true, true, true, trusted_daemon); } //---------------------------------------------------------------------------------------------------- From 70f6bc5f7d2bac6387e39cd4fa52ea6f17ed3960 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 02:01:07 +0900 Subject: [PATCH 39/47] Remove enforce rct --- src/cryptonote_config.h | 1 - src/cryptonote_core/blockchain.cpp | 7 ------- 2 files changed, 8 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 4a657c17cf..35e5f57515 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -131,7 +131,6 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 // #define HF_VERSION_DYNAMIC_FEE 4 -#define HF_VERSION_ENFORCE_RCT 6 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 6c57e49afb..6a71ec5e50 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2565,13 +2565,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, tvc.m_verifivation_failed = true; return false; } - const size_t min_tx_version = (n_unmixable > 0 ? 1 : (hf_version >= HF_VERSION_ENFORCE_RCT) ? 2 : 1); - if (tx.version < min_tx_version) - { - MERROR_VER("transaction version " << (unsigned)tx.version << " is lower than min accepted version " << min_tx_version); - tvc.m_verifivation_failed = true; - return false; - } } auto it = m_check_txin_table.find(tx_prefix_hash); From f740ef33d22cdfaf5251bf3e22cad06fbe954d10 Mon Sep 17 00:00:00 2001 From: stoffu Date: Mon, 19 Mar 2018 23:49:22 +0900 Subject: [PATCH 40/47] Disable RingCT by #defining HF_VERSION_ALLOW_RCT 255 which replaces hf_version 4 --- src/cryptonote_basic/cryptonote_basic_impl.cpp | 5 +++++ src/cryptonote_config.h | 1 + src/cryptonote_core/blockchain.cpp | 6 +++--- src/cryptonote_core/cryptonote_core.cpp | 2 +- src/cryptonote_core/cryptonote_tx_utils.cpp | 6 +++--- src/wallet/wallet2.cpp | 8 ++++---- tests/core_tests/multisig.cpp | 8 ++++---- tests/core_tests/multisig.h | 2 +- tests/core_tests/rct.cpp | 8 ++++---- tests/core_tests/rct.h | 2 +- 10 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index 4350ec90cd..8484d4a91e 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -86,6 +86,11 @@ namespace cryptonote { //----------------------------------------------------------------------------------------------- size_t get_min_block_size(uint8_t version) { + if (version == 255) + { + // Needed in order to make core_tests rct/multisig pass while HF_VERSION_ALLOW_RCT is set to 255 + return 60000; + } return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; } //----------------------------------------------------------------------------------------------- diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 35e5f57515..b14c62c7bd 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -130,6 +130,7 @@ #define THREAD_STACK_SIZE 5 * 1024 * 1024 +#define HF_VERSION_ALLOW_RCT 255 // #define HF_VERSION_DYNAMIC_FEE 4 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 6a71ec5e50..d134d054e5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1165,7 +1165,7 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m */ //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size uint8_t hf_version = m_hardfork->get_current_version(); - size_t max_outs = hf_version >= 4 ? 1 : 11; + size_t max_outs = hf_version >= HF_VERSION_ALLOW_RCT ? 1 : 11; bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance"); size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); @@ -2372,7 +2372,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } // in a v2 tx, all outputs must have 0 amount - if (hf_version >= 3) { + if (hf_version >= HF_VERSION_ALLOW_RCT) { if (tx.version >= 2) { for (auto &o: tx.vout) { if (o.amount != 0) { @@ -2558,7 +2558,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } // min/max tx version based on HF, and we accept v1 txes if having a non mixable - const size_t max_tx_version = (hf_version <= 3) ? 1 : 2; + const size_t max_tx_version = (hf_version < HF_VERSION_ALLOW_RCT) ? 1 : 2; if (tx.version > max_tx_version) { MERROR_VER("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version); diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 18e4e7dae4..50d6f33bf2 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -591,7 +591,7 @@ namespace cryptonote bad_semantics_txes_lock.unlock(); uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); - const size_t max_tx_version = version == 1 ? 1 : 2; + const size_t max_tx_version = version < HF_VERSION_ALLOW_RCT ? 1 : 2; if (tx.version == 0 || tx.version > max_tx_version) { // v2 is the latest one we know diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index b04e07c1d0..3976fa87d1 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -106,7 +106,7 @@ namespace cryptonote // from hard fork 4, we use a single "dusty" output. This makes the tx even smaller, // and avoids the quantization. These outputs will be added as rct outputs with identity // masks, to they can be used as rct inputs. - if (hard_fork_version >= 2 && hard_fork_version < 4) { + if (hard_fork_version >= 2 && hard_fork_version < HF_VERSION_ALLOW_RCT) { block_reward = block_reward - block_reward % ::config::BASE_REWARD_CLAMP_THRESHOLD; } @@ -116,7 +116,7 @@ namespace cryptonote [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); - if (height == 0 || hard_fork_version >= 4) + if (height == 0 || hard_fork_version >= HF_VERSION_ALLOW_RCT) { // the genesis block was not decomposed, for unknown reasons while (max_outs < out_amounts.size()) @@ -156,7 +156,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); - if (hard_fork_version >= 4) + if (hard_fork_version >= HF_VERSION_ALLOW_RCT) tx.version = 2; else tx.version = 1; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e2f43e5cfa..f6ce38c9bc 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6489,7 +6489,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_all(uint64_t below { std::vector unused_transfers_indices; std::vector unused_dust_indices; - const bool use_rct = use_fork_rules(4, 0); + const bool use_rct = use_fork_rules(HF_VERSION_ALLOW_RCT, 0); THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account) == 0, error::wallet_internal_error, "No unlocked balance in the entire wallet"); @@ -6943,7 +6943,7 @@ std::vector wallet2::create_transactions_single(const crypt { std::vector unused_transfers_indices; std::vector unused_dust_indices; - const bool use_rct = use_fork_rules(4, 0); + const bool use_rct = use_fork_rules(HF_VERSION_ALLOW_RCT, 0); // find output with the given key image for (size_t i = 0; i < m_transfers.size(); ++i) { @@ -6975,7 +6975,7 @@ std::vector wallet2::create_transactions_from(const crypton uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); std::vector> outs; - const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); + const bool use_rct = fake_outs_count > 0 && use_fork_rules(HF_VERSION_ALLOW_RCT, 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0); const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp index 623b529675..edc30065f0 100644 --- a/tests/core_tests/multisig.cpp +++ b/tests/core_tests/multisig.cpp @@ -95,8 +95,8 @@ bool gen_multisig_tx_validation_base::generate_with(std::vectortimestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long - crypto::hash(), 0, transaction(), std::vector(), 0, 1, 4), + HF_VERSION_ALLOW_RCT, HF_VERSION_ALLOW_RCT, prev_block->timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long + crypto::hash(), 0, transaction(), std::vector(), 0, 1, HF_VERSION_ALLOW_RCT), false, "Failed to generate block"); events.push_back(blocks[n]); prev_block = blocks + n; @@ -113,8 +113,8 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector(), 0, 1, 4), + HF_VERSION_ALLOW_RCT, HF_VERSION_ALLOW_RCT, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long + crypto::hash(), 0, transaction(), std::vector(), 0, 1, HF_VERSION_ALLOW_RCT), false, "Failed to generate block"); events.push_back(blk); blk_last = blk; diff --git a/tests/core_tests/multisig.h b/tests/core_tests/multisig.h index 62a1c6a352..1496981ec5 100644 --- a/tests/core_tests/multisig.h +++ b/tests/core_tests/multisig.h @@ -82,7 +82,7 @@ struct gen_multisig_tx_validation_base : public test_chain_unit_base template<> struct get_test_options { - const std::pair hard_forks[3] = {std::make_pair(1, 0), std::make_pair(4, 1), std::make_pair(0, 0)}; + const std::pair hard_forks[3] = {std::make_pair(1, 0), std::make_pair(HF_VERSION_ALLOW_RCT, 1), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { hard_forks }; diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 25e77e9e58..de52a40f1b 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -140,8 +140,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_tx_hashes | test_generator::bf_hf_version | test_generator::bf_max_outs, - 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long - crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 4, 4), + HF_VERSION_ALLOW_RCT, HF_VERSION_ALLOW_RCT, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long + crypto::hash(), 0, transaction(), starting_rct_tx_hashes, 0, 4, HF_VERSION_ALLOW_RCT), false, "Failed to generate block"); events.push_back(blk_txes[n]); blk_last = blk_txes[n]; @@ -154,8 +154,8 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev cryptonote::block blk; CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk, blk_last, miner_account, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version | test_generator::bf_max_outs, - 4, 4, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long - crypto::hash(), 0, transaction(), std::vector(), 0, 4, 4), + HF_VERSION_ALLOW_RCT, HF_VERSION_ALLOW_RCT, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 4, // v2 has blocks four times as long + crypto::hash(), 0, transaction(), std::vector(), 0, 4, HF_VERSION_ALLOW_RCT), false, "Failed to generate block"); events.push_back(blk); blk_last = blk; diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index 8cd84c4625..98694a5530 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -81,7 +81,7 @@ struct gen_rct_tx_validation_base : public test_chain_unit_base template<> struct get_test_options { - const std::pair hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(4, 65), std::make_pair(0, 0)}; + const std::pair hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(HF_VERSION_ALLOW_RCT, 65), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { hard_forks }; From 1c4e8b85e5c61f7d5f0bd6500f2619a5cf1ee62d Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 01:02:58 +0900 Subject: [PATCH 41/47] Allow bulletproofs as soon as RingCT is allowed --- src/cryptonote_core/blockchain.cpp | 4 ++-- src/wallet/wallet2.cpp | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d134d054e5..15030f7e8d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2396,8 +2396,8 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - // from v7, allow bulletproofs - if (hf_version < 7 || !m_testnet) { + // when RingCT is allowed, allow bulletproofs + if (hf_version < HF_VERSION_ALLOW_RCT) { if (!tx.rct_signatures.p.bulletproofs.empty()) { MERROR("Bulletproofs are not allowed before v7 or on mainnet"); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f6ce38c9bc..5e4fcddf1c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -523,10 +523,8 @@ size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, si uint8_t get_bulletproof_fork(bool testnet) { - if (testnet) - return 7; - else - return 255; // TODO + // in Aeon, RingCT is activated with bulletproof + return HF_VERSION_ALLOW_RCT; } crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx) From 9fa6cc0bfb7b1c9c181037dfd32bfc32b32ebd5b Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 02:53:53 +0900 Subject: [PATCH 42/47] simplewallet: disable fee command --- src/simplewallet/simplewallet.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 114617fb74..e48f478272 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1742,9 +1742,11 @@ simple_wallet::simple_wallet() m_cmd_binder.set_handler("payment_id", boost::bind(&simple_wallet::payment_id, this, _1), tr("Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids.")); +#if 0 m_cmd_binder.set_handler("fee", boost::bind(&simple_wallet::print_fee_info, this, _1), tr("Print the information about the current fee and transaction backlog.")); +#endif m_cmd_binder.set_handler("prepare_multisig", boost::bind(&simple_wallet::prepare_multisig, this, _1), tr("Export data needed to create a multisig wallet")); m_cmd_binder.set_handler("make_multisig", boost::bind(&simple_wallet::make_multisig, this, _1), From e599f055c6139b56cae040757217d248672f6b63 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 13:51:03 +0900 Subject: [PATCH 43/47] blockchain: require miner tx to have well behaved outs /monero#763 --- src/cryptonote_core/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 15030f7e8d..d0fab7b594 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -995,7 +995,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl money_in_use += o.amount; partial_block_reward = false; - if (version == 3) { + if (version >= 3 && version < HF_VERSION_ALLOW_RCT) { for (auto &o: b.miner_tx.vout) { if (!is_valid_decomposed_amount(o.amount)) { MERROR_VER("miner tx output " << print_money(o.amount) << " is not a valid decomposed amount"); From 6ff68d7bcaea77acd4e28f9a00092191d25cdc5f Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 15:17:46 +0900 Subject: [PATCH 44/47] get_block_longhash: on testnet etc, switch to cn-lite at v2 --- src/cryptonote_basic/cryptonote_format_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 691899effa..6e1a64b12f 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -934,7 +934,7 @@ namespace cryptonote { blobdata bd = get_block_hashing_blob(b); const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; - crypto::cn_slow_hash(bd.data(), bd.size(), res, height >= HARDFORK_1_HEIGHT, cn_variant); + crypto::cn_slow_hash(bd.data(), bd.size(), res, height >= HARDFORK_1_HEIGHT || b.major_version >= 2, cn_variant); // on testnet etc, switch to cn-lite at v2 return true; } //--------------------------------------------------------------- From 4f3683765ba93b3639eac39a4d89e1d5b1be438c Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 18:20:52 +0900 Subject: [PATCH 45/47] Allow 10% of txs in a block to be of ringsize 1 Prohibit ringsize 2 --- src/cryptonote_config.h | 1 + src/cryptonote_core/blockchain.cpp | 49 ++++++++++++++++-- src/cryptonote_core/blockchain.h | 2 +- src/cryptonote_core/tx_pool.cpp | 83 ++++++++++++++++++++++++++++-- tests/core_tests/v2_tests.cpp | 2 +- 5 files changed, 125 insertions(+), 12 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index b14c62c7bd..4b161e8be3 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -137,6 +137,7 @@ #define HASH_OF_HASHES_STEP 256 +#define NOFAKE_TXS_TO_TOTAL_TXS_RATIO 10 // New constants are intended to go here namespace config diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index d0fab7b594..8f5d0dca57 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2494,10 +2494,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr // check_tx_input() rather than here, and use this function simply // to iterate the inputs as necessary (splitting the task // using threads, etc.) -bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height) +bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height, uint64_t* total_txs, uint64_t* nofake_txs) { PERF_TIMER(check_tx_inputs); LOG_PRINT_L3("Blockchain::" << __func__); + + CHECK_AND_ASSERT_MES((total_txs && nofake_txs) || (!total_txs && !nofake_txs), false, "only one of total_txs and nofake_txs is non-null"); + bool is_nofake_tx = false; + size_t sig_index = 0; if(pmax_used_block_height) *pmax_used_block_height = 0; @@ -2545,9 +2549,33 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { if (n_unmixable == 0) { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and no unmixable inputs"); - tvc.m_low_mixin = true; - return false; + if (mixin == 1) + { + MERROR_VER("Tx " << get_transaction_hash(tx) << " has prohibited ring size 2, and no unmixable inputs"); + tvc.m_low_mixin = true; + return false; + } + MDEBUG("Tx " << get_transaction_hash(tx) << " is non-private, and has no unmixable inputs."); + if (nofake_txs) + { + MDEBUG("So far in this block we've seen " << (*nofake_txs) << " non-private txs out of " << (*total_txs) << " txs."); + if ((*nofake_txs) * NOFAKE_TXS_TO_TOTAL_TXS_RATIO <= (*total_txs)) + { + MDEBUG("This tx can be added because non-private txs are scarce enough"); + is_nofake_tx = true; + } + else + { + MERROR_VER("This tx cannot be added because there's no more room for non-private txs"); + tvc.m_low_mixin = true; + return false; + } + } + else + { + // we came here from tx_memory_pool::is_transaction_ready_to_go(). since tx_memory_pool::fill_block_template() has already checked that + // this non-private txs is scarce enough in the block, we can consider it as valid + } } if (n_mixable > 1) { @@ -2831,6 +2859,16 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } } + + if (total_txs) + { + (*total_txs) += 1; + if (is_nofake_tx) + { + (*nofake_txs) += 1; + } + } + return true; } @@ -3309,6 +3347,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& // XXX old code adds miner tx here size_t tx_index = 0; + uint64_t total_txs = 0, nofake_txs = 0; // Iterate over the block's transaction hashes, grabbing each // from the tx_pool and validating them. Each is then added // to txs. Keys spent in each are added to by the double spend check. @@ -3373,7 +3412,7 @@ bool Blockchain::handle_block_to_main_chain(const block& bl, const crypto::hash& { // validate that transaction inputs and the keys spending them are correct. tx_verification_context tvc; - if(!check_tx_inputs(tx, tvc)) + if(!check_tx_inputs(tx, tvc, NULL, &total_txs, &nofake_txs)) { MERROR_VER("Block with id: " << id << " has at least one transaction (id: " << tx_id << ") with wrong inputs."); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 6168de959b..502a5ed683 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1104,7 +1104,7 @@ namespace cryptonote * * @return false if any validation step fails, otherwise true */ - bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL, uint64_t* total_txs = NULL, uint64_t* nofake_txs = NULL); /** * @brief performs a blockchain reorganization according to the longest chain rule diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index 5239950ae5..a812df5f06 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -982,6 +982,7 @@ namespace cryptonote total_size = 0; fee = 0; size_t n = 0; + size_t nofake_txs = 0; //baseline empty block get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version, height); @@ -1050,14 +1051,82 @@ namespace cryptonote continue; } + bool is_nofake_tx = false; if (tx.vin.size() > 0) { - CHECKED_GET_SPECIFIC_VARIANT(tx.vin[0], const txin_to_key, itk, false); - // discourage < 3-way-mix transactions by mining them only as the first tx in an empty block - if (n > 0 && itk.key_offsets.size() < 3) + if (version >= 2) { - sorted_it++; - continue; + size_t n_unmixable = 0, n_mixable = 0; + size_t mixin = std::numeric_limits::max(); + const size_t min_mixin = 2; + for (const auto& txin : tx.vin) + { + if (txin.type() == typeid(txin_to_key)) + { + const txin_to_key& in_to_key = boost::get(txin); + if (in_to_key.amount == 0) + { + // always consider rct inputs mixable. Even if there's not enough rct + // inputs on the chain to mix with, this is going to be the case for + // just a few blocks right after the fork at most + ++n_mixable; + } + else + { + uint64_t n_outputs = m_blockchain.get_db().get_num_outputs(in_to_key.amount); + LOG_PRINT_L2("output size " << print_money(in_to_key.amount) << ": " << n_outputs << " available"); + // n_outputs includes the output we're considering + if (n_outputs <= min_mixin) + ++n_unmixable; + else + ++n_mixable; + } + if (in_to_key.key_offsets.size() - 1 < mixin) + mixin = in_to_key.key_offsets.size() - 1; + } + } + + if (mixin < min_mixin) + { + if (n_unmixable == 0) + { + if (mixin == 1) + { + LOG_PRINT_L2("Tx " << get_transaction_hash(tx) << " has prohibited ring size 2, and no unmixable inputs"); + sorted_it++; + continue; + } + LOG_PRINT_L2("Tx " << get_transaction_hash(tx) << " is non-private, and has no unmixable inputs. " + "So far in this block we've seen " << nofake_txs << " non-private txs out of " << n << " txs."); + if (nofake_txs * NOFAKE_TXS_TO_TOTAL_TXS_RATIO <= n) + { + LOG_PRINT_L2("This tx can be added because non-private txs are scarce enough"); + is_nofake_tx = true; + } + else + { + LOG_PRINT_L2("This tx cannot be added because there's no more room for non-private txs"); + sorted_it++; + continue; + } + } + if (n_mixable > 1) + { + LOG_PRINT_L2("Tx " << get_transaction_hash(tx) << " has too low ring size (" << (mixin + 1) << "), and more than one mixable input with unmixable inputs"); + sorted_it++; + continue; + } + } + } + else + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[0], const txin_to_key, itk, false); + // discourage < 3-way-mix transactions by mining them only as the first tx in an empty block + if (n > 0 && itk.key_offsets.size() < 3) + { + sorted_it++; + continue; + } } } @@ -1098,6 +1167,10 @@ namespace cryptonote append_key_images(k_images, tx); sorted_it++; n++; + if (is_nofake_tx) + { + nofake_txs++; + } LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); } diff --git a/tests/core_tests/v2_tests.cpp b/tests/core_tests/v2_tests.cpp index 5b6a22b341..7a7300cc2f 100644 --- a/tests/core_tests/v2_tests.cpp +++ b/tests/core_tests/v2_tests.cpp @@ -122,7 +122,7 @@ bool gen_v2_tx_mixable_0_mixin::generate(std::vector& events) const int mixin = 0; const int out_idx[] = {1, -1}; const uint64_t amount_paid = 10000; - return generate_with(events, out_idx, mixin, amount_paid, false); + return generate_with(events, out_idx, mixin, amount_paid, true); } bool gen_v2_tx_mixable_low_mixin::generate(std::vector& events) const From b35e26503fdd8b1141b90c8a18711a567d2ea3a8 Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 19:09:05 +0900 Subject: [PATCH 46/47] wallet: replace default mixin semantics with default ring size semantics Giving ring size 0 (whether explicitly or implicitly) means using wallet's saved default ring size. If the saved default ring size is also 0, the predefined DEFAULT_RING_SIZE (=3) is used. --- src/cryptonote_config.h | 2 + src/simplewallet/simplewallet.cpp | 67 ++++++++++++++++--------------- src/wallet/api/wallet.cpp | 11 ++--- src/wallet/api/wallet.h | 2 +- src/wallet/api/wallet2_api.h | 4 +- src/wallet/wallet2.cpp | 14 +++---- src/wallet/wallet2.h | 6 +-- 7 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 4b161e8be3..baefe18d87 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -139,6 +139,8 @@ #define NOFAKE_TXS_TO_TOTAL_TXS_RATIO 10 +#define DEFAULT_RING_SIZE 3 + // New constants are intended to go here namespace config { diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e48f478272..01e77fbe0b 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -80,10 +80,6 @@ typedef cryptonote::simple_wallet sw; #define EXTENDED_LOGS_FILE "wallet_details.log" -#define DEFAULT_MIX 4 - -#define MIN_RING_SIZE 5 // Used to inform user about min ring size -- does not track actual protocol - #define OUTPUT_EXPORT_FILE_MAGIC "Aeon output export\003" #define LOCK_IDLE_SCOPE() \ @@ -1223,27 +1219,27 @@ bool simple_wallet::set_default_ring_size(const std::vector &args/* { if (strchr(args[1].c_str(), '-')) { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; + fail_msg_writer() << tr("ring size must be an integer >= 0"); return true; } uint32_t ring_size = boost::lexical_cast(args[1]); - if (ring_size < MIN_RING_SIZE && ring_size != 0) + if (ring_size == 2) { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; + fail_msg_writer() << tr("ring size must not be 2"); return true; } const auto pwd_container = get_and_verify_password(); if (pwd_container) { - m_wallet->default_mixin(ring_size > 0 ? ring_size - 1 : 0); + m_wallet->default_ring_size(ring_size); m_wallet->rewrite(m_wallet_file, pwd_container->password()); } return true; } catch(const boost::bad_lexical_cast &) { - fail_msg_writer() << tr("ring size must be an integer >= ") << MIN_RING_SIZE; + fail_msg_writer() << tr("ring size must be an integer >= 0"); return true; } catch(...) @@ -1790,7 +1786,7 @@ bool simple_wallet::set_variable(const std::vector &args) success_msg_writer() << "always-confirm-transfers = " << m_wallet->always_confirm_transfers(); success_msg_writer() << "print-ring-members = " << m_wallet->print_ring_members(); success_msg_writer() << "store-tx-info = " << m_wallet->store_tx_info(); - success_msg_writer() << "default-ring-size = " << (m_wallet->default_mixin() ? m_wallet->default_mixin() + 1 : 0); + success_msg_writer() << "default-ring-size = " << m_wallet->default_ring_size(); success_msg_writer() << "auto-refresh = " << m_wallet->auto_refresh(); success_msg_writer() << "refresh-type = " << get_refresh_type_name(m_wallet->get_refresh_type()); success_msg_writer() << "priority = " << m_wallet->get_default_priority(); @@ -1838,7 +1834,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("always-confirm-transfers", set_always_confirm_transfers, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("print-ring-members", set_print_ring_members, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("store-tx-info", set_store_tx_info, tr("0 or 1")); - CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= ") << MIN_RING_SIZE); + CHECK_SIMPLE_VARIABLE("default-ring-size", set_default_ring_size, tr("integer >= 0, must not be 2")); CHECK_SIMPLE_VARIABLE("auto-refresh", set_auto_refresh, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("refresh-type", set_refresh_type, tr("full (slowest, no assumptions); optimize-coinbase (fast, assumes the whole coinbase is paid to a single address); no-coinbase (fastest, assumes we receive no coinbase transaction), default (same as optimize-coinbase)")); CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4")); @@ -3509,26 +3505,26 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector 0 && parse_priority(local_args[0], priority)) local_args.erase(local_args.begin()); - size_t fake_outs_count = 0; + size_t ring_size = 0; if(local_args.size() > 0) { - size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) { - fake_outs_count = m_wallet->default_mixin(); - if (fake_outs_count == 0) - fake_outs_count = DEFAULT_MIX; } - else if (ring_size == 0) + else if (ring_size == 2) { - fail_msg_writer() << tr("Ring size must not be 0"); + fail_msg_writer() << tr("Ring size must not be 2"); return true; } else { - fake_outs_count = ring_size - 1; local_args.erase(local_args.begin()); } } + if (ring_size == 0) + ring_size = m_wallet->default_ring_size(); + if (ring_size == 0) + ring_size = DEFAULT_RING_SIZE; + size_t fake_outs_count = ring_size - 1; uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count); if (adjusted_fake_outs_count > fake_outs_count) { @@ -3989,26 +3985,26 @@ bool simple_wallet::sweep_main(uint64_t below, const std::vector &a if (local_args.size() > 0 && parse_priority(local_args[0], priority)) local_args.erase(local_args.begin()); - size_t fake_outs_count = 0; + size_t ring_size = 0; if(local_args.size() > 0) { - size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) { - fake_outs_count = m_wallet->default_mixin(); - if (fake_outs_count == 0) - fake_outs_count = DEFAULT_MIX; } - else if (ring_size == 0) + else if (ring_size == 2) { - fail_msg_writer() << tr("Ring size must not be 0"); + fail_msg_writer() << tr("Ring size must not be 2"); return true; } else { - fake_outs_count = ring_size - 1; local_args.erase(local_args.begin()); } } + if (ring_size == 0) + ring_size = m_wallet->default_ring_size(); + if (ring_size == 0) + ring_size = DEFAULT_RING_SIZE; + size_t fake_outs_count = ring_size - 1; uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count); if (adjusted_fake_outs_count > fake_outs_count) { @@ -4213,21 +4209,26 @@ bool simple_wallet::sweep_single(const std::vector &args_) } } - size_t fake_outs_count = 0; + size_t ring_size = 0; if(local_args.size() > 0) { - size_t ring_size; if(!epee::string_tools::get_xtype_from_string(ring_size, local_args[0])) { - fake_outs_count = m_wallet->default_mixin(); - if (fake_outs_count == 0) - fake_outs_count = DEFAULT_MIX; + } + else if (ring_size == 2) + { + fail_msg_writer() << tr("Ring size must not be 2"); + return true; } else { - fake_outs_count = ring_size - 1; local_args.erase(local_args.begin()); } } + if (ring_size == 0) + ring_size = m_wallet->default_ring_size(); + if (ring_size == 0) + ring_size = DEFAULT_RING_SIZE; + size_t fake_outs_count = ring_size - 1; std::vector extra; bool payment_id_seen = false; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index b229a9f9d6..efcb5f3b94 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -55,7 +55,6 @@ namespace Monero { namespace { // copy-pasted from simplewallet - static const size_t DEFAULT_MIXIN = 4; static const int DEFAULT_REFRESH_INTERVAL_MILLIS = 1000 * 10; // limit maximum refresh interval as one minute static const int MAX_REFRESH_INTERVAL_MILLIS = 1000 * 60 * 1; @@ -1047,7 +1046,7 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex // - unconfirmed_transfer_details; // - confirmed_transfer_details) -PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional amount, uint32_t mixin_count, +PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional amount, uint32_t ring_size, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set subaddr_indices) { @@ -1059,9 +1058,11 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const // indicates if dst_addr is integrated address (address + payment_id) // TODO: (https://bitcointalk.org/index.php?topic=753252.msg9985441#msg9985441) - size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin(); - if (fake_outs_count == 0) - fake_outs_count = DEFAULT_MIXIN; + if (ring_size == 0) + ring_size = m_wallet->default_ring_size(); + if (ring_size == 0) + ring_size = DEFAULT_RING_SIZE; + size_t fake_outs_count = ring_size - 1; PendingTransactionImpl * transaction = new PendingTransactionImpl(*this); diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 01359ffc62..ea4720070a 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -116,7 +116,7 @@ class WalletImpl : public Wallet void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label); PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, - optional amount, uint32_t mixin_count, + optional amount, uint32_t ring_size, PendingTransaction::Priority priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, std::set subaddr_indices = {}); diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index ab1a48d6e5..d5ffd81a1b 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -614,7 +614,7 @@ struct Wallet * \param dst_addr destination address as string * \param payment_id optional payment_id, can be empty string * \param amount amount - * \param mixin_count mixin count. if 0 passed, wallet will use default value + * \param ring_size ring size. if 0 passed, wallet will use default value * \param subaddr_account subaddress account from which the input funds are taken * \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices * \param priority @@ -623,7 +623,7 @@ struct Wallet */ virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, - optional amount, uint32_t mixin_count, + optional amount, uint32_t ring_size, PendingTransaction::Priority = PendingTransaction::Priority_Low, uint32_t subaddr_account = 0, std::set subaddr_indices = {}) = 0; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5e4fcddf1c..df2d83679f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -582,7 +582,7 @@ wallet2::wallet2(bool testnet, bool restricted): m_always_confirm_transfers(true), m_print_ring_members(false), m_store_tx_info(true), - m_default_mixin(0), + m_default_ring_size(0), m_default_priority(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), @@ -2323,8 +2323,8 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetInt(m_store_tx_info ? 1 :0); json.AddMember("store_tx_info", value2, json.GetAllocator()); - value2.SetUint(m_default_mixin); - json.AddMember("default_mixin", value2, json.GetAllocator()); + value2.SetUint(m_default_ring_size); + json.AddMember("default_ring_size", value2, json.GetAllocator()); value2.SetUint(m_default_priority); json.AddMember("default_priority", value2, json.GetAllocator()); @@ -2432,7 +2432,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_multisig_signers.clear(); m_always_confirm_transfers = false; m_print_ring_members = false; - m_default_mixin = 0; + m_default_ring_size = 0; m_default_priority = 0; m_auto_refresh = true; m_refresh_type = RefreshType::RefreshDefault; @@ -2498,8 +2498,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false, true); GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false, true); m_store_tx_info = ((field_store_tx_keys != 0) || (field_store_tx_info != 0)); - GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false, 0); - m_default_mixin = field_default_mixin; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_ring_size, unsigned int, Uint, false, 0); + m_default_ring_size = field_default_ring_size; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_priority, unsigned int, Uint, false, 0); if (field_default_priority_found) { @@ -4884,7 +4884,7 @@ int wallet2::get_fee_algorithm() //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::adjust_mixin(uint64_t mixin) { - if (mixin < 2 && use_fork_rules(2, 10)) { + if (mixin == 1 && use_fork_rules(2, 10)) { MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3"); mixin = 2; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 593cbc33a8..fced883ea9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -793,8 +793,8 @@ namespace tools void print_ring_members(bool value) { m_print_ring_members = value; } bool store_tx_info() const { return m_store_tx_info; } void store_tx_info(bool store) { m_store_tx_info = store; } - uint32_t default_mixin() const { return m_default_mixin; } - void default_mixin(uint32_t m) { m_default_mixin = m; } + uint32_t default_ring_size() const { return m_default_ring_size; } + void default_ring_size(uint32_t m) { m_default_ring_size = m; } uint32_t get_default_priority() const { return m_default_priority; } void set_default_priority(uint32_t p) { m_default_priority = p; } bool auto_refresh() const { return m_auto_refresh; } @@ -1046,7 +1046,7 @@ namespace tools bool m_always_confirm_transfers; bool m_print_ring_members; bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */ - uint32_t m_default_mixin; + uint32_t m_default_ring_size; uint32_t m_default_priority; RefreshType m_refresh_type; bool m_auto_refresh; From 40c394969851baa3db13a2f1271675d30bbb532f Mon Sep 17 00:00:00 2001 From: stoffu Date: Tue, 20 Mar 2018 20:56:08 +0900 Subject: [PATCH 47/47] Set testnet v7 fork height to 44000 (24 March 2018) --- src/cryptonote_core/blockchain.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 8f5d0dca57..c048b98921 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -101,6 +101,10 @@ static const struct { } testnet_hard_forks[] = { // version 1 from the start of the blockchain { 1, 1, 0, 1341378000 }, + + // versions 2, 3, 4, 5 and 6 are skipped, in favor of reducing the cost of adopting the POW change and other consensus updates from Monero + // version 7 starts from block 44000, which is on or around the 24th of March, 2018. + { 7, 44000, 0, 1521900000 }, }; //------------------------------------------------------------------