diff --git a/src/common/timings.cc b/src/common/timings.cc index 612ac2cc6..aa719fa5e 100644 --- a/src/common/timings.cc +++ b/src/common/timings.cc @@ -12,10 +12,11 @@ TimingsDatabase::TimingsDatabase() { } -TimingsDatabase::TimingsDatabase(const std::string &filename): +TimingsDatabase::TimingsDatabase(const std::string &filename, const bool load_previous /*=false*/): filename(filename) { - load(); + if (load_previous) + load(); } TimingsDatabase::~TimingsDatabase() @@ -73,53 +74,96 @@ bool TimingsDatabase::load() { i.deciles.push_back(atoi(fields[idx++].c_str())); } - instances.insert(std::make_pair(name, i)); + instances.emplace_back(name, i); } fclose(f); return true; } -bool TimingsDatabase::save() +bool TimingsDatabase::save(const bool save_current_time /*=true*/) { - if (filename.empty()) + if (filename.empty() || instances.empty()) return true; - FILE *f = fopen(filename.c_str(), "w"); + FILE *f = fopen(filename.c_str(), "a"); // append if (!f) { MERROR("Failed to write to file " << filename << ": " << strerror(errno)); return false; } - for (const auto &i: instances) + + if (save_current_time) { - fprintf(f, "%s", i.first.c_str()); - fprintf(f, "\t%lu", (unsigned long)i.second.t); - fprintf(f, " %zu", i.second.npoints); - fprintf(f, " %f", i.second.min); - fprintf(f, " %f", i.second.max); - fprintf(f, " %f", i.second.mean); - fprintf(f, " %f", i.second.median); - fprintf(f, " %f", i.second.stddev); - fprintf(f, " %f", i.second.npskew); - for (uint64_t v: i.second.deciles) - fprintf(f, " %lu", (unsigned long)v); + // save current time in readable format (UTC) + std::time_t sys_time{std::time(nullptr)}; + std::tm *utc_time = std::gmtime(&sys_time); //GMT is equivalent to UTC + + // format: year-month-day : hour:minute:second + std::string current_time{}; + if (utc_time && sys_time != (std::time_t)(-1)) + { + char timeString[22]; //length = std::size("yyyy-mm-dd : hh:mm:ss") (constexpr std::size is C++17) + std::strftime(timeString, 22, "%F : %T", utc_time); + current_time += timeString; + } + else + { + current_time += "TIME_ERROR_"; + } + fputc('\n', f); // add an extra line before each 'print time' + fprintf(f, "%s", current_time.c_str()); fputc('\n', f); } + + for (const auto &i: instances) + { + fprintf(f, "%s,", i.first.c_str()); + + if (i.second.npoints > 0) + { + fprintf(f, "%lu,", (unsigned long)i.second.t); + fprintf(f, "%zu,", i.second.npoints); + fprintf(f, "%f,", i.second.min); + fprintf(f, "%f,", i.second.max); + fprintf(f, "%f,", i.second.mean); + fprintf(f, "%f,", i.second.median); + fprintf(f, "%f,", i.second.stddev); + fprintf(f, "%f,", i.second.npskew); + for (uint64_t v: i.second.deciles) + fprintf(f, "%lu,", (unsigned long)v); + + // note: only add a new line if there are points; assume that 'no points' means i.first is a message meant to be + // prepended to the next save operation + fputc('\n', f); + } + } fclose(f); + + // after saving, clear so next save does not append the same stuff over again + instances.clear(); + return true; } -std::vector TimingsDatabase::get(const char *name) const +const TimingsDatabase::instance* TimingsDatabase::get_most_recent(const char *name) const { - std::vector ret; - auto range = instances.equal_range(name); - for (auto i = range.first; i != range.second; ++i) - ret.push_back(i->second); - std::sort(ret.begin(), ret.end(), [](const instance &e0, const instance &e1){ return e0.t < e1.t; }); - return ret; + time_t latest_time = 0; + const TimingsDatabase::instance *instance_ptr = nullptr; + + for (const auto &i: instances) + { + if (i.first != name) + continue; + if (i.second.t < latest_time) + continue; + + latest_time = i.second.t; + instance_ptr = &i.second; + } + return instance_ptr; } void TimingsDatabase::add(const char *name, const instance &i) { - instances.insert(std::make_pair(name, i)); + instances.emplace_back(name, i); } diff --git a/src/common/timings.h b/src/common/timings.h index fb905611f..6e7ff5659 100644 --- a/src/common/timings.h +++ b/src/common/timings.h @@ -2,8 +2,8 @@ #include #include +#include #include -#include class TimingsDatabase { @@ -18,17 +18,17 @@ public: public: TimingsDatabase(); - TimingsDatabase(const std::string &filename); + TimingsDatabase(const std::string &filename, const bool load_previous = false); ~TimingsDatabase(); - std::vector get(const char *name) const; + const instance* get_most_recent(const char *name) const; void add(const char *name, const instance &data); + bool save(const bool print_current_time = true); private: bool load(); - bool save(); private: std::string filename; - std::multimap instances; + std::vector> instances; }; diff --git a/tests/core_tests/chaingen.cpp b/tests/core_tests/chaingen.cpp index f70d99277..a0b39ab55 100644 --- a/tests/core_tests/chaingen.cpp +++ b/tests/core_tests/chaingen.cpp @@ -496,7 +496,7 @@ bool init_spent_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, subaddresses[from.get_keys().m_account_address.m_spend_public_key] = {0,0}; cryptonote::origin_data od{3, crypto::null_pkey, 0}; rct::salvium_input_data_t sid; - generate_key_image_helper(from.get_keys(), subaddresses, out_key, get_tx_pub_key_from_extra(*oi.p_tx), get_additional_tx_pub_keys_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img, hw::get_device(("default")), false, od, sid); + generate_key_image_helper(from.get_keys(), subaddresses, out_key, get_tx_pub_key_from_extra(*oi.p_tx), get_additional_tx_pub_keys_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, img, hw::get_device(("default"))); // lookup for this key image in the events vector BOOST_FOREACH(auto& tx_pair, mtx) { diff --git a/tests/core_tests/tx_validation.cpp b/tests/core_tests/tx_validation.cpp index d85729b1a..4da10b0c7 100644 --- a/tests/core_tests/tx_validation.cpp +++ b/tests/core_tests/tx_validation.cpp @@ -65,7 +65,7 @@ namespace auto& out_key = reinterpret_cast(src_entr.outputs[src_entr.real_output].second.dest); rct::salvium_input_data_t sid; const cryptonote::origin_data od{3, crypto::null_pkey, src_entr.real_output}; - generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral, img, hw::get_device(("default")), false, od, sid); + generate_key_image_helper(sender_account_keys, subaddresses, out_key, src_entr.real_out_tx_key, src_entr.real_out_additional_tx_keys, src_entr.real_output_in_tx_index, in_ephemeral, img, hw::get_device(("default"))); // put key image into tx input txin_to_key input_to_key; diff --git a/tests/performance_tests/CMakeLists.txt b/tests/performance_tests/CMakeLists.txt index 5b37e28e3..8c60a15b2 100644 --- a/tests/performance_tests/CMakeLists.txt +++ b/tests/performance_tests/CMakeLists.txt @@ -56,8 +56,6 @@ set(performance_tests_headers performance_tests.h performance_utils.h single_tx_test_base.h - torsion_ops.h - zero_commit.h view_scan.h) monero_add_minimal_executable(performance_tests diff --git a/tests/performance_tests/generate_key_image_helper.h b/tests/performance_tests/generate_key_image_helper.h index 2308bb84a..3ee536b58 100644 --- a/tests/performance_tests/generate_key_image_helper.h +++ b/tests/performance_tests/generate_key_image_helper.h @@ -51,6 +51,6 @@ public: crypto::public_key out_key = boost::get(m_tx.vout[0].target).key; cryptonote::origin_data od{3,crypto::null_pkey,0}; rct::salvium_input_data_t sid; - return cryptonote::generate_key_image_helper(m_bob.get_keys(), subaddresses, out_key, m_tx_pub_key, m_additional_tx_pub_keys, 0, in_ephemeral, ki, hw::get_device("default"), false, od, sid); + return cryptonote::generate_key_image_helper(m_bob.get_keys(), subaddresses, out_key, m_tx_pub_key, m_additional_tx_pub_keys, 0, in_ephemeral, ki, hw::get_device("default")); } }; diff --git a/tests/performance_tests/is_valid_decomposed_amount.h b/tests/performance_tests/is_valid_decomposed_amount.h new file mode 100644 index 000000000..2b303f9f4 --- /dev/null +++ b/tests/performance_tests/is_valid_decomposed_amount.h @@ -0,0 +1,61 @@ +// Copyright (c) 2024, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include "cryptonote_basic/cryptonote_format_utils.h" + +class test_is_valid_decomposed_amount +{ +public: + static const size_t loop_count = 3; + static const uint64_t max_per_loop = 1000000000; // must be power of 10 + + bool init() + { + m_num_valid_per_loop = 1; + uint64_t x = 1; + while (x != max_per_loop) + { + m_num_valid_per_loop += 9; + x *= 10; + } + + return true; + } + + bool test() + { + size_t num_valid = 0; + for (uint64_t a = 0; a <= max_per_loop; ++a) + num_valid += cryptonote::is_valid_decomposed_amount(a); + return num_valid == m_num_valid_per_loop; + } + + size_t m_num_valid_per_loop; +}; diff --git a/tests/performance_tests/main.cpp b/tests/performance_tests/main.cpp index e37f2b87d..2de8dc258 100644 --- a/tests/performance_tests/main.cpp +++ b/tests/performance_tests/main.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2022, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -51,6 +51,7 @@ #include "generate_keypair.h" #include "signature.h" #include "is_out_to_acc.h" +#include "is_valid_decomposed_amount.h" #include "out_can_be_to_acc.h" #include "subaddress_expand.h" #include "sc_reduce32.h" @@ -65,9 +66,9 @@ #include "multiexp.h" #include "sig_mlsag.h" #include "sig_clsag.h" -#include "torsion_ops.h" +// #include "torsion_ops.h" #include "view_scan.h" -#include "zero_commit.h" +// #include "zero_commit.h" namespace po = boost::program_options; @@ -104,12 +105,14 @@ int main(int argc, char** argv) const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter)); const std::string timings_database = command_line::get_arg(vm, arg_timings_database); - Params p; + Params core_params; if (!timings_database.empty()) - p.td = TimingsDatabase(timings_database); - p.verbose = command_line::get_arg(vm, arg_verbose); - p.stats = command_line::get_arg(vm, arg_stats); - p.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier); + core_params.td = TimingsDatabase(timings_database); + core_params.verbose = command_line::get_arg(vm, arg_verbose); + core_params.stats = command_line::get_arg(vm, arg_stats); + core_params.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier); + + ParamsShuttle p{core_params}; performance_timer timer; timer.start(); @@ -171,24 +174,15 @@ int main(int argc, char** argv) TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofBorromean); TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof, 2); - TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof, 2); TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof, 2); - TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof, 2); TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof, 2); - TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof, 2); TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof, 2); - TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof, 2); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 100, 2, 64); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 10, 64); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 62, 4); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 62, 4); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 56, 16); - TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 56, 16); - TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 1, 0, 1); TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 0xffffffffffffffff, 0, 0xffffffffffffffff); TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 0xffffffffffffffff, 0, 1); @@ -219,6 +213,10 @@ int main(int argc, char** argv) TEST_PERFORMANCE0(filter, p, test_generate_key_image); TEST_PERFORMANCE0(filter, p, test_derive_public_key); TEST_PERFORMANCE0(filter, p, test_derive_secret_key); + // TEST_PERFORMANCE1(filter, p, test_fe_batch_invert, true); // batched + // TEST_PERFORMANCE1(filter, p, test_fe_batch_invert, false); // individual inversions + // TEST_PERFORMANCE1(filter, p, test_torsion_ops, true); // check for torsion + // TEST_PERFORMANCE1(filter, p, test_torsion_ops, false); // clear torsion TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime); TEST_PERFORMANCE0(filter, p, test_ge_tobytes); TEST_PERFORMANCE0(filter, p, test_generate_keypair); @@ -230,6 +228,8 @@ int main(int argc, char** argv) TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200); + TEST_PERFORMANCE0(filter, p, test_is_valid_decomposed_amount); + TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 0); TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 1); TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 2); @@ -613,6 +613,9 @@ int main(int argc, char** argv) TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9); #endif + // TEST_PERFORMANCE1(filter, p, test_zero_commit, true); // fast + // TEST_PERFORMANCE1(filter, p, test_zero_commit, false); + std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl; return 0; diff --git a/tests/performance_tests/performance_tests.h b/tests/performance_tests/performance_tests.h index 1a423879b..0f6063fcf 100644 --- a/tests/performance_tests/performance_tests.h +++ b/tests/performance_tests/performance_tests.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2022, The Monero Project +// Copyright (c) 2014-2024, The Monero Project // // All rights reserved. // @@ -31,6 +31,8 @@ #pragma once #include +#include +#include #include #include @@ -41,7 +43,7 @@ #include "common/perf_timer.h" #include "common/timings.h" -class performance_timer +class performance_timer final { public: typedef boost::chrono::high_resolution_clock clock; @@ -67,7 +69,7 @@ private: clock::time_point m_start; }; -struct Params +struct Params final { TimingsDatabase td; bool verbose; @@ -75,45 +77,79 @@ struct Params unsigned loop_multiplier; }; -template -class test_runner +struct ParamsShuttle +{ + Params core_params; + + ParamsShuttle() = default; + + ParamsShuttle(Params ¶ms) : core_params{params} + {} + + virtual ~ParamsShuttle() = default; // virtual for non-final type +}; + +template ::value, bool>::type = true> +bool init_test(T &test, ParamsT ¶ms_shuttle) +{ + // assume if the params shuttle isn't the base shuttle type, then the test must take the shuttle as an input on init + if (!test.init(params_shuttle)) + return false; + + return true; +} + +template ::value, bool>::type = true> +bool init_test(T &test, ParamsT ¶ms_shuttle) +{ + if (!test.init()) + return false; + + return true; +} + +template +class test_runner final { public: - test_runner(const Params ¶ms) + test_runner(const ParamsT ¶ms_shuttle) : m_elapsed(0) - , m_params(params) - , m_per_call_timers(T::loop_count * params.loop_multiplier, {true}) + , m_params_shuttle(params_shuttle) + , m_core_params(params_shuttle.core_params) + , m_per_call_timers(T::loop_count * params_shuttle.core_params.loop_multiplier, {true}) { } - bool run() + int run() { static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); T test; - if (!test.init()) - return false; + if (!init_test(test, m_params_shuttle)) + return -1; performance_timer timer; timer.start(); warm_up(); - if (m_params.verbose) + if (m_core_params.verbose) std::cout << "Warm up: " << timer.elapsed_ms() << " ms" << std::endl; timer.start(); - for (size_t i = 0; i < T::loop_count * m_params.loop_multiplier; ++i) + for (size_t i = 0; i < T::loop_count * m_core_params.loop_multiplier; ++i) { - if (m_params.stats) + if (m_core_params.stats) m_per_call_timers[i].resume(); if (!test.test()) - return false; - if (m_params.stats) + return i + 1; + if (m_core_params.stats) m_per_call_timers[i].pause(); } m_elapsed = timer.elapsed_ms(); m_stats.reset(new Stats(m_per_call_timers)); - return true; + return 0; } int elapsed_time() const { return m_elapsed; } @@ -122,7 +158,7 @@ public: int time_per_call(int scale = 1) const { static_assert(0 < T::loop_count, "T::loop_count must be greater than 0"); - return m_elapsed * scale / (T::loop_count * m_params.loop_multiplier); + return m_elapsed * scale / (T::loop_count * m_core_params.loop_multiplier); } uint64_t get_min() const { return m_stats->get_min(); } @@ -156,20 +192,25 @@ private: private: volatile uint64_t m_warm_up; /// m_per_call_timers; std::unique_ptr> m_stats; }; -template -void run_test(const std::string &filter, Params ¶ms, const char* test_name) +template +bool run_test(const std::string &filter, ParamsT ¶ms_shuttle, const char* test_name) { + static_assert(std::is_base_of::value, "Must use a ParamsShuttle."); + Params ¶ms = params_shuttle.core_params; + boost::smatch match; if (!filter.empty() && !boost::regex_match(std::string(test_name), match, boost::regex(filter))) - return; + return true; - test_runner runner(params); - if (runner.run()) + test_runner runner(params_shuttle); + int run_result{runner.run()}; + if (run_result == 0) { if (params.verbose) { @@ -208,7 +249,7 @@ void run_test(const std::string &filter, Params ¶ms, const char* test_name) double stddev = runner.get_stddev(); double npskew = runner.get_non_parametric_skew(); - std::vector prev_instances = params.td.get(test_name); + const TimingsDatabase::instance* prev_instance = params.td.get_most_recent(test_name); params.td.add(test_name, {time(NULL), runner.get_size(), min, max, mean, med, stddev, npskew, quantiles}); std::cout << (params.verbose ? " time per call: " : " ") << time_per_call << " " << unit << "/call" << (params.verbose ? "\n" : ""); @@ -219,24 +260,31 @@ void run_test(const std::string &filter, Params ¶ms, const char* test_name) uint64_t p95s = quantiles[9] / scale; uint64_t stddevs = stddev / scale; std::string cmp; - if (!prev_instances.empty()) + if (prev_instance) { - const TimingsDatabase::instance &prev_instance = prev_instances.back(); - if (!runner.is_same_distribution(prev_instance.npoints, prev_instance.mean, prev_instance.stddev)) + if (!runner.is_same_distribution(prev_instance->npoints, prev_instance->mean, prev_instance->stddev)) { - double pc = fabs(100. * (prev_instance.mean - runner.get_mean()) / prev_instance.mean); - cmp = ", " + std::to_string(pc) + "% " + (mean > prev_instance.mean ? "slower" : "faster"); + double pc = fabs(100. * (prev_instance->mean - runner.get_mean()) / prev_instance->mean); + cmp = ", " + std::to_string(pc) + "% " + (mean > prev_instance->mean ? "slower" : "faster"); } -cmp += " -- " + std::to_string(prev_instance.mean); + cmp += " -- " + std::to_string(prev_instance->mean); } std::cout << " (min " << mins << " " << unit << ", 90th " << p95s << " " << unit << ", median " << meds << " " << unit << ", std dev " << stddevs << " " << unit << ")" << cmp; } std::cout << std::endl; } + else if (run_result == -1) + { + std::cout << test_name << " - FAILED ON INIT" << std::endl; + return false; + } else { - std::cout << test_name << " - FAILED" << std::endl; + std::cout << test_name << " - FAILED ON TEST LOOP " << run_result << std::endl; + return false; } + + return true; } #define QUOTEME(x) #x diff --git a/tests/performance_tests/subaddress_expand.h b/tests/performance_tests/subaddress_expand.h index f4ed568e1..505e27ba2 100644 --- a/tests/performance_tests/subaddress_expand.h +++ b/tests/performance_tests/subaddress_expand.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2022, The Monero Project +// Copyright (c) 2017-2024, The Monero Project // // All rights reserved. // diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 41b3d4873..86de9705a 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -57,7 +57,6 @@ set(unit_tests_sources epee_utils.cpp expect.cpp fee.cpp - fake_pruned_blockchain.cpp json_serialization.cpp check_output_types.cpp protocol_tx.cpp diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp index cc0d0b4a0..73d444f7c 100644 --- a/tests/unit_tests/account.cpp +++ b/tests/unit_tests/account.cpp @@ -119,7 +119,7 @@ TEST(account, derive_output_key) hw::device *hw = &hw::get_device("default"); ASSERT_TRUE(hw->generate_key_derivation(txkey.first, secret_view_key, derivation)); - ASSERT_TRUE(is_out_to_acc_precomp(subaddresses, txkey.second, derivation, {}, 0, *hw, boost::none)); + ASSERT_TRUE(is_out_to_acc_precomp(subaddresses, txkey.second, derivation, std::vector(), 0, *hw, boost::none)); i++; } diff --git a/tests/unit_tests/carrot_core.cpp b/tests/unit_tests/carrot_core.cpp index e83e7ce2a..14217d3ec 100644 --- a/tests/unit_tests/carrot_core.cpp +++ b/tests/unit_tests/carrot_core.cpp @@ -1559,7 +1559,7 @@ static void get_coinbase_output_proposal_janus_attack_v1(const JanusAttackPropos s_sender_receiver); // 8. C_a = G + a H - const rct::key amount_commitment = rct::zeroCommitVartime(proposal.normal.amount); + const rct::key amount_commitment = rct::zeroCommit(proposal.normal.amount); // 9. Ko = K^i_s + K^o_ext = K^i_s + (k^o_g G + k^o_t T) make_carrot_onetime_address(proposal.readjusted_opening_subaddress_spend_pubkey, diff --git a/tests/unit_tests/check_output_types.cpp b/tests/unit_tests/check_output_types.cpp index faa496ffb..2124c74e0 100644 --- a/tests/unit_tests/check_output_types.cpp +++ b/tests/unit_tests/check_output_types.cpp @@ -60,12 +60,7 @@ TEST(check_output_types, check_all) EXPECT_TRUE(check_output_types(tx, hf_version)); tx.vout.clear(); - // should only allow txout_to_key or txout_to_tagged_key output types - txout_to_script tx_out_script; - tx.vout.push_back(tx_out {0, tx_out_script}); - EXPECT_FALSE(check_output_types(tx, hf_version)); - tx.vout.clear(); - + // should only allow txout_to_key or txout_to_tagged_key output types txout_to_scripthash tx_out_scripthash; tx.vout.push_back(tx_out {0, tx_out_scripthash}); EXPECT_FALSE(check_output_types(tx, hf_version)); diff --git a/tests/unit_tests/crypto.cpp b/tests/unit_tests/crypto.cpp index c2c027648..d3b4757ed 100644 --- a/tests/unit_tests/crypto.cpp +++ b/tests/unit_tests/crypto.cpp @@ -34,6 +34,9 @@ #include "cryptonote_basic/cryptonote_basic_impl.h" #include "cryptonote_basic/merge_mining.h" +#include "string_tools.h" +#include "ringct/rctOps.h" +#include "ringct/rctTypes.h" namespace { diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp index 727b6c991..78d4bedfc 100644 --- a/tests/unit_tests/multisig.cpp +++ b/tests/unit_tests/multisig.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2022, The Monero Project +// Copyright (c) 2017-2024, The Monero Project // // All rights reserved. // @@ -43,24 +43,24 @@ static const struct } test_addresses[] = { { - "SaLvTyLhZmEeWVwtKBH6j5Fpmanj1t3ah1qQ2tScWFyqDFg6i1bEDw7eMP2NGbNthmS7v8jUK1riz2Sh6R6PEK2kTHGxpcFJb6B2f", - "468f7d5b698a6b14292be554886c0161416e241343f87b73786bb93a90d4a30d" + "9uvjbU54ZJb8j7Dcq1h3F1DnBRkxXdYUX4pbJ7mE3ghM8uF3fKzqRKRNAKYZXcNLqMg7MxjVVD2wKC2PALUwEveGSC3YSWD", + "2dd6e34a234c3e8b5d29a371789e4601e96dee4ea6f7ef79224d1a2d91164c01" }, { - "SaLvTyLmuEiATd9jLTjBkKAaapqEgn1JqTHkri8m9g4oT35TbQyi5vjjaX31j1szBtErUDHE9HG3JC4wQEx7k84QMGyAMms6tUp3m", - "d11819f2dd837c901371aaba30392a7014d1b634dfbf2d0c24ee302fee60f004" + "9ywDBAyDbb6QKFiZxDJ4hHZqZEQXXCR5EaYNcndUpqPDeE7rEgs6neQdZnhcDrWbURYK8xUjhuG2mVjJdmknrZbcG7NnbaB", + "fac47aecc948ce9d3531aa042abb18235b1df632087c55a361b632ffdd6ede0c" }, { - "SaLvTyLBwuJ1EPjNJ86Ezv1PDo5bLAbFcSobs9LU9it88nHNsd7XTtWMBxLNAgERE7Lz5CxyhYfeRK2TZs5AGpW7XoTZT3TAioR37", - "27d90aad7db5026751c3fb12c7b7ddc137844b720ed446bca91092704456950f" + "9t6Hn946u3eah5cuncH1hB5hGzsTUoevtf4SY7MHN5NgJZh2SFWsyVt3vUhuHyRKyrCQvr71Lfc1AevG3BXE11PQFoXDtD8", + "bbd3175ef9fd9f5eefdc43035f882f74ad14c4cf1799d8b6f9001bc197175d02" }, { - "SaLvTyLVzqUUo2RdEWLX8jZgif7wJL5SWg4PvkBmkynFR6sqc3kNmAtjjzVUH8J73yfjceR5ZhbDmhRQYtrrej4KJLGMSAgS8t236", - "113c262c9621ded1a55a2ff4d22d7a26cd25a444532daa018795ff257b690b0a" + "9zmAWoNyNPbgnYSm3nJNpAKHm6fCcs3MR94gBWxp9MCDUiMUhyYFfyQETUDLPF7DP6ZsmNo6LRxwPP9VmhHNxKrER9oGigT", + "f2efae45bef1917a7430cda8fcffc4ee010e3178761aa41d4628e23b1fe2d501" }, { - "SaLvTyLQAH4U9sgB7vT5Na9X5UrHsf5Fp6eFpkQQ1wQyg7gRyKoiXCQ4HA8Fmg6xqdhtDWcWmrbfyTcGNPRJokqiJpkMsVpicdK1R", - "5c3f39f8a5f05c928cddcadcdd56854e2068be7020450a5f3404ae4782e5f907" + "9ue8NJMg3WzKxTtmjeXzWYF5KmU6dC7LHEt9wvYdPn2qMmoFUa8hJJHhSHvJ46UEwpDyy5jSboNMRaDBKwU54NT42YcNUp5", + "a4cef54ed3fd61cd78a2ceb82ecf85a903ad2db9a86fb77ff56c35c56016280a" } }; @@ -149,6 +149,7 @@ static void check_results(const std::vector &intermediate_infos, std::unordered_set unique_privkeys; rct::key composite_pubkey = rct::identity(); + ASSERT_TRUE(wallets.size() > 0); wallets[0].decrypt_keys(""); crypto::public_key spend_pubkey = wallets[0].get_account().get_keys().m_account_address.m_spend_public_key; crypto::secret_key view_privkey = wallets[0].get_account().get_keys().m_view_secret_key; @@ -156,32 +157,48 @@ static void check_results(const std::vector &intermediate_infos, EXPECT_TRUE(crypto::secret_key_to_public_key(view_privkey, view_pubkey)); wallets[0].encrypt_keys(""); - for (size_t i = 0; i < wallets.size(); ++i) + // at the end of multisig kex, all wallets should emit a post-kex message with the same two pubkeys + std::vector post_kex_msg_pubkeys; + ASSERT_TRUE(intermediate_infos.size() == wallets.size()); + for (const std::string &intermediate_info : intermediate_infos) { - EXPECT_TRUE(!intermediate_infos[i].empty()); - bool ready; - uint32_t threshold, total; - EXPECT_TRUE(wallets[i].multisig(&ready, &threshold, &total)); - EXPECT_TRUE(ready); - EXPECT_TRUE(threshold == M); - EXPECT_TRUE(total == wallets.size()); + multisig::multisig_kex_msg post_kex_msg; + EXPECT_TRUE(!intermediate_info.empty()); + EXPECT_NO_THROW(post_kex_msg = intermediate_info); - wallets[i].decrypt_keys(""); + if (post_kex_msg_pubkeys.size() != 0) + EXPECT_TRUE(post_kex_msg_pubkeys == post_kex_msg.get_msg_pubkeys()); //assumes sorting is always the same + else + post_kex_msg_pubkeys = post_kex_msg.get_msg_pubkeys(); - if (i != 0) - { - // "equals" is transitive relation so we need only to compare first wallet's address to each others' addresses. - // no need to compare 0's address with itself. - EXPECT_TRUE(wallets[0].get_account().get_public_address_str(cryptonote::TESTNET) == - wallets[i].get_account().get_public_address_str(cryptonote::TESTNET)); - - EXPECT_EQ(spend_pubkey, wallets[i].get_account().get_keys().m_account_address.m_spend_public_key); - EXPECT_EQ(view_privkey, wallets[i].get_account().get_keys().m_view_secret_key); - EXPECT_EQ(view_pubkey, wallets[i].get_account().get_keys().m_account_address.m_view_public_key); - } + EXPECT_TRUE(post_kex_msg_pubkeys.size() == 2); + } + + // the post-kex pubkeys should equal the account's public view and spend keys + EXPECT_TRUE(std::find(post_kex_msg_pubkeys.begin(), post_kex_msg_pubkeys.end(), spend_pubkey) != post_kex_msg_pubkeys.end()); + EXPECT_TRUE(std::find(post_kex_msg_pubkeys.begin(), post_kex_msg_pubkeys.end(), view_pubkey) != post_kex_msg_pubkeys.end()); + + // each wallet should have the same state (private view key, public spend key), and the public spend key should be + // reproducible from the private spend keys found in each account + for (tools::wallet2 &wallet : wallets) + { + wallet.decrypt_keys(""); + const multisig::multisig_account_status ms_status{wallet.get_multisig_status()}; + EXPECT_TRUE(ms_status.multisig_is_active); + EXPECT_TRUE(ms_status.kex_is_done); + EXPECT_TRUE(ms_status.is_ready); + EXPECT_TRUE(ms_status.threshold == M); + EXPECT_TRUE(ms_status.total == wallets.size()); + + EXPECT_TRUE(wallets[0].get_account().get_public_address_str(cryptonote::TESTNET) == + wallet.get_account().get_public_address_str(cryptonote::TESTNET)); + + EXPECT_EQ(spend_pubkey, wallet.get_account().get_keys().m_account_address.m_spend_public_key); + EXPECT_EQ(view_privkey, wallet.get_account().get_keys().m_view_secret_key); + EXPECT_EQ(view_pubkey, wallet.get_account().get_keys().m_account_address.m_view_public_key); // sum together unique multisig keys - for (const auto &privkey : wallets[i].get_account().get_keys().m_multisig_keys) + for (const auto &privkey : wallet.get_account().get_keys().m_multisig_keys) { EXPECT_NE(privkey, crypto::null_skey); @@ -189,17 +206,17 @@ static void check_results(const std::vector &intermediate_infos, { unique_privkeys.insert(privkey); crypto::public_key pubkey; - crypto::secret_key_to_public_key(privkey, pubkey); + EXPECT_TRUE(crypto::secret_key_to_public_key(privkey, pubkey)); EXPECT_NE(privkey, crypto::null_skey); EXPECT_NE(pubkey, crypto::null_pkey); EXPECT_NE(pubkey, rct::rct2pk(rct::identity())); rct::addKeys(composite_pubkey, composite_pubkey, rct::pk2rct(pubkey)); } } - wallets[i].encrypt_keys(""); + wallet.encrypt_keys(""); } - // final key via sums should equal the wallets' public spend key + // final key via sum of privkeys should equal the wallets' public spend key wallets[0].decrypt_keys(""); EXPECT_EQ(wallets[0].get_account().get_keys().m_account_address.m_spend_public_key, rct::rct2pk(composite_pubkey)); wallets[0].encrypt_keys(""); @@ -226,10 +243,8 @@ static void make_wallets(const unsigned int M, const unsigned int N, const bool } // wallets should not be multisig yet - for (const auto &wallet: wallets) - { - ASSERT_FALSE(wallet.multisig()); - } + for (const auto& wallet: wallets) + ASSERT_FALSE(wallet.get_multisig_status().multisig_is_active); // make wallets multisig, get second round kex messages (if appropriate) std::vector intermediate_infos(wallets.size()); @@ -242,16 +257,15 @@ static void make_wallets(const unsigned int M, const unsigned int N, const bool ++rounds_complete; // perform kex rounds until kex is complete - bool ready; - wallets[0].multisig(&ready); - while (!ready) + multisig::multisig_account_status ms_status{wallets[0].get_multisig_status()}; + while (!ms_status.is_ready) { if (force_update) intermediate_infos = exchange_round_force_update(wallets, intermediate_infos, rounds_complete + 1); else intermediate_infos = exchange_round(wallets, intermediate_infos); - wallets[0].multisig(&ready); + ms_status = wallets[0].get_multisig_status(); ++rounds_complete; } @@ -260,6 +274,104 @@ static void make_wallets(const unsigned int M, const unsigned int N, const bool check_results(intermediate_infos, wallets, M); } +static void make_wallets_boosting(std::vector& wallets, unsigned int M) +{ + ASSERT_TRUE(wallets.size() > 1 && wallets.size() <= KEYS_COUNT); + ASSERT_TRUE(M <= wallets.size()); + std::uint32_t kex_rounds_required = multisig::multisig_kex_rounds_required(wallets.size(), M); + std::uint32_t rounds_required = multisig::multisig_setup_rounds_required(wallets.size(), M); + std::uint32_t rounds_complete{0}; + + // initialize wallets, get first round multisig kex msgs + std::vector initial_infos(wallets.size()); + + for (size_t i = 0; i < wallets.size(); ++i) + { + make_wallet(i, wallets[i]); + + wallets[i].decrypt_keys(""); + initial_infos[i] = wallets[i].get_multisig_first_kex_msg(); + wallets[i].encrypt_keys(""); + } + + // wallets should not be multisig yet + for (const auto &wallet: wallets) + { + const multisig::multisig_account_status ms_status{wallet.get_multisig_status()}; + ASSERT_FALSE(ms_status.multisig_is_active); + } + + // get round 2 booster messages for wallet0 (if appropriate) + auto initial_infos_truncated = initial_infos; + initial_infos_truncated.erase(initial_infos_truncated.begin()); + + std::vector wallet0_booster_infos; + wallet0_booster_infos.reserve(wallets.size() - 1); + + if (rounds_complete + 1 < kex_rounds_required) + { + for (size_t i = 1; i < wallets.size(); ++i) + { + wallet0_booster_infos.push_back( + wallets[i].get_multisig_key_exchange_booster("", initial_infos_truncated, M, wallets.size()) + ); + } + } + + // make wallets multisig + std::vector intermediate_infos(wallets.size()); + + for (size_t i = 0; i < wallets.size(); ++i) + intermediate_infos[i] = wallets[i].make_multisig("", initial_infos, M); + + ++rounds_complete; + + // perform all kex rounds + // boost wallet0 each round, so wallet0 is always 1 round ahead + std::string wallet0_intermediate_info; + std::vector new_infos(intermediate_infos.size()); + multisig::multisig_account_status ms_status{wallets[0].get_multisig_status()}; + while (!ms_status.is_ready) + { + // use booster infos to update wallet0 'early' + if (rounds_complete < kex_rounds_required) + new_infos[0] = wallets[0].exchange_multisig_keys("", wallet0_booster_infos); + else + { + // force update the post-kex round with wallet0's post-kex message since wallet0 is 'ahead' of the other wallets + wallet0_booster_infos = {wallets[0].exchange_multisig_keys("", {})}; + new_infos[0] = wallets[0].exchange_multisig_keys("", wallet0_booster_infos, true); + } + + // get wallet0 booster infos for next round + if (rounds_complete + 1 < kex_rounds_required) + { + // remove wallet0 info for this round (so boosters have incomplete kex message set) + auto intermediate_infos_truncated = intermediate_infos; + intermediate_infos_truncated.erase(intermediate_infos_truncated.begin()); + + // obtain booster messages from all other wallets + for (size_t i = 1; i < wallets.size(); ++i) + { + wallet0_booster_infos[i-1] = + wallets[i].get_multisig_key_exchange_booster("", intermediate_infos_truncated, M, wallets.size()); + } + } + + // update other wallets + for (size_t i = 1; i < wallets.size(); ++i) + new_infos[i] = wallets[i].exchange_multisig_keys("", intermediate_infos); + + intermediate_infos = new_infos; + ++rounds_complete; + ms_status = wallets[0].get_multisig_status(); + } + + EXPECT_EQ(rounds_required, rounds_complete); + + check_results(intermediate_infos, wallets, M); +} + TEST(multisig, make_1_2) { make_wallets(1, 2, false); @@ -296,6 +408,12 @@ TEST(multisig, make_2_4) make_wallets(2, 4, true); } +TEST(multisig, make_2_4_boosting) +{ + std::vector wallets(4); + make_wallets_boosting(wallets, 2); +} + TEST(multisig, multisig_kex_msg) { using namespace multisig; diff --git a/tests/unit_tests/tx_construction_helpers.cpp b/tests/unit_tests/tx_construction_helpers.cpp index e98992337..e6db2077c 100644 --- a/tests/unit_tests/tx_construction_helpers.cpp +++ b/tests/unit_tests/tx_construction_helpers.cpp @@ -120,7 +120,7 @@ bool construct_miner_tx_fake_reward_1out(const size_t height, crypto::derive_view_tag(derivation, local_output_index, view_tag); cryptonote::tx_out out; - cryptonote::set_tx_out(reward, out_eph_public_key, use_view_tags, view_tag, out); + cryptonote::set_tx_out(reward, "SAL", 0, out_eph_public_key, use_view_tags, view_tag, out); tx.vout.push_back(out); } @@ -171,7 +171,7 @@ cryptonote::tx_source_entry gen_tx_source_entry_fake_members( continue; used_indices.insert(global_output_index); const rct::ctkey output_pair{rct::pkGen(), - in.is_rct ? rct::pkGen() : rct::zeroCommitVartime(in.amount)}; + in.is_rct ? rct::pkGen() : rct::zeroCommit(in.amount)}; res.outputs.push_back({global_output_index, output_pair}); } // sort by index @@ -214,29 +214,6 @@ cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs( switch (hf_version) { case 1: - case 2: - case 3: - case HF_VERSION_DYNAMIC_FEE: - case 5: - case HF_VERSION_MIN_MIXIN_4: - case 7: - rct_config = { rct::RangeProofBorromean, 0 }; - break; - case HF_VERSION_PER_BYTE_FEE: - case 9: - rct_config = { rct::RangeProofPaddedBulletproof, 1 }; - break; - case HF_VERSION_SMALLER_BP: - case 11: - case HF_VERSION_MIN_2_OUTPUTS: - rct_config = { rct::RangeProofPaddedBulletproof, 2 }; - break; - case HF_VERSION_CLSAG: - case 14: - rct_config = { rct::RangeProofPaddedBulletproof, 3 }; - break; - case HF_VERSION_BULLETPROOF_PLUS: - case 16: rct_config = { rct::RangeProofPaddedBulletproof, 4 }; break; default: @@ -334,18 +311,23 @@ cryptonote::transaction construct_pre_carrot_tx_with_fake_inputs( "failed to add nonce to tx_extra"); } - fcmp_pp::ProofParams dummy_fcmp_params; + // fcmp_pp::ProofParams dummy_fcmp_params; const bool r = cryptonote::construct_tx_and_get_tx_key( sender_account_keys, sender_subaddress_map, sources, destinations, + 1, + "SAL", + "SAL", + cryptonote::transaction_type::TRANSFER, change_addr, extra, tx, + 0, main_tx_privkey_out, additional_tx_privkeys_out, - dummy_fcmp_params, + // dummy_fcmp_params, rct, rct_config, use_view_tags); diff --git a/tests/unit_tests/wallet_scanning.cpp b/tests/unit_tests/wallet_scanning.cpp index f9042a5a0..72a2e0b06 100644 --- a/tests/unit_tests/wallet_scanning.cpp +++ b/tests/unit_tests/wallet_scanning.cpp @@ -31,8 +31,8 @@ #include "carrot_impl/address_device_ram_borrowed.h" #include "carrot_mock_helpers.h" #include "cryptonote_basic/cryptonote_basic_impl.h" -#include "fake_pruned_blockchain.h" -#include "fcmp_pp/prove.h" +// #include "fake_pruned_blockchain.h" +// #include "fcmp_pp/prove.h" #include "tx_construction_helpers.h" #include "wallet/tx_builder.h" @@ -54,7 +54,7 @@ TEST(wallet_scanning, view_scan_as_sender_mainaddr) const rct::xmr_amount fee = 565678; - for (uint8_t hf_version = 1; hf_version < HF_VERSION_FCMP_PLUS_PLUS; ++hf_version) + for (uint8_t hf_version = 1; hf_version < HF_VERSION_CARROT; ++hf_version) { MDEBUG("view_scan_as_sender_mainaddr: hf_version=" << static_cast(hf_version)); @@ -123,7 +123,7 @@ TEST(wallet_scanning, view_scan_long_payment_id) const crypto::hash payment_id = crypto::rand(); - for (uint8_t hf_version = 1; hf_version < HF_VERSION_FCMP_PLUS_PLUS; ++hf_version) + for (uint8_t hf_version = 1; hf_version < HF_VERSION_CARROT; ++hf_version) { MDEBUG("view_scan_as_sender_mainaddr: hf_version=" << static_cast(hf_version)); @@ -201,7 +201,7 @@ TEST(wallet_scanning, view_scan_short_payment_id) ASSERT_FALSE(tools::wallet::is_long_payment_id(payment_id)); ASSERT_NE(crypto::null_hash, payment_id); - for (uint8_t hf_version = 1; hf_version < HF_VERSION_FCMP_PLUS_PLUS; ++hf_version) + for (uint8_t hf_version = 1; hf_version < HF_VERSION_CARROT; ++hf_version) { MDEBUG("view_scan_as_sender_mainaddr: hf_version=" << static_cast(hf_version)); @@ -257,416 +257,416 @@ TEST(wallet_scanning, view_scan_short_payment_id) } } //---------------------------------------------------------------------------------------------------------------------- -TEST(wallet_scanning, positive_smallout_main_addr_all_types_outputs) -{ - // Test that wallet can scan and recover enotes of following type: - // a. pre-ringct coinbase - // b. pre-ringct - // c. ringct coinbase - // d. ringct long-amount - // e. ringct short-amount - // f. view-tagged ringct coinbase - // g. view-tagged pre-ringct (only possible in unmixable sweep txs) - // h. view-tagged ringct - // i. carrot v1 coinbase - // j. carrot v1 normal - // k. carrot v1 special - // l. carrot v1 internal (@TODO) - // - // All enotes are addressed to the main address in 2-out noin-coinbase txs or 1-out coinbase txs. - // We also don't test reorgs here. +// TEST(wallet_scanning, positive_smallout_main_addr_all_types_outputs) +// { +// // Test that wallet can scan and recover enotes of following type: +// // a. pre-ringct coinbase +// // b. pre-ringct +// // c. ringct coinbase +// // d. ringct long-amount +// // e. ringct short-amount +// // f. view-tagged ringct coinbase +// // g. view-tagged pre-ringct (only possible in unmixable sweep txs) +// // h. view-tagged ringct +// // i. carrot v1 coinbase +// // j. carrot v1 normal +// // k. carrot v1 special +// // l. carrot v1 internal (@TODO) +// // +// // All enotes are addressed to the main address in 2-out noin-coinbase txs or 1-out coinbase txs. +// // We also don't test reorgs here. - // init blockchain - mock::fake_pruned_blockchain bc(0); +// // init blockchain +// mock::fake_pruned_blockchain bc(0); - // generate wallet - tools::wallet2 w(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); - w.generate("", ""); - const cryptonote::account_keys &acc_keys = w.get_account().get_keys(); - const cryptonote::account_public_address main_addr = w.get_account().get_keys().m_account_address; - ASSERT_EQ(0, w.balance(0, true)); - bc.init_wallet_for_starting_block(w); // needed b/c internal logic +// // generate wallet +// tools::wallet2 w(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); +// w.generate("", ""); +// const cryptonote::account_keys &acc_keys = w.get_account().get_keys(); +// const cryptonote::account_public_address main_addr = w.get_account().get_keys().m_account_address; +// ASSERT_EQ(0, w.balance(0, true)); +// bc.init_wallet_for_starting_block(w); // needed b/c internal logic - uint64_t refresh_height = 0; - const auto wallet_process_new_blocks = [&w, &bc, &refresh_height]() -> boost::multiprecision::int128_t - { - const boost::multiprecision::int128_t old_balance = w.balance(0, true); +// uint64_t refresh_height = 0; +// const auto wallet_process_new_blocks = [&w, &bc, &refresh_height]() -> boost::multiprecision::int128_t +// { +// const boost::multiprecision::int128_t old_balance = w.balance(0, true); - // note: doesn't handle reorgs - std::vector block_entries; - std::vector parsed_blocks; - bc.get_blocks_data(0, bc.height()-1, block_entries, parsed_blocks); //! @TODO: figure out why starting from refresh_height doesn't work - uint64_t blocks_added{}; - auto output_tracker_cache = w.create_output_tracker_cache(); - w.process_parsed_blocks(0, block_entries, parsed_blocks, blocks_added, output_tracker_cache); +// // note: doesn't handle reorgs +// std::vector block_entries; +// std::vector parsed_blocks; +// bc.get_blocks_data(0, bc.height()-1, block_entries, parsed_blocks); //! @TODO: figure out why starting from refresh_height doesn't work +// uint64_t blocks_added{}; +// auto output_tracker_cache = w.create_output_tracker_cache(); +// w.process_parsed_blocks(0, block_entries, parsed_blocks, blocks_added, output_tracker_cache); - // update refresh_height - refresh_height = bc.height(); +// // update refresh_height +// refresh_height = bc.height(); - // return amount of money received - return boost::multiprecision::int128_t(w.balance(0, true)) - old_balance; - }; +// // return amount of money received +// return boost::multiprecision::int128_t(w.balance(0, true)) - old_balance; +// }; - // a. push block containing a pre-ringct coinbase output to wallet - bc.add_block(1, {}, main_addr); +// // a. push block containing a pre-ringct coinbase output to wallet +// bc.add_block(1, {}, main_addr); - // a. scan pre-ringct coinbase tx - auto balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); - EXPECT_TRUE(verify_sals_of_recent_transfers()); +// // a. scan pre-ringct coinbase tx +// auto balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); +// EXPECT_TRUE(verify_sals_of_recent_transfers()); - // b. construct and push a pre-ringct tx - const rct::xmr_amount amount_b = rct::randXmrAmount(COIN); - { - const rct::xmr_amount fee = rct::randXmrAmount(COIN); - std::vector dests = { - cryptonote::tx_destination_entry(amount_b, acc_keys.m_account_address, false)}; - cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( - acc_keys, - w.m_subaddresses, - /*stripped_sources=*/{}, - dests, - acc_keys.m_account_address, - fee, - /*hf_version=*/1); - ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); - ASSERT_EQ(1, curr_tx.version); - ASSERT_EQ(rct::RCTTypeNull, curr_tx.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_key), curr_tx.vout.at(0).target.type()); - ASSERT_EQ(amount_b, curr_tx.vout.at(0).amount); - bc.add_block(1, {std::move(curr_tx)}, mock::null_addr); - } +// // b. construct and push a pre-ringct tx +// const rct::xmr_amount amount_b = rct::randXmrAmount(COIN); +// { +// const rct::xmr_amount fee = rct::randXmrAmount(COIN); +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_b, acc_keys.m_account_address, false)}; +// cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( +// acc_keys, +// w.m_subaddresses, +// /*stripped_sources=*/{}, +// dests, +// acc_keys.m_account_address, +// fee, +// /*hf_version=*/1); +// ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); +// ASSERT_EQ(1, curr_tx.version); +// ASSERT_EQ(rct::RCTTypeNull, curr_tx.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_key), curr_tx.vout.at(0).target.type()); +// ASSERT_EQ(amount_b, curr_tx.vout.at(0).amount); +// bc.add_block(1, {std::move(curr_tx)}, mock::null_addr); +// } - // b. scan pre-ringct tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(amount_b, balance_diff); +// // b. scan pre-ringct tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(amount_b, balance_diff); - // c. construct and push a ringct coinbase tx - bc.add_block(HF_VERSION_DYNAMIC_FEE, {}, main_addr); - { - auto top_block = bc.get_parsed_block(bc.height() - 1); - const cryptonote::transaction &top_miner_tx = top_block.block.miner_tx; - ASSERT_EQ(2, top_miner_tx.version); - ASSERT_NE(0, top_miner_tx.vout.size()); - ASSERT_EQ(rct::RCTTypeNull, top_miner_tx.rct_signatures.type); - ASSERT_EQ(0, top_miner_tx.signatures.size()); - ASSERT_EQ(mock::fake_pruned_blockchain::miner_reward, top_miner_tx.vout.at(0).amount); - } +// // c. construct and push a ringct coinbase tx +// bc.add_block(HF_VERSION_DYNAMIC_FEE, {}, main_addr); +// { +// auto top_block = bc.get_parsed_block(bc.height() - 1); +// const cryptonote::transaction &top_miner_tx = top_block.block.miner_tx; +// ASSERT_EQ(2, top_miner_tx.version); +// ASSERT_NE(0, top_miner_tx.vout.size()); +// ASSERT_EQ(rct::RCTTypeNull, top_miner_tx.rct_signatures.type); +// ASSERT_EQ(0, top_miner_tx.signatures.size()); +// ASSERT_EQ(mock::fake_pruned_blockchain::miner_reward, top_miner_tx.vout.at(0).amount); +// } - // c. scan ringct coinbase tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); - EXPECT_TRUE(verify_sals_of_recent_transfers()); +// // c. scan ringct coinbase tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); +// EXPECT_TRUE(verify_sals_of_recent_transfers()); - // d. construct and push a ringct long-amount tx - const rct::xmr_amount amount_d = rct::randXmrAmount(COIN); - { - const rct::xmr_amount fee = rct::randXmrAmount(COIN); - std::vector dests = { - cryptonote::tx_destination_entry(amount_d, acc_keys.m_account_address, false)}; - cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( - acc_keys, - w.m_subaddresses, - /*stripped_sources=*/{}, - dests, - acc_keys.m_account_address, - fee, - HF_VERSION_DYNAMIC_FEE); - ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); - ASSERT_EQ(2, curr_tx.version); - ASSERT_EQ(rct::RCTTypeFull, curr_tx.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_key), curr_tx.vout.at(0).target.type()); - ASSERT_EQ(0, curr_tx.vout.at(0).amount); - bc.add_block(HF_VERSION_DYNAMIC_FEE, {std::move(curr_tx)}, mock::null_addr); - } +// // d. construct and push a ringct long-amount tx +// const rct::xmr_amount amount_d = rct::randXmrAmount(COIN); +// { +// const rct::xmr_amount fee = rct::randXmrAmount(COIN); +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_d, acc_keys.m_account_address, false)}; +// cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( +// acc_keys, +// w.m_subaddresses, +// /*stripped_sources=*/{}, +// dests, +// acc_keys.m_account_address, +// fee, +// HF_VERSION_DYNAMIC_FEE); +// ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); +// ASSERT_EQ(2, curr_tx.version); +// ASSERT_EQ(rct::RCTTypeFull, curr_tx.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_key), curr_tx.vout.at(0).target.type()); +// ASSERT_EQ(0, curr_tx.vout.at(0).amount); +// bc.add_block(HF_VERSION_DYNAMIC_FEE, {std::move(curr_tx)}, mock::null_addr); +// } - // d. scan ringct long-amount tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(amount_d, balance_diff); +// // d. scan ringct long-amount tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(amount_d, balance_diff); - // e. construct and push a ringct short-amount tx - const rct::xmr_amount amount_e = rct::randXmrAmount(COIN); - { - const rct::xmr_amount fee = rct::randXmrAmount(COIN); - std::vector dests = { - cryptonote::tx_destination_entry(amount_e, acc_keys.m_account_address, false)}; - cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( - acc_keys, - w.m_subaddresses, - /*stripped_sources=*/{}, - dests, - acc_keys.m_account_address, - fee, - HF_VERSION_SMALLER_BP); - ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); - ASSERT_EQ(2, curr_tx.version); - ASSERT_EQ(rct::RCTTypeBulletproof2, curr_tx.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_key), curr_tx.vout.at(0).target.type()); - ASSERT_EQ(0, curr_tx.vout.at(0).amount); - bc.add_block(HF_VERSION_SMALLER_BP, {std::move(curr_tx)}, mock::null_addr); - } +// // e. construct and push a ringct short-amount tx +// const rct::xmr_amount amount_e = rct::randXmrAmount(COIN); +// { +// const rct::xmr_amount fee = rct::randXmrAmount(COIN); +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_e, acc_keys.m_account_address, false)}; +// cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( +// acc_keys, +// w.m_subaddresses, +// /*stripped_sources=*/{}, +// dests, +// acc_keys.m_account_address, +// fee, +// HF_VERSION_SMALLER_BP); +// ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); +// ASSERT_EQ(2, curr_tx.version); +// ASSERT_EQ(rct::RCTTypeBulletproof2, curr_tx.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_key), curr_tx.vout.at(0).target.type()); +// ASSERT_EQ(0, curr_tx.vout.at(0).amount); +// bc.add_block(HF_VERSION_SMALLER_BP, {std::move(curr_tx)}, mock::null_addr); +// } - // e. scan ringct short-amount tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(amount_e, balance_diff); +// // e. scan ringct short-amount tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(amount_e, balance_diff); - // f. construct and push a view-tagged ringct coinbase tx - bc.add_block(HF_VERSION_VIEW_TAGS, {}, main_addr); - { - auto top_block = bc.get_parsed_block(bc.height() - 1); - const cryptonote::transaction &top_miner_tx = top_block.block.miner_tx; - ASSERT_EQ(2, top_miner_tx.version); - ASSERT_EQ(1, top_miner_tx.vout.size()); - ASSERT_EQ(rct::RCTTypeNull, top_miner_tx.rct_signatures.type); - ASSERT_EQ(0, top_miner_tx.signatures.size()); - ASSERT_EQ(typeid(cryptonote::txout_to_tagged_key), top_miner_tx.vout.at(0).target.type()); - ASSERT_EQ(mock::fake_pruned_blockchain::miner_reward, top_miner_tx.vout.at(0).amount); - } +// // f. construct and push a view-tagged ringct coinbase tx +// bc.add_block(HF_VERSION_VIEW_TAGS, {}, main_addr); +// { +// auto top_block = bc.get_parsed_block(bc.height() - 1); +// const cryptonote::transaction &top_miner_tx = top_block.block.miner_tx; +// ASSERT_EQ(2, top_miner_tx.version); +// ASSERT_EQ(1, top_miner_tx.vout.size()); +// ASSERT_EQ(rct::RCTTypeNull, top_miner_tx.rct_signatures.type); +// ASSERT_EQ(0, top_miner_tx.signatures.size()); +// ASSERT_EQ(typeid(cryptonote::txout_to_tagged_key), top_miner_tx.vout.at(0).target.type()); +// ASSERT_EQ(mock::fake_pruned_blockchain::miner_reward, top_miner_tx.vout.at(0).amount); +// } - // f. scan view-tagged ringct coinbase tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); - EXPECT_TRUE(verify_sals_of_recent_transfers()); +// // f. scan view-tagged ringct coinbase tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); +// EXPECT_TRUE(verify_sals_of_recent_transfers()); - // g. construct and push a view-tagged pre-ringct (only possible in unmixable sweep txs) tx - const rct::xmr_amount amount_g = rct::randXmrAmount(COIN); - { - const rct::xmr_amount fee = rct::randXmrAmount(COIN); - std::vector dests = { - cryptonote::tx_destination_entry(amount_g, acc_keys.m_account_address, false)}; - cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( - acc_keys, - w.m_subaddresses, - /*stripped_sources=*/{}, - dests, - acc_keys.m_account_address, - fee, - HF_VERSION_VIEW_TAGS, - /*sweep_unmixable_override=*/true); - ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); - ASSERT_EQ(1, curr_tx.version); - ASSERT_EQ(rct::RCTTypeNull, curr_tx.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_tagged_key), curr_tx.vout.at(0).target.type()); - ASSERT_EQ(amount_g, curr_tx.vout.at(0).amount); - bc.add_block(HF_VERSION_VIEW_TAGS, {std::move(curr_tx)}, mock::null_addr); - } +// // g. construct and push a view-tagged pre-ringct (only possible in unmixable sweep txs) tx +// const rct::xmr_amount amount_g = rct::randXmrAmount(COIN); +// { +// const rct::xmr_amount fee = rct::randXmrAmount(COIN); +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_g, acc_keys.m_account_address, false)}; +// cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( +// acc_keys, +// w.m_subaddresses, +// /*stripped_sources=*/{}, +// dests, +// acc_keys.m_account_address, +// fee, +// HF_VERSION_VIEW_TAGS, +// /*sweep_unmixable_override=*/true); +// ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); +// ASSERT_EQ(1, curr_tx.version); +// ASSERT_EQ(rct::RCTTypeNull, curr_tx.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_tagged_key), curr_tx.vout.at(0).target.type()); +// ASSERT_EQ(amount_g, curr_tx.vout.at(0).amount); +// bc.add_block(HF_VERSION_VIEW_TAGS, {std::move(curr_tx)}, mock::null_addr); +// } - // g. scan view-tagged pre-ringct (only possible in unmixable sweep txs) tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(amount_g, balance_diff); +// // g. scan view-tagged pre-ringct (only possible in unmixable sweep txs) tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(amount_g, balance_diff); - // h. construct and push a view-tagged ringct tx - const rct::xmr_amount amount_h = rct::randXmrAmount(COIN); - { - const rct::xmr_amount fee = rct::randXmrAmount(COIN); - std::vector dests = { - cryptonote::tx_destination_entry(amount_h, acc_keys.m_account_address, false)}; - cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( - acc_keys, - w.m_subaddresses, - /*stripped_sources=*/{}, - dests, - acc_keys.m_account_address, - fee, - HF_VERSION_VIEW_TAGS); - ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); - ASSERT_EQ(2, curr_tx.version); - ASSERT_EQ(rct::RCTTypeBulletproofPlus, curr_tx.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_tagged_key), curr_tx.vout.at(0).target.type()); - ASSERT_EQ(0, curr_tx.vout.at(0).amount); - bc.add_block(HF_VERSION_VIEW_TAGS, {std::move(curr_tx)}, mock::null_addr); - } +// // h. construct and push a view-tagged ringct tx +// const rct::xmr_amount amount_h = rct::randXmrAmount(COIN); +// { +// const rct::xmr_amount fee = rct::randXmrAmount(COIN); +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_h, acc_keys.m_account_address, false)}; +// cryptonote::transaction curr_tx = mock::construct_pre_carrot_tx_with_fake_inputs( +// acc_keys, +// w.m_subaddresses, +// /*stripped_sources=*/{}, +// dests, +// acc_keys.m_account_address, +// fee, +// HF_VERSION_VIEW_TAGS); +// ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); +// ASSERT_EQ(2, curr_tx.version); +// ASSERT_EQ(rct::RCTTypeBulletproofPlus, curr_tx.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_tagged_key), curr_tx.vout.at(0).target.type()); +// ASSERT_EQ(0, curr_tx.vout.at(0).amount); +// bc.add_block(HF_VERSION_VIEW_TAGS, {std::move(curr_tx)}, mock::null_addr); +// } - // h. scan ringct view-tagged ringct tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(amount_h, balance_diff); +// // h. scan ringct view-tagged ringct tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(amount_h, balance_diff); - // i. construct and push a carrot v1 coinbase tx - bc.add_block(HF_VERSION_CARROT, {}, main_addr); - { - auto top_block = bc.get_parsed_block(bc.height() - 1); - const cryptonote::transaction &top_miner_tx = top_block.block.miner_tx; - ASSERT_EQ(2, top_miner_tx.version); - ASSERT_EQ(1, top_miner_tx.vout.size()); - ASSERT_EQ(rct::RCTTypeNull, top_miner_tx.rct_signatures.type); - ASSERT_EQ(0, top_miner_tx.signatures.size()); - ASSERT_EQ(typeid(cryptonote::txout_to_carrot_v1), top_miner_tx.vout.at(0).target.type()); - ASSERT_EQ(mock::fake_pruned_blockchain::miner_reward, top_miner_tx.vout.at(0).amount); - } +// // i. construct and push a carrot v1 coinbase tx +// bc.add_block(HF_VERSION_CARROT, {}, main_addr); +// { +// auto top_block = bc.get_parsed_block(bc.height() - 1); +// const cryptonote::transaction &top_miner_tx = top_block.block.miner_tx; +// ASSERT_EQ(2, top_miner_tx.version); +// ASSERT_EQ(1, top_miner_tx.vout.size()); +// ASSERT_EQ(rct::RCTTypeNull, top_miner_tx.rct_signatures.type); +// ASSERT_EQ(0, top_miner_tx.signatures.size()); +// ASSERT_EQ(typeid(cryptonote::txout_to_carrot_v1), top_miner_tx.vout.at(0).target.type()); +// ASSERT_EQ(mock::fake_pruned_blockchain::miner_reward, top_miner_tx.vout.at(0).amount); +// } - // i. scan carrot v1 coinbase tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); - EXPECT_TRUE(verify_sals_of_recent_transfers()); +// // i. scan carrot v1 coinbase tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(mock::fake_pruned_blockchain::miner_reward, balance_diff); +// EXPECT_TRUE(verify_sals_of_recent_transfers()); - // j. construct and push a carrot v1 normal tx - const rct::xmr_amount amount_j = rct::randXmrAmount(COIN); - { - std::vector dests = { - cryptonote::tx_destination_entry(amount_j, acc_keys.m_account_address, false)}; - cryptonote::transaction curr_tx = mock::construct_carrot_pruned_transaction_fake_inputs( - {carrot::mock::convert_normal_payment_proposal_v1(dests.front())}, - /*selfsend_payment_proposals=*/{}, - acc_keys); - ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); - ASSERT_EQ(2, curr_tx.version); - ASSERT_EQ(rct::RCTTypeFcmpPlusPlus, curr_tx.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_carrot_v1), curr_tx.vout.at(0).target.type()); - ASSERT_EQ(0, curr_tx.vout.at(0).amount); - bc.add_block(HF_VERSION_CARROT, {std::move(curr_tx)}, mock::null_addr); - } +// // j. construct and push a carrot v1 normal tx +// const rct::xmr_amount amount_j = rct::randXmrAmount(COIN); +// { +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_j, acc_keys.m_account_address, false)}; +// cryptonote::transaction curr_tx = mock::construct_carrot_pruned_transaction_fake_inputs( +// {carrot::mock::convert_normal_payment_proposal_v1(dests.front())}, +// /*selfsend_payment_proposals=*/{}, +// acc_keys); +// ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); +// ASSERT_EQ(2, curr_tx.version); +// ASSERT_EQ(rct::RCTTypeFcmpPlusPlus, curr_tx.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_carrot_v1), curr_tx.vout.at(0).target.type()); +// ASSERT_EQ(0, curr_tx.vout.at(0).amount); +// bc.add_block(HF_VERSION_CARROT, {std::move(curr_tx)}, mock::null_addr); +// } - // j. scan carrot v1 normal tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(amount_j, balance_diff); +// // j. scan carrot v1 normal tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(amount_j, balance_diff); - // k. construct and push a carrot v1 special tx - const rct::xmr_amount amount_k = rct::randXmrAmount(COIN); - { - std::vector dests = { - cryptonote::tx_destination_entry(amount_k, acc_keys.m_account_address, false)}; - cryptonote::transaction curr_tx = mock::construct_carrot_pruned_transaction_fake_inputs( - /*normal_payment_proposals=*/{}, - {{carrot::mock::convert_selfsend_payment_proposal_v1(dests.front()), {/*main*/}}}, - acc_keys); - ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); - ASSERT_EQ(2, curr_tx.version); - ASSERT_EQ(rct::RCTTypeFcmpPlusPlus, curr_tx.rct_signatures.type); - ASSERT_EQ(2, curr_tx.vout.size()); - ASSERT_EQ(typeid(cryptonote::txout_to_carrot_v1), curr_tx.vout.at(0).target.type()); - ASSERT_EQ(0, curr_tx.vout.at(0).amount); - bc.add_block(HF_VERSION_CARROT, {std::move(curr_tx)}, mock::null_addr); - } +// // k. construct and push a carrot v1 special tx +// const rct::xmr_amount amount_k = rct::randXmrAmount(COIN); +// { +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_k, acc_keys.m_account_address, false)}; +// cryptonote::transaction curr_tx = mock::construct_carrot_pruned_transaction_fake_inputs( +// /*normal_payment_proposals=*/{}, +// {{carrot::mock::convert_selfsend_payment_proposal_v1(dests.front()), {/*main*/}}}, +// acc_keys); +// ASSERT_FALSE(cryptonote::is_coinbase(curr_tx)); +// ASSERT_EQ(2, curr_tx.version); +// ASSERT_EQ(rct::RCTTypeFcmpPlusPlus, curr_tx.rct_signatures.type); +// ASSERT_EQ(2, curr_tx.vout.size()); +// ASSERT_EQ(typeid(cryptonote::txout_to_carrot_v1), curr_tx.vout.at(0).target.type()); +// ASSERT_EQ(0, curr_tx.vout.at(0).amount); +// bc.add_block(HF_VERSION_CARROT, {std::move(curr_tx)}, mock::null_addr); +// } - // k. scan carrot v1 special tx - balance_diff = wallet_process_new_blocks(); - EXPECT_EQ(amount_k, balance_diff); -} +// // k. scan carrot v1 special tx +// balance_diff = wallet_process_new_blocks(); +// EXPECT_EQ(amount_k, balance_diff); +// } //---------------------------------------------------------------------------------------------------------------------- -TEST(wallet_scanning, burned_zombie) -{ - // Check that a wallet which receives attempted burn outputs counts all outputs of the same key - // image spent when that key image is spent. Those with the same key image which aren't marked - // as spent are "burned zombies": they aren't burn and not usable, but they shuffle around in - // the internal state and inflate the balance or attract input selection. +// TEST(wallet_scanning, burned_zombie) +// { +// // Check that a wallet which receives attempted burn outputs counts all outputs of the same key +// // image spent when that key image is spent. Those with the same key image which aren't marked +// // as spent are "burned zombies": they aren't burn and not usable, but they shuffle around in +// // the internal state and inflate the balance or attract input selection. - // init blockchain - mock::fake_pruned_blockchain bc(0); +// // init blockchain +// mock::fake_pruned_blockchain bc(0); - // generate wallet - tools::wallet2 w(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); - w.set_offline(true); - w.generate("", ""); - const cryptonote::account_keys &acc_keys = w.get_account().get_keys(); - const cryptonote::account_public_address main_addr = w.get_account().get_keys().m_account_address; - ASSERT_EQ(0, w.balance(0, true)); - bc.init_wallet_for_starting_block(w); // needed b/c internal logic +// // generate wallet +// tools::wallet2 w(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); +// w.set_offline(true); +// w.generate("", ""); +// const cryptonote::account_keys &acc_keys = w.get_account().get_keys(); +// const cryptonote::account_public_address main_addr = w.get_account().get_keys().m_account_address; +// ASSERT_EQ(0, w.balance(0, true)); +// bc.init_wallet_for_starting_block(w); // needed b/c internal logic - const rct::xmr_amount amount_a = rct::randXmrAmount(COIN) + 1; - const rct::xmr_amount amount_b = rct::randXmrAmount(amount_a - 1); - const rct::xmr_amount fee = rct::randXmrAmount(COIN); +// const rct::xmr_amount amount_a = rct::randXmrAmount(COIN) + 1; +// const rct::xmr_amount amount_b = rct::randXmrAmount(amount_a - 1); +// const rct::xmr_amount fee = rct::randXmrAmount(COIN); - // make incoming pre-ringct tx to wallet with amount a - cryptonote::transaction incoming_tx_a; - { - std::vector dests = { - cryptonote::tx_destination_entry(amount_a, main_addr, false)}; - incoming_tx_a = mock::construct_pre_carrot_tx_with_fake_inputs( - dests, - fee, - /*hf_version=*/1); - ASSERT_FALSE(cryptonote::is_coinbase(incoming_tx_a)); - ASSERT_EQ(1, incoming_tx_a.version); - ASSERT_EQ(rct::RCTTypeNull, incoming_tx_a.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_key), incoming_tx_a.vout.at(0).target.type()); - ASSERT_EQ(amount_a, incoming_tx_a.vout.at(0).amount); - } +// // make incoming pre-ringct tx to wallet with amount a +// cryptonote::transaction incoming_tx_a; +// { +// std::vector dests = { +// cryptonote::tx_destination_entry(amount_a, main_addr, false)}; +// incoming_tx_a = mock::construct_pre_carrot_tx_with_fake_inputs( +// dests, +// fee, +// /*hf_version=*/1); +// ASSERT_FALSE(cryptonote::is_coinbase(incoming_tx_a)); +// ASSERT_EQ(1, incoming_tx_a.version); +// ASSERT_EQ(rct::RCTTypeNull, incoming_tx_a.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_key), incoming_tx_a.vout.at(0).target.type()); +// ASSERT_EQ(amount_a, incoming_tx_a.vout.at(0).amount); +// } - // make a burn transaction with amount b < a - cryptonote::transaction incoming_tx_b = incoming_tx_a; - boost::get(incoming_tx_b.vin.at(0)).k_image = rct::rct2ki(rct::pkGen()); - incoming_tx_b.vout[0].amount = amount_b; +// // make a burn transaction with amount b < a +// cryptonote::transaction incoming_tx_b = incoming_tx_a; +// boost::get(incoming_tx_b.vin.at(0)).k_image = rct::rct2ki(rct::pkGen()); +// incoming_tx_b.vout[0].amount = amount_b; - // submit burning transaction first - bc.add_block(1, {incoming_tx_b}, mock::null_addr); +// // submit burning transaction first +// bc.add_block(1, {incoming_tx_b}, mock::null_addr); - // then submit original transaction - bc.add_block(1, {incoming_tx_a}, mock::null_addr); +// // then submit original transaction +// bc.add_block(1, {incoming_tx_a}, mock::null_addr); - // add 10 blocks to put space between sending outgoing tx - const cryptonote::tx_out dummy_output{.amount = 5, .target = cryptonote::txout_to_key(rct::rct2pk(rct::pkGen()))}; - cryptonote::transaction dummy_tx; //! @TODO: remove dummy as prop fixing another bug - dummy_tx.version = 1; - dummy_tx.unlock_time = 0; - dummy_tx.vout.push_back(dummy_output); - for (size_t i = 0; i <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE; ++i) - bc.add_block(1, {dummy_tx}, mock::null_addr); +// // add 10 blocks to put space between sending outgoing tx +// const cryptonote::tx_out dummy_output{.amount = 5, .target = cryptonote::txout_to_key(rct::rct2pk(rct::pkGen()))}; +// cryptonote::transaction dummy_tx; //! @TODO: remove dummy as prop fixing another bug +// dummy_tx.version = 1; +// dummy_tx.unlock_time = 0; +// dummy_tx.vout.push_back(dummy_output); +// for (size_t i = 0; i <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE; ++i) +// bc.add_block(1, {dummy_tx}, mock::null_addr); - // scan, assert balance is amount a, (NOT a + b) and get key image to received output - bc.refresh_wallet(w, 0); - ASSERT_EQ(amount_a, w.balance_all(true)); - uint64_t key_image_offset; - std::vector> exported_key_images; - std::tie(key_image_offset, exported_key_images) = w.export_key_images(/*all=*/true); - ASSERT_EQ(0, key_image_offset); - ASSERT_EQ(2, exported_key_images.size()); - const crypto::key_image &received_key_image = exported_key_images.at(0).first; - ASSERT_EQ(received_key_image, exported_key_images.at(1).first); +// // scan, assert balance is amount a, (NOT a + b) and get key image to received output +// bc.refresh_wallet(w, 0); +// ASSERT_EQ(amount_a, w.balance_all(true)); +// uint64_t key_image_offset; +// std::vector> exported_key_images; +// std::tie(key_image_offset, exported_key_images) = w.export_key_images(/*all=*/true); +// ASSERT_EQ(0, key_image_offset); +// ASSERT_EQ(2, exported_key_images.size()); +// const crypto::key_image &received_key_image = exported_key_images.at(0).first; +// ASSERT_EQ(received_key_image, exported_key_images.at(1).first); - // make "outgoing" transaction by including this key image in an input - cryptonote::transaction outgoing_tx; - { - const rct::xmr_amount outgoing_amount = rct::randXmrAmount(amount_a); - const rct::xmr_amount outgoing_fee = amount_a - outgoing_amount; - std::vector dests = { - cryptonote::tx_destination_entry(outgoing_amount, mock::null_addr, false)}; - crypto::secret_key main_tx_privkey; - std::vector additional_tx_privkeys; - tools::wallet2::transfer_container transfers; - w.get_transfers(transfers); - ASSERT_EQ(2, transfers.size()); - const tools::wallet2::transfer_details &input_td = transfers.at(1); - const mock::stripped_down_tx_source_entry_t input_src{ - .is_rct = false, - .global_output_index = input_td.m_global_output_index, - .onetime_address = input_td.get_public_key(), - .real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(input_td.m_tx, input_td.m_pk_index), - .local_output_index = 0, - .amount = amount_a, - .mask = rct::I - }; - outgoing_tx = mock::construct_pre_carrot_tx_with_fake_inputs(acc_keys, - w.m_subaddresses, - {input_src}, - dests, - /*change_addr=*/{}, - crypto::null_hash, - outgoing_fee, - /*hf_version=*/1, - main_tx_privkey, - additional_tx_privkeys); - ASSERT_FALSE(cryptonote::is_coinbase(outgoing_tx)); - ASSERT_EQ(1, outgoing_tx.version); - ASSERT_EQ(1, outgoing_tx.vin.size()); - ASSERT_EQ(1, outgoing_tx.vout.size()); - ASSERT_EQ(received_key_image, boost::get(outgoing_tx.vin.at(0)).k_image); - ASSERT_EQ(rct::RCTTypeNull, outgoing_tx.rct_signatures.type); - ASSERT_EQ(typeid(cryptonote::txout_to_key), outgoing_tx.vout.at(0).target.type()); - ASSERT_EQ(amount_a, boost::get(outgoing_tx.vin.at(0)).amount); - ASSERT_EQ(outgoing_amount, outgoing_tx.vout.at(0).amount); - ASSERT_EQ(outgoing_fee, cryptonote::get_tx_fee(outgoing_tx)); - } +// // make "outgoing" transaction by including this key image in an input +// cryptonote::transaction outgoing_tx; +// { +// const rct::xmr_amount outgoing_amount = rct::randXmrAmount(amount_a); +// const rct::xmr_amount outgoing_fee = amount_a - outgoing_amount; +// std::vector dests = { +// cryptonote::tx_destination_entry(outgoing_amount, mock::null_addr, false)}; +// crypto::secret_key main_tx_privkey; +// std::vector additional_tx_privkeys; +// tools::wallet2::transfer_container transfers; +// w.get_transfers(transfers); +// ASSERT_EQ(2, transfers.size()); +// const tools::wallet2::transfer_details &input_td = transfers.at(1); +// const mock::stripped_down_tx_source_entry_t input_src{ +// .is_rct = false, +// .global_output_index = input_td.m_global_output_index, +// .onetime_address = input_td.get_public_key(), +// .real_out_tx_key = cryptonote::get_tx_pub_key_from_extra(input_td.m_tx, input_td.m_pk_index), +// .local_output_index = 0, +// .amount = amount_a, +// .mask = rct::I +// }; +// outgoing_tx = mock::construct_pre_carrot_tx_with_fake_inputs(acc_keys, +// w.m_subaddresses, +// {input_src}, +// dests, +// /*change_addr=*/{}, +// crypto::null_hash, +// outgoing_fee, +// /*hf_version=*/1, +// main_tx_privkey, +// additional_tx_privkeys); +// ASSERT_FALSE(cryptonote::is_coinbase(outgoing_tx)); +// ASSERT_EQ(1, outgoing_tx.version); +// ASSERT_EQ(1, outgoing_tx.vin.size()); +// ASSERT_EQ(1, outgoing_tx.vout.size()); +// ASSERT_EQ(received_key_image, boost::get(outgoing_tx.vin.at(0)).k_image); +// ASSERT_EQ(rct::RCTTypeNull, outgoing_tx.rct_signatures.type); +// ASSERT_EQ(typeid(cryptonote::txout_to_key), outgoing_tx.vout.at(0).target.type()); +// ASSERT_EQ(amount_a, boost::get(outgoing_tx.vin.at(0)).amount); +// ASSERT_EQ(outgoing_amount, outgoing_tx.vout.at(0).amount); +// ASSERT_EQ(outgoing_fee, cryptonote::get_tx_fee(outgoing_tx)); +// } - // add outgoing tx to chain and wallet scans it - bc.add_block(1, {outgoing_tx}, mock::null_addr); - bc.refresh_wallet(w, 0); +// // add outgoing tx to chain and wallet scans it +// bc.add_block(1, {outgoing_tx}, mock::null_addr); +// bc.refresh_wallet(w, 0); - // check that the balance drops to 0 and that all transfers are marked spent - ASSERT_EQ(0, w.balance_all(true)); - tools::wallet2::transfer_container post_spend_transfers; - w.get_transfers(post_spend_transfers); - ASSERT_EQ(2, post_spend_transfers.size()); - for (const tools::wallet2::transfer_details &td : post_spend_transfers) - ASSERT_TRUE(td.m_spent); -} +// // check that the balance drops to 0 and that all transfers are marked spent +// ASSERT_EQ(0, w.balance_all(true)); +// tools::wallet2::transfer_container post_spend_transfers; +// w.get_transfers(post_spend_transfers); +// ASSERT_EQ(2, post_spend_transfers.size()); +// for (const tools::wallet2::transfer_details &td : post_spend_transfers) +// ASSERT_TRUE(td.m_spent); +// } //---------------------------------------------------------------------------------------------------------------------- diff --git a/tests/unit_tests/wallet_storage.cpp b/tests/unit_tests/wallet_storage.cpp new file mode 100644 index 000000000..43c5954cd --- /dev/null +++ b/tests/unit_tests/wallet_storage.cpp @@ -0,0 +1,384 @@ +// Copyright (c) 2023-2024, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "unit_tests_utils.h" +#include "gtest/gtest.h" + +#include + +#include "file_io_utils.h" +#include "wallet/wallet2.h" +#include "common/util.h" + +using namespace boost::filesystem; +using namespace epee::file_io_utils; + +static constexpr const char WALLET_00fd416a_PRIMARY_ADDRESS[] = + "45p2SngJAPSJbqSiUvYfS3BfhEdxZmv8pDt25oW1LzxrZv9Uq6ARagiFViMGUE3gJk5VPWingCXVf1p2tyAy6SUeSHPhbve"; + +// https://github.com/monero-project/monero/blob/67d190ce7c33602b6a3b804f633ee1ddb7fbb4a1/src/wallet/wallet2.cpp#L156 +static constexpr const char WALLET2_ASCII_OUTPUT_MAGIC[] = "MoneroAsciiDataV1"; + +TEST(wallet_storage, store_to_file2file) +{ + const path source_wallet_file = unit_test::data_dir / "wallet_00fd416a"; + const path interm_wallet_file = unit_test::data_dir / "wallet_00fd416a_copy_file2file"; + const path target_wallet_file = unit_test::data_dir / "wallet_00fd416a_new_file2file"; + + ASSERT_TRUE(is_file_exist(source_wallet_file.string())); + ASSERT_TRUE(is_file_exist(source_wallet_file.string() + ".keys")); + + tools::copy_file(source_wallet_file.string(), interm_wallet_file.string()); + tools::copy_file(source_wallet_file.string() + ".keys", interm_wallet_file.string() + ".keys"); + + ASSERT_TRUE(is_file_exist(interm_wallet_file.string())); + ASSERT_TRUE(is_file_exist(interm_wallet_file.string() + ".keys")); + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + epee::wipeable_string password("beepbeep"); + + const auto files_are_expected = [&]() + { + EXPECT_FALSE(is_file_exist(interm_wallet_file.string())); + EXPECT_FALSE(is_file_exist(interm_wallet_file.string() + ".keys")); + EXPECT_TRUE(is_file_exist(target_wallet_file.string())); + EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + }; + + { + tools::wallet2 w; + w.load(interm_wallet_file.string(), password); + const std::string primary_address = w.get_address_as_str(); + EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address); + w.store_to(target_wallet_file.string(), password); + files_are_expected(); + } + + files_are_expected(); + + { + tools::wallet2 w; + w.load(target_wallet_file.string(), password); + const std::string primary_address = w.get_address_as_str(); + EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address); + w.store_to("", ""); + files_are_expected(); + } + + files_are_expected(); +} + +TEST(wallet_storage, store_to_mem2file) +{ + const path target_wallet_file = unit_test::data_dir / "wallet_mem2file"; + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + epee::wipeable_string password("beepbeep2"); + + { + tools::wallet2 w; + w.generate("", password); + w.store_to(target_wallet_file.string(), password); + + EXPECT_TRUE(is_file_exist(target_wallet_file.string())); + EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + } + + EXPECT_TRUE(is_file_exist(target_wallet_file.string())); + EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + + { + tools::wallet2 w; + w.load(target_wallet_file.string(), password); + + EXPECT_TRUE(is_file_exist(target_wallet_file.string())); + EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + } + + EXPECT_TRUE(is_file_exist(target_wallet_file.string())); + EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); +} + +TEST(wallet_storage, change_password_same_file) +{ + const path source_wallet_file = unit_test::data_dir / "wallet_00fd416a"; + const path interm_wallet_file = unit_test::data_dir / "wallet_00fd416a_copy_change_password_same"; + + ASSERT_TRUE(is_file_exist(source_wallet_file.string())); + ASSERT_TRUE(is_file_exist(source_wallet_file.string() + ".keys")); + + tools::copy_file(source_wallet_file.string(), interm_wallet_file.string()); + tools::copy_file(source_wallet_file.string() + ".keys", interm_wallet_file.string() + ".keys"); + + ASSERT_TRUE(is_file_exist(interm_wallet_file.string())); + ASSERT_TRUE(is_file_exist(interm_wallet_file.string() + ".keys")); + + epee::wipeable_string old_password("beepbeep"); + epee::wipeable_string new_password("meepmeep"); + + { + tools::wallet2 w; + w.load(interm_wallet_file.string(), old_password); + const std::string primary_address = w.get_address_as_str(); + EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address); + w.change_password(w.get_wallet_file(), old_password, new_password); + } + + { + tools::wallet2 w; + w.load(interm_wallet_file.string(), new_password); + const std::string primary_address = w.get_address_as_str(); + EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address); + } + + { + tools::wallet2 w; + EXPECT_THROW(w.load(interm_wallet_file.string(), old_password), tools::error::invalid_password); + } +} + +TEST(wallet_storage, change_password_different_file) +{ + const path source_wallet_file = unit_test::data_dir / "wallet_00fd416a"; + const path interm_wallet_file = unit_test::data_dir / "wallet_00fd416a_copy_change_password_diff"; + const path target_wallet_file = unit_test::data_dir / "wallet_00fd416a_new_change_password_diff"; + + ASSERT_TRUE(is_file_exist(source_wallet_file.string())); + ASSERT_TRUE(is_file_exist(source_wallet_file.string() + ".keys")); + + tools::copy_file(source_wallet_file.string(), interm_wallet_file.string()); + tools::copy_file(source_wallet_file.string() + ".keys", interm_wallet_file.string() + ".keys"); + + ASSERT_TRUE(is_file_exist(interm_wallet_file.string())); + ASSERT_TRUE(is_file_exist(interm_wallet_file.string() + ".keys")); + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + epee::wipeable_string old_password("beepbeep"); + epee::wipeable_string new_password("meepmeep"); + + { + tools::wallet2 w; + w.load(interm_wallet_file.string(), old_password); + const std::string primary_address = w.get_address_as_str(); + EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address); + w.change_password(target_wallet_file.string(), old_password, new_password); + } + + EXPECT_FALSE(is_file_exist(interm_wallet_file.string())); + EXPECT_FALSE(is_file_exist(interm_wallet_file.string() + ".keys")); + EXPECT_TRUE(is_file_exist(target_wallet_file.string())); + EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + + { + tools::wallet2 w; + w.load(target_wallet_file.string(), new_password); + const std::string primary_address = w.get_address_as_str(); + EXPECT_EQ(WALLET_00fd416a_PRIMARY_ADDRESS, primary_address); + } +} + +TEST(wallet_storage, change_password_in_memory) +{ + const epee::wipeable_string password1("monero"); + const epee::wipeable_string password2("means money"); + const epee::wipeable_string password_wrong("is traceable"); + + tools::wallet2 w; + w.generate("", password1); + const std::string primary_address_1 = w.get_address_as_str(); + w.change_password("", password1, password2); + const std::string primary_address_2 = w.get_address_as_str(); + EXPECT_EQ(primary_address_1, primary_address_2); + + EXPECT_THROW(w.change_password("", password_wrong, password1), tools::error::invalid_password); +} + +TEST(wallet_storage, change_password_mem2file) +{ + const path target_wallet_file = unit_test::data_dir / "wallet_change_password_mem2file"; + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + const epee::wipeable_string password1("https://safecurves.cr.yp.to/rigid.html"); + const epee::wipeable_string password2( + "https://csrc.nist.gov/csrc/media/projects/crypto-standards-development-process/documents/dualec_in_x982_and_sp800-90.pdf"); + + std::string primary_address_1, primary_address_2; + { + tools::wallet2 w; + w.generate("", password1); + primary_address_1 = w.get_address_as_str(); + w.change_password(target_wallet_file.string(), password1, password2); + } + + EXPECT_TRUE(is_file_exist(target_wallet_file.string())); + EXPECT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + + { + tools::wallet2 w; + w.load(target_wallet_file.string(), password2); + primary_address_2 = w.get_address_as_str(); + } + + EXPECT_EQ(primary_address_1, primary_address_2); +} + +TEST(wallet_storage, gen_ascii_format) +{ + const path target_wallet_file = unit_test::data_dir / "wallet_gen_ascii_format"; + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + const epee::wipeable_string password("https://safecurves.cr.yp.to/rigid.html"); + + std::string primary_address_1, primary_address_2; + { + tools::wallet2 w; + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.generate(target_wallet_file.string(), password); + primary_address_1 = w.get_address_as_str(); + } + + ASSERT_TRUE(is_file_exist(target_wallet_file.string())); + ASSERT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + + // Assert that we store keys in ascii format + { + std::string key_file_contents; + ASSERT_TRUE(epee::file_io_utils::load_file_to_string(target_wallet_file.string() + ".keys", key_file_contents)); + EXPECT_NE(std::string::npos, key_file_contents.find(WALLET2_ASCII_OUTPUT_MAGIC)); + for (const char c : key_file_contents) + ASSERT_TRUE(std::isprint(c) || c == '\n' || c == '\r'); + } + + { + tools::wallet2 w; + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.load(target_wallet_file.string(), password); + primary_address_2 = w.get_address_as_str(); + } + + EXPECT_EQ(primary_address_1, primary_address_2); +} + +TEST(wallet_storage, change_export_format) +{ + const path target_wallet_file = unit_test::data_dir / "wallet_change_export_format"; + + if (is_file_exist(target_wallet_file.string())) + remove(target_wallet_file); + if (is_file_exist(target_wallet_file.string() + ".keys")) + remove(target_wallet_file.string() + ".keys"); + ASSERT_FALSE(is_file_exist(target_wallet_file.string())); + ASSERT_FALSE(is_file_exist(target_wallet_file.string() + ".keys")); + + const epee::wipeable_string password("https://safecurves.cr.yp.to/rigid.html"); + + std::string primary_address_1, primary_address_2; + { + tools::wallet2 w; + ASSERT_EQ(tools::wallet2::Binary, w.export_format()); + w.generate(target_wallet_file.string(), password); + primary_address_1 = w.get_address_as_str(); + w.store(); + + // Assert that we initially store keys in binary format + { + std::string key_file_contents; + ASSERT_TRUE(epee::file_io_utils::load_file_to_string(target_wallet_file.string() + ".keys", key_file_contents)); + EXPECT_EQ(std::string::npos, key_file_contents.find(WALLET2_ASCII_OUTPUT_MAGIC)); + bool only_printable = true; + for (const char c : key_file_contents) + { + if (!std::isprint(c) && c != '\n' && c != '\r') + { + only_printable = false; + break; + } + } + EXPECT_FALSE(only_printable); + } + + // switch formats and store + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.store_to("", password, /*force_rewrite_keys=*/ true); + } + + ASSERT_TRUE(is_file_exist(target_wallet_file.string())); + ASSERT_TRUE(is_file_exist(target_wallet_file.string() + ".keys")); + + // Assert that we store keys in ascii format + { + std::string key_file_contents; + ASSERT_TRUE(epee::file_io_utils::load_file_to_string(target_wallet_file.string() + ".keys", key_file_contents)); + EXPECT_NE(std::string::npos, key_file_contents.find(WALLET2_ASCII_OUTPUT_MAGIC)); + for (const char c : key_file_contents) + ASSERT_TRUE(std::isprint(c) || c == '\n' || c == '\r'); + } + + { + tools::wallet2 w; + w.set_export_format(tools::wallet2::Ascii); + ASSERT_EQ(tools::wallet2::Ascii, w.export_format()); + w.load(target_wallet_file.string(), password); + primary_address_2 = w.get_address_as_str(); + } + + EXPECT_EQ(primary_address_1, primary_address_2); +} diff --git a/tests/unit_tests/wallet_tx_builder.cpp b/tests/unit_tests/wallet_tx_builder.cpp index 6281c2cdc..9281fa9d4 100644 --- a/tests/unit_tests/wallet_tx_builder.cpp +++ b/tests/unit_tests/wallet_tx_builder.cpp @@ -30,6 +30,7 @@ #include "gtest/gtest.h" #include "carrot_core/config.h" +#include "carrot_mock_helpers.h" #include "common/container_helpers.h" #include "ringct/rctOps.h" #include "ringct/rctSigs.h" @@ -171,7 +172,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_transfer_1) /*ignore_below=*/0, {}, top_block_index, - alice); + alice.get_keys()); ASSERT_EQ(1, tx_proposals.size()); const carrot::CarrotTransactionProposalV1 tx_proposal = tx_proposals.at(0); @@ -651,147 +652,147 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_sweep_6) ASSERT_NE(enote_type_0, enote_type_1); } //---------------------------------------------------------------------------------------------------------------------- -TEST(wallet_tx_builder, wallet2_scan_propose_sign_prove_member_and_scan_1) -{ - // 1. create fake blockchain - // 2. create Alice, Bob wallet2 instance - // 3. send a mix of fake-input legacy and carrot txs to Alice - // 4. step blockchain forward 10 blocks - // 5. scan blockchain with Alice wallet - // 6. create carrot transaction proposal - // 7. construct proofs for transaction - // 8. serialize tx - // 9. deserialize tx - // 10. check ver_non_input_consensus() - // 11. check verRctNonSemanticsSimple() - // 12. add Alice's transaction to blockchain - // 13. scan blockchain with Bob's wallet and assert money received - // 14. scan blockchain with Alice's wallet and assert money left +// TEST(wallet_tx_builder, wallet2_scan_propose_sign_prove_member_and_scan_1) +// { +// // 1. create fake blockchain +// // 2. create Alice, Bob wallet2 instance +// // 3. send a mix of fake-input legacy and carrot txs to Alice +// // 4. step blockchain forward 10 blocks +// // 5. scan blockchain with Alice wallet +// // 6. create carrot transaction proposal +// // 7. construct proofs for transaction +// // 8. serialize tx +// // 9. deserialize tx +// // 10. check ver_non_input_consensus() +// // 11. check verRctNonSemanticsSimple() +// // 12. add Alice's transaction to blockchain +// // 13. scan blockchain with Bob's wallet and assert money received +// // 14. scan blockchain with Alice's wallet and assert money left - // 1. - LOG_PRINT_L2("Initiating my imaginary, friendly chain of blocks"); - mock::fake_pruned_blockchain bc(0); +// // 1. +// LOG_PRINT_L2("Initiating my imaginary, friendly chain of blocks"); +// mock::fake_pruned_blockchain bc(0); - // 2. - LOG_PRINT_L2("Generating wallets for Alice and Bob, the usual suspects"); - tools::wallet2 alice(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); - tools::wallet2 bob(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); - alice.set_offline(true); - bob.set_offline(true); - alice.generate("", ""); - bob.generate("", ""); - const cryptonote::account_keys &alice_keys = alice.get_account().get_keys(); - const cryptonote::account_public_address alice_main_addr = alice.get_account().get_keys().m_account_address; - const cryptonote::account_public_address bob_main_addr = bob.get_account().get_keys().m_account_address; - bc.init_wallet_for_starting_block(alice); - bc.init_wallet_for_starting_block(bob); +// // 2. +// LOG_PRINT_L2("Generating wallets for Alice and Bob, the usual suspects"); +// tools::wallet2 alice(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); +// tools::wallet2 bob(cryptonote::MAINNET, /*kdf_rounds=*/1, /*unattended=*/true); +// alice.set_offline(true); +// bob.set_offline(true); +// alice.generate("", ""); +// bob.generate("", ""); +// const cryptonote::account_keys &alice_keys = alice.get_account().get_keys(); +// const cryptonote::account_public_address alice_main_addr = alice.get_account().get_keys().m_account_address; +// const cryptonote::account_public_address bob_main_addr = bob.get_account().get_keys().m_account_address; +// bc.init_wallet_for_starting_block(alice); +// bc.init_wallet_for_starting_block(bob); - // 3. - LOG_PRINT_L2("Sending transactions from the aether to Alice (0)"); - const rct::xmr_amount amount0 = rct::randXmrAmount(COIN); - std::vector dests0{cryptonote::tx_destination_entry(amount0, alice_main_addr, false)}; - cryptonote::transaction tx = mock::construct_pre_carrot_tx_with_fake_inputs(dests0, /*fee=*/1234, /*hf_version=*/2); - bc.add_block(2, {std::move(tx)}, mock::null_addr); - LOG_PRINT_L2("Sending transactions from the aether to Alice (1)"); - const rct::xmr_amount amount1 = rct::randXmrAmount(COIN); - std::vector dests1{cryptonote::tx_destination_entry(amount1, alice.get_subaddress({0, 13}), true)}; - cryptonote::account_base aether; - aether.generate(); - tx = mock::construct_carrot_pruned_transaction_fake_inputs({carrot::mock::convert_normal_payment_proposal_v1(dests1.front())}, {}, aether.get_keys()); - bc.add_block(HF_VERSION_CARROT, {std::move(tx)}, mock::null_addr); +// // 3. +// LOG_PRINT_L2("Sending transactions from the aether to Alice (0)"); +// const rct::xmr_amount amount0 = rct::randXmrAmount(COIN); +// std::vector dests0{cryptonote::tx_destination_entry(amount0, alice_main_addr, false)}; +// cryptonote::transaction tx = mock::construct_pre_carrot_tx_with_fake_inputs(dests0, /*fee=*/1234, /*hf_version=*/2); +// bc.add_block(2, {std::move(tx)}, mock::null_addr); +// LOG_PRINT_L2("Sending transactions from the aether to Alice (1)"); +// const rct::xmr_amount amount1 = rct::randXmrAmount(COIN); +// std::vector dests1{cryptonote::tx_destination_entry(amount1, alice.get_subaddress({0, 13}), true)}; +// cryptonote::account_base aether; +// aether.generate(); +// tx = mock::construct_carrot_pruned_transaction_fake_inputs({carrot::mock::convert_normal_payment_proposal_v1(dests1.front())}, {}, aether.get_keys()); +// bc.add_block(HF_VERSION_CARROT, {std::move(tx)}, mock::null_addr); - // 4. - //!@TODO: figure out why membership proving fails if there's fewer leaves than the curve1 width - const size_t target_num_outputs = fcmp_pp::curve_trees::SELENE_CHUNK_WIDTH * fcmp_pp::curve_trees::HELIOS_CHUNK_WIDTH + 7; - while (bc.num_outputs() < target_num_outputs) - bc.add_block(HF_VERSION_CARROT, {}, mock::null_addr, target_num_outputs - bc.num_outputs()); +// // 4. +// //!@TODO: figure out why membership proving fails if there's fewer leaves than the curve1 width +// const size_t target_num_outputs = fcmp_pp::curve_trees::SELENE_CHUNK_WIDTH * fcmp_pp::curve_trees::HELIOS_CHUNK_WIDTH + 7; +// while (bc.num_outputs() < target_num_outputs) +// bc.add_block(HF_VERSION_CARROT, {}, mock::null_addr, target_num_outputs - bc.num_outputs()); - LOG_PRINT_L2("Twiddling thumbs"); - for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) - bc.add_block(HF_VERSION_CARROT, {}, mock::null_addr); +// LOG_PRINT_L2("Twiddling thumbs"); +// for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) +// bc.add_block(HF_VERSION_CARROT, {}, mock::null_addr); - // 5. - LOG_PRINT_L2("Alice's vision is filled with shadowy keys, hashes, points, rings, trees, curves, chains, all flowing in and out of one another"); - uint64_t blocks_added = bc.refresh_wallet(alice, 0); - ASSERT_EQ(bc.height()-1, blocks_added); - ASSERT_EQ(2, alice.m_transfers.size()); - ASSERT_EQ(amount0 + amount1, alice.balance_all(true)); // really, we care about unlocked_balance_all() for sending, but that call uses RPC +// // 5. +// LOG_PRINT_L2("Alice's vision is filled with shadowy keys, hashes, points, rings, trees, curves, chains, all flowing in and out of one another"); +// uint64_t blocks_added = bc.refresh_wallet(alice, 0); +// ASSERT_EQ(bc.height()-1, blocks_added); +// ASSERT_EQ(2, alice.m_transfers.size()); +// ASSERT_EQ(amount0 + amount1, alice.balance_all(true)); // really, we care about unlocked_balance_all() for sending, but that call uses RPC - // 6. - LOG_PRINT_L2("Alice feels pity on Bob and proposes to send his broke ass some dough"); - const rct::xmr_amount out_amount = rct::randXmrAmount(amount0 + amount1); - const std::vector tx_proposals = - tools::wallet::make_carrot_transaction_proposals_wallet2_transfer( // stupidly long function name ;( - alice.m_transfers, - alice.m_subaddresses, - {cryptonote::tx_destination_entry(out_amount, bob_main_addr, false)}, - /*fee_per_weight=*/1, - /*extra=*/{}, - /*subaddr_account=*/0, - /*subaddr_indices=*/{}, - /*ignore_above=*/std::numeric_limits::max(), - /*ignore_below=*/0, - {}, - /*top_block_index=*/bc.height()-1, - alice_keys); +// // 6. +// LOG_PRINT_L2("Alice feels pity on Bob and proposes to send his broke ass some dough"); +// const rct::xmr_amount out_amount = rct::randXmrAmount(amount0 + amount1); +// const std::vector tx_proposals = +// tools::wallet::make_carrot_transaction_proposals_wallet2_transfer( // stupidly long function name ;( +// alice.m_transfers, +// alice.m_subaddresses, +// {cryptonote::tx_destination_entry(out_amount, bob_main_addr, false)}, +// /*fee_per_weight=*/1, +// /*extra=*/{}, +// /*subaddr_account=*/0, +// /*subaddr_indices=*/{}, +// /*ignore_above=*/std::numeric_limits::max(), +// /*ignore_below=*/0, +// {}, +// /*top_block_index=*/bc.height()-1, +// alice_keys); - ASSERT_EQ(1, tx_proposals.size()); - const carrot::CarrotTransactionProposalV1 tx_proposal = tx_proposals.at(0); +// ASSERT_EQ(1, tx_proposals.size()); +// const carrot::CarrotTransactionProposalV1 tx_proposal = tx_proposals.at(0); - // 7. - LOG_PRINT_L2("Alice has something to prove"); - tx = tools::wallet::finalize_all_proofs_from_transfer_details(tx_proposal, - alice.m_transfers, - alice.m_tree_cache, - *alice.m_curve_trees, - alice_keys); +// // 7. +// LOG_PRINT_L2("Alice has something to prove"); +// tx = tools::wallet::finalize_all_proofs_from_transfer_details(tx_proposal, +// alice.m_transfers, +// alice.m_tree_cache, +// *alice.m_curve_trees, +// alice_keys); - // 8. - LOG_PRINT_L2("Hello, Mr. Blobby"); - const cryptonote::blobdata alicebob_tx_blob = cryptonote::tx_to_blob(tx); +// // 8. +// LOG_PRINT_L2("Hello, Mr. Blobby"); +// const cryptonote::blobdata alicebob_tx_blob = cryptonote::tx_to_blob(tx); - // 9. - LOG_PRINT_L2("Goodbye, Mr. Blobby"); - cryptonote::transaction alicebob_tx; - ASSERT_TRUE(cryptonote::parse_and_validate_tx_from_blob(alicebob_tx_blob, alicebob_tx)); +// // 9. +// LOG_PRINT_L2("Goodbye, Mr. Blobby"); +// cryptonote::transaction alicebob_tx; +// ASSERT_TRUE(cryptonote::parse_and_validate_tx_from_blob(alicebob_tx_blob, alicebob_tx)); - // 10. - LOG_PRINT_L2("Bob couldn't believe someone to be so generous in his time of need, so he verifies"); - ASSERT_GE(bc.hf_version(), HF_VERSION_FCMP_PLUS_PLUS); - cryptonote::tx_verification_context tvc{}; - ASSERT_TRUE(cryptonote::ver_non_input_consensus(alicebob_tx, tvc, bc.hf_version())); - EXPECT_FALSE(tvc.m_verifivation_failed); +// // 10. +// LOG_PRINT_L2("Bob couldn't believe someone to be so generous in his time of need, so he verifies"); +// ASSERT_GE(bc.hf_version(), HF_VERSION_FCMP_PLUS_PLUS); +// cryptonote::tx_verification_context tvc{}; +// ASSERT_TRUE(cryptonote::ver_non_input_consensus(alicebob_tx, tvc, bc.hf_version())); +// EXPECT_FALSE(tvc.m_verifivation_failed); - // 11. - LOG_PRINT_L2("'Perhaps this is valid money that belongs to another chain', Bob postulates"); - const uint8_t *tree_root = bc.get_fcmp_tree_root_at(bc.height() - 1); - ASSERT_TRUE(cryptonote::Blockchain::expand_transaction_2(alicebob_tx, - cryptonote::get_transaction_prefix_hash(alicebob_tx), - /*pubkeys=*/{}, - tree_root)); - EXPECT_TRUE(rct::verRctNonSemanticsSimple(alicebob_tx.rct_signatures)); +// // 11. +// LOG_PRINT_L2("'Perhaps this is valid money that belongs to another chain', Bob postulates"); +// const uint8_t *tree_root = bc.get_fcmp_tree_root_at(bc.height() - 1); +// ASSERT_TRUE(cryptonote::Blockchain::expand_transaction_2(alicebob_tx, +// cryptonote::get_transaction_prefix_hash(alicebob_tx), +// /*pubkeys=*/{}, +// tree_root)); +// EXPECT_TRUE(rct::verRctNonSemanticsSimple(alicebob_tx.rct_signatures)); - // 12. - LOG_PRINT_L2("'Chain, chain, chain (Chain, chain, chain)' - Aretha Franklin"); - const rct::xmr_amount alicebob_tx_fee = alicebob_tx.rct_signatures.txnFee; - bc.add_block(HF_VERSION_CARROT, {std::move(alicebob_tx)}, mock::null_addr); +// // 12. +// LOG_PRINT_L2("'Chain, chain, chain (Chain, chain, chain)' - Aretha Franklin"); +// const rct::xmr_amount alicebob_tx_fee = alicebob_tx.rct_signatures.txnFee; +// bc.add_block(HF_VERSION_CARROT, {std::move(alicebob_tx)}, mock::null_addr); - // 13. - LOG_PRINT_L2("A great day for Bob"); - ASSERT_EQ(0, bob.balance_all(true)); - blocks_added = bc.refresh_wallet(bob, 0); - ASSERT_EQ(bc.height()-1, blocks_added); - ASSERT_EQ(1, bob.m_transfers.size()); - EXPECT_EQ(out_amount, bob.balance_all(true)); +// // 13. +// LOG_PRINT_L2("A great day for Bob"); +// ASSERT_EQ(0, bob.balance_all(true)); +// blocks_added = bc.refresh_wallet(bob, 0); +// ASSERT_EQ(bc.height()-1, blocks_added); +// ASSERT_EQ(1, bob.m_transfers.size()); +// EXPECT_EQ(out_amount, bob.balance_all(true)); - // 14. - LOG_PRINT_L2("Alice obtains the fulfillment that only stems from selfless generosity"); - const rct::xmr_amount alice_old_balance = alice.balance_all(true); - ASSERT_GE(alice_old_balance, out_amount + alicebob_tx_fee); - blocks_added = bc.refresh_wallet(alice, 0); - ASSERT_EQ(1, blocks_added); - const rct::xmr_amount alice_new_balance = alice.balance_all(true); - ASSERT_LT(alice_new_balance, alice_old_balance); - EXPECT_EQ(alice_new_balance + out_amount + alicebob_tx_fee, alice_old_balance); -} +// // 14. +// LOG_PRINT_L2("Alice obtains the fulfillment that only stems from selfless generosity"); +// const rct::xmr_amount alice_old_balance = alice.balance_all(true); +// ASSERT_GE(alice_old_balance, out_amount + alicebob_tx_fee); +// blocks_added = bc.refresh_wallet(alice, 0); +// ASSERT_EQ(1, blocks_added); +// const rct::xmr_amount alice_new_balance = alice.balance_all(true); +// ASSERT_LT(alice_new_balance, alice_old_balance); +// EXPECT_EQ(alice_new_balance + out_amount + alicebob_tx_fee, alice_old_balance); +// } //----------------------------------------------------------------------------------------------------------------------