tests compiles

This commit is contained in:
akildemir
2025-05-13 15:46:47 +03:00
parent f1e48c9094
commit 7a05f26810
20 changed files with 1305 additions and 669 deletions
+69 -25
View File
@@ -12,9 +12,10 @@ TimingsDatabase::TimingsDatabase()
{
}
TimingsDatabase::TimingsDatabase(const std::string &filename):
TimingsDatabase::TimingsDatabase(const std::string &filename, const bool load_previous /*=false*/):
filename(filename)
{
if (load_previous)
load();
}
@@ -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::instance> TimingsDatabase::get(const char *name) const
const TimingsDatabase::instance* TimingsDatabase::get_most_recent(const char *name) const
{
std::vector<instance> 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);
}
+5 -5
View File
@@ -2,8 +2,8 @@
#include <stdint.h>
#include <string>
#include <utility>
#include <vector>
#include <map>
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<instance> 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<std::string, instance> instances;
std::vector<std::pair<std::string, instance>> instances;
};
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -65,7 +65,7 @@ namespace
auto& out_key = reinterpret_cast<const crypto::public_key&>(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;
-2
View File
@@ -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
@@ -51,6 +51,6 @@ public:
crypto::public_key out_key = boost::get<cryptonote::txout_to_key>(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"));
}
};
@@ -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;
};
+20 -17
View File
@@ -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;
+80 -32
View File
@@ -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 <iostream>
#include <memory>
#include <type_traits>
#include <stdint.h>
#include <boost/chrono.hpp>
@@ -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 <typename T>
class test_runner
struct ParamsShuttle
{
Params core_params;
ParamsShuttle() = default;
ParamsShuttle(Params &params) : core_params{params}
{}
virtual ~ParamsShuttle() = default; // virtual for non-final type
};
template <typename T, typename ParamsT,
typename std::enable_if<!std::is_same<ParamsT, ParamsShuttle>::value, bool>::type = true>
bool init_test(T &test, ParamsT &params_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 <typename T, typename ParamsT,
typename std::enable_if<std::is_same<ParamsT, ParamsShuttle>::value, bool>::type = true>
bool init_test(T &test, ParamsT &params_shuttle)
{
if (!test.init())
return false;
return true;
}
template <typename T, typename ParamsT>
class test_runner final
{
public:
test_runner(const Params &params)
test_runner(const ParamsT &params_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<tools::PerformanceTimer, uint64_t>(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; ///<! This field is intended for preclude compiler optimizations
int m_elapsed;
Params m_params;
Params m_core_params;
ParamsT m_params_shuttle;
std::vector<tools::PerformanceTimer> m_per_call_timers;
std::unique_ptr<Stats<tools::PerformanceTimer, uint64_t>> m_stats;
};
template <typename T>
void run_test(const std::string &filter, Params &params, const char* test_name)
template <typename T, typename ParamsT>
bool run_test(const std::string &filter, ParamsT &params_shuttle, const char* test_name)
{
static_assert(std::is_base_of<ParamsShuttle, ParamsT>::value, "Must use a ParamsShuttle.");
Params &params = 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<T> runner(params);
if (runner.run())
test_runner<T, ParamsT> 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 &params, const char* test_name)
double stddev = runner.get_stddev();
double npskew = runner.get_non_parametric_skew();
std::vector<TimingsDatabase::instance> 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 &params, 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
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2017-2022, The Monero Project
// Copyright (c) 2017-2024, The Monero Project
//
// All rights reserved.
//
-1
View File
@@ -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
+1 -1
View File
@@ -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<crypto::key_derivation>(), 0, *hw, boost::none));
i++;
}
+1 -1
View File
@@ -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,
-5
View File
@@ -61,11 +61,6 @@ TEST(check_output_types, check_all)
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();
txout_to_scripthash tx_out_scripthash;
tx.vout.push_back(tx_out {0, tx_out_scripthash});
EXPECT_FALSE(check_output_types(tx, hf_version));
+3
View File
@@ -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
{
+160 -42
View File
@@ -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<std::string> &intermediate_infos,
std::unordered_set<crypto::secret_key> 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<std::string> &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<crypto::public_key> 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<std::string> &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<std::string> 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<tools::wallet2>& 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<tools::wallet2> wallets(4);
make_wallets_boosting(wallets, 2);
}
TEST(multisig, multisig_kex_msg)
{
using namespace multisig;
+9 -27
View File
@@ -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);
+376 -376
View File
@@ -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<int>(hf_version));
@@ -123,7 +123,7 @@ TEST(wallet_scanning, view_scan_long_payment_id)
const crypto::hash payment_id = crypto::rand<crypto::hash>();
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<int>(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<int>(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<cryptonote::block_complete_entry> block_entries;
std::vector<tools::wallet2::parsed_block> 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<cryptonote::block_complete_entry> block_entries;
// std::vector<tools::wallet2::parsed_block> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::txin_to_key>(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<cryptonote::txin_to_key>(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<std::pair<crypto::key_image, crypto::signature>> 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<std::pair<crypto::key_image, crypto::signature>> 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<cryptonote::tx_destination_entry> dests = {
cryptonote::tx_destination_entry(outgoing_amount, mock::null_addr, false)};
crypto::secret_key main_tx_privkey;
std::vector<crypto::secret_key> 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<cryptonote::txin_to_key>(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<cryptonote::txin_to_key>(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<cryptonote::tx_destination_entry> dests = {
// cryptonote::tx_destination_entry(outgoing_amount, mock::null_addr, false)};
// crypto::secret_key main_tx_privkey;
// std::vector<crypto::secret_key> 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<cryptonote::txin_to_key>(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<cryptonote::txin_to_key>(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);
// }
//----------------------------------------------------------------------------------------------------------------------
+384
View File
@@ -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 <cctype>
#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);
}
+129 -128
View File
@@ -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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<cryptonote::tx_destination_entry> 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<carrot::CarrotTransactionProposalV1> 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<rct::xmr_amount>::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<carrot::CarrotTransactionProposalV1> 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<rct::xmr_amount>::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);
// }
//----------------------------------------------------------------------------------------------------------------------