Merge branch 'decoy_selection' into cryptoassets

This commit is contained in:
cryptozoidberg 2024-02-01 17:18:52 +01:00
commit 67b7336a97
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
57 changed files with 6004 additions and 312 deletions

View file

@ -274,6 +274,21 @@ namespace misc_utils
}
}
uint64_t get_avg() const
{
CRITICAL_REGION_LOCAL(m_lock);
if (!queued_items.size())
return 0;
uint64_t summ = 0;
for (const auto& item : queued_items)
{
summ += *item.first;
}
return summ / queued_items.size();
}
template<typename key_t, typename associated_data_t>
friend std::ostream & operator<< (std::ostream &out, median_helper<key_t, associated_data_t> const &mh);
}; // class median_helper
@ -292,26 +307,25 @@ namespace misc_utils
/************************************************************************/
/* */
/************************************************************************/
template<class type_vec_type>
type_vec_type median(std::vector<type_vec_type> &v)
template<typename container_t>
typename container_t::value_type median(container_t &v)
{
//CRITICAL_REGION_LOCAL(m_lock);
typename container_t::value_type median{};
if(v.empty())
return boost::value_initialized<type_vec_type>();
return median;
if(v.size() == 1)
return v[0];
size_t n = (v.size()) / 2;
std::sort(v.begin(), v.end());
//nth_element(v.begin(), v.begin()+n-1, v.end());
if(v.size()%2)
{//1, 3, 5...
return v[n];
}else
{//2, 4, 6...
return (v[n-1] + v[n])/2;
auto median_it = v.begin() + v.size() / 2;
std::nth_element(v.begin(), median_it, v.end());
median = *median_it;
if (v.size() % 2 == 0)
{
auto max_it = std::max_element(v.begin(), median_it); // it's garanteed that after nth_element() the necessary element is in this interval
median = (median + *max_it) / 2; // average of [size/2-1] and [size/2] elements
}
return median;
}
/************************************************************************/

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2023 Zano Project
// Copyright (c) 2014-2024 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -115,27 +115,6 @@ namespace crypto
END_BOOST_SERIALIZATION()
};
struct CLSAG_GGXG_signature_serialized : public CLSAG_GGXG_signature
{
BEGIN_SERIALIZE_OBJECT()
FIELD(c)
FIELD((std::vector<scalar_t>&)(r_g))
FIELD((std::vector<scalar_t>&)(r_x))
FIELD(K1)
FIELD(K2)
FIELD(K3)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(c)
BOOST_SERIALIZE((std::vector<scalar_t>&)(r_g))
BOOST_SERIALIZE((std::vector<scalar_t>&)(r_x))
BOOST_SERIALIZE(K1)
BOOST_SERIALIZE(K2)
BOOST_SERIALIZE(K3)
END_BOOST_SERIALIZATION()
};
struct CLSAG_GGXXG_signature_serialized : public CLSAG_GGXXG_signature
{
BEGIN_SERIALIZE_OBJECT()

View file

@ -24,6 +24,7 @@
#define API_RETURN_CODE_WALLET_WRONG_ID "WALLET_WRONG_ID"
#define API_RETURN_CODE_WALLET_WATCH_ONLY_NOT_SUPPORTED "WALLET_WATCH_ONLY_NOT_SUPPORTED"
#define API_RETURN_CODE_WALLET_AUDITABLE_NOT_SUPPORTED "WALLET_AUDITABLE_NOT_SUPPORTED"
#define API_RETURN_CODE_WALLET_FEE_TOO_LOW "API_RETURN_CODE_WALLET_FEE_TOO_LOW"
#define API_RETURN_CODE_FILE_NOT_FOUND "FILE_NOT_FOUND"
#define API_RETURN_CODE_ALREADY_EXISTS "ALREADY_EXISTS"
#define API_RETURN_CODE_CANCELED "CANCELED"
@ -42,4 +43,4 @@
#define API_RETURN_CODE_TX_REJECTED "TX_REJECTED"
#define API_RETURN_CODE_HTLC_ORIGIN_HASH_MISSMATCHED "HTLC_ORIGIN_HASH_MISSMATCHED"
#define API_RETURN_CODE_WRAP "WRAP"
#define API_RETURN_CODE_MISSING_ZC_INPUTS "MISSING_ZC_INPUTS"
#define API_RETURN_CODE_MISSING_ZC_INPUTS "MISSING_ZC_INPUTS"

View file

@ -1,9 +1,10 @@
// Copyright (c) 2022-2023 Zano Project
// Copyright (c) 2022-2023 sowle (val@zano.org, crypto.sowle@gmail.com)
// Copyright (c) 2022-2024 Zano Project
// Copyright (c) 2022-2024 sowle (val@zano.org, crypto.sowle@gmail.com)
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// This file contains implementation of CLSAG (s.a. https://eprint.iacr.org/2019/654.pdf by Goodel at el)
// This file contains implementation of the original d-CLSAG (s.a. https://eprint.iacr.org/2019/654.pdf by Goodel at el)
// and the extended d/v-CLSAG version (s.a. https://github.com/hyle-team/docs/blob/master/zano/dv-CLSAG-extension/ by sowle)
//
#include "clsag.h"
//#include "misc_log_ex.h"
@ -51,6 +52,7 @@ namespace crypto
}
hsc.add_point(c_scalar_1div8 * pseudo_out_amount_commitment);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hash input_hash = hsc.calc_hash_no_reduce();
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_LAYER_0);
@ -87,10 +89,7 @@ namespace crypto
hsc.add_point(alpha * ki_base);
scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1}
sig.r.clear();
sig.r.reserve(ring_size);
for(size_t i = 0; i < ring_size; ++i)
sig.r.emplace_back(scalar_t::random());
sig.r.resize_and_make_random(ring_size);
for(size_t j = 0, i = (secret_index + 1) % ring_size; j < ring_size - 1; ++j, i = (i + 1) % ring_size)
{
@ -137,6 +136,7 @@ namespace crypto
}
hsc.add_pub_key(pseudo_out_amount_commitment);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hash input_hash = hsc.calc_hash_no_reduce();
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GG_LAYER_0);
@ -203,8 +203,6 @@ namespace crypto
CRYPTO_CHECK_AND_THROW_MES((secret_0_xp * c_point_G).to_public_key() == ring[secret_index].stealth_address, "secret_0_xp mismatch");
CRYPTO_CHECK_AND_THROW_MES( secret_1_f * c_point_G == 8 * point_t(ring[secret_index].amount_commitment) - pseudo_out_amount_commitment, "secret_1_f mismatch");
CRYPTO_CHECK_AND_THROW_MES( secret_2_t * c_point_X == 8 * point_t(ring[secret_index].blinded_asset_id) - pseudo_out_blinded_asset_id, "secret_2_t mismatch");
//CRYPTO_CHECK_AND_THROW_MES( secret_3_q * c_point_G == 8 * point_t(ring[secret_index].concealing_point), "");
//CRYPTO_CHECK_AND_THROW_MES( secret_4_x * c_point_X == extended_amount_commitment - 8 * point_t(ring[secret_index].amount_commitment) - 8 * point_t(ring[secret_index].concealing_point), "");
#endif
point_t K1_div8 = (c_scalar_1div8 * secret_1_f) * ki_base;
@ -217,16 +215,6 @@ namespace crypto
point_t K2 = K2_div8;
K2.modify_mul8();
//point_t K3_div8 = (c_scalar_1div8 * secret_3_q) * ki_base;
//K3_div8.to_public_key(sig.K3);
//point_t K3 = K3_div8;
//K3.modify_mul8();
//point_t K4_div8 = (c_scalar_1div8 * secret_4_x) * ki_base;
//K4_div8.to_public_key(sig.K4);
//point_t K4 = K4_div8;
//K4.modify_mul8();
// calculate aggregation coefficients
hash_helper_t::hs_t hsc(4 + 3 * ring_size);
hsc.add_scalar(m);
@ -240,6 +228,8 @@ namespace crypto
hsc.add_point(c_scalar_1div8 * pseudo_out_amount_commitment);
hsc.add_point(c_scalar_1div8 * pseudo_out_blinded_asset_id);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hsc.add_pub_key(sig.K2);
hash input_hash = hsc.calc_hash_no_reduce();
DBG_VAL_PRINT(input_hash);
@ -259,11 +249,6 @@ namespace crypto
scalar_t agg_coeff_2 = hsc.calc_hash();
DBG_VAL_PRINT(agg_coeff_2);
//hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_3);
//hsc.add_hash(input_hash);
//scalar_t agg_coeff_3 = hsc.calc_hash();
//DBG_VAL_PRINT(agg_coeff_3);
// prepare A_i, Q_i
std::vector<point_t> A_i, Q_i;
A_i.reserve(ring_size), Q_i.reserve(ring_size);
@ -300,18 +285,17 @@ namespace crypto
}
// aggregate secret key (layers 0, 1; G component)
scalar_t w_sec_key_g = agg_coeff_0 * secret_0_xp + agg_coeff_1 * secret_1_f; // + agg_coeff_3 * secret_3_q;
scalar_t w_sec_key_g = agg_coeff_0 * secret_0_xp + agg_coeff_1 * secret_1_f;
DBG_VAL_PRINT(w_sec_key_g * c_point_G);
// aggregate secret key (layer 2; X component)
scalar_t w_sec_key_x = agg_coeff_2 * secret_2_t;
DBG_VAL_PRINT(w_sec_key_x * c_point_X);
// calculate aggregate key image (layers 0, 1, 3; G component)
point_t W_key_image_g = agg_coeff_0 * key_image + agg_coeff_1 * K1 /* + agg_coeff_3 * K3 */;
// calculate aggregate key image (layers 0, 1; G component)
point_t W_key_image_g = agg_coeff_0 * key_image + agg_coeff_1 * K1;
DBG_VAL_PRINT(key_image);
DBG_VAL_PRINT(K1);
//DBG_VAL_PRINT(K3);
DBG_VAL_PRINT(W_key_image_g);
// calculate aggregate key image (layer 2; X component)
@ -338,15 +322,8 @@ namespace crypto
//DBG_PRINT("c[" << secret_index << "] = Hs(ih, " << alpha_g * c_point_G << ", " << alpha_g * ki_base << ", " << alpha_x * c_point_X << ", " << alpha_x * ki_base << ")");
scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1}
sig.r_g.clear();
sig.r_x.clear();
sig.r_g.reserve(ring_size);
sig.r_x.reserve(ring_size);
for(size_t i = 0; i < ring_size; ++i)
{
sig.r_g.emplace_back(scalar_t::random());
sig.r_x.emplace_back(scalar_t::random());
}
sig.r_g.resize_and_make_random(ring_size);
sig.r_x.resize_and_make_random(ring_size);
for(size_t j = 0, i = (secret_index + 1) % ring_size; j < ring_size - 1; ++j, i = (i + 1) % ring_size)
{
@ -403,6 +380,8 @@ namespace crypto
hsc.add_pub_key(pseudo_out_amount_commitment);
hsc.add_pub_key(pseudo_out_blinded_asset_id);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hsc.add_pub_key(sig.K2);
hash input_hash = hsc.calc_hash_no_reduce();
DBG_VAL_PRINT(input_hash);
@ -422,11 +401,6 @@ namespace crypto
scalar_t agg_coeff_2 = hsc.calc_hash();
DBG_VAL_PRINT(agg_coeff_2);
//hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGX_LAYER_3);
//hsc.add_hash(input_hash);
//scalar_t agg_coeff_3 = hsc.calc_hash();
//DBG_VAL_PRINT(agg_coeff_3);
// prepare A_i, Q_i
std::vector<point_t> A_i, Q_i;
A_i.reserve(ring_size), Q_i.reserve(ring_size);
@ -462,13 +436,11 @@ namespace crypto
DBG_VAL_PRINT(W_pub_keys_x[i]);
}
// calculate aggregate key image (layers 0, 1, 3; G components)
// calculate aggregate key image (layers 0, 1; G components)
point_t W_key_image_g =
agg_coeff_0 * key_image +
agg_coeff_1 * point_t(sig.K1).modify_mul8();
// agg_coeff_3 * point_t(sig.K3).modify_mul8();
DBG_VAL_PRINT(point_t(sig.K1).modify_mul8());
//DBG_VAL_PRINT(point_t(sig.K3).modify_mul8());
DBG_VAL_PRINT(W_key_image_g);
// calculate aggregate key image (layer 2; X component)
@ -498,7 +470,7 @@ namespace crypto
//---------------------------------------------------------------
/*
bool generate_CLSAG_GGXG(const hash& m, const std::vector<CLSAG_GGXG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment, const point_t& extended_amount_commitment, const key_image& ki,
const scalar_t& secret_0_xp, const scalar_t& secret_1_f, const scalar_t& secret_2_x, const scalar_t& secret_3_q, uint64_t secret_index, CLSAG_GGXG_signature& sig)
@ -548,6 +520,9 @@ namespace crypto
hsc.add_point(c_scalar_1div8 * pseudo_out_amount_commitment);
hsc.add_point(c_scalar_1div8 * extended_amount_commitment);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hsc.add_pub_key(sig.K2);
hsc.add_pub_key(sig.K3);
hash input_hash = hsc.calc_hash_no_reduce();
DBG_VAL_PRINT(input_hash);
@ -617,7 +592,7 @@ namespace crypto
DBG_VAL_PRINT(w_sec_key_x * c_point_X);
// calculate aggregate key image (layers 0, 1, 3; G component)
point_t W_key_image_g = agg_coeff_0 * key_image + agg_coeff_1 * K1 + /*agg_coeff_2 * K2 +*/ agg_coeff_3 * K3;
point_t W_key_image_g = agg_coeff_0 * key_image + agg_coeff_1 * K1 + agg_coeff_3 * K3;
DBG_VAL_PRINT(key_image);
DBG_VAL_PRINT(K1);
DBG_VAL_PRINT(K3);
@ -647,15 +622,8 @@ namespace crypto
//DBG_PRINT("c[" << secret_index << "] = Hs(ih, " << alpha_g * c_point_G << ", " << alpha_g * ki_base << ", " << alpha_x * c_point_X << ", " << alpha_x * ki_base << ")");
scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1}
sig.r_g.clear();
sig.r_x.clear();
sig.r_g.reserve(ring_size);
sig.r_x.reserve(ring_size);
for(size_t i = 0; i < ring_size; ++i)
{
sig.r_g.emplace_back(scalar_t::random());
sig.r_x.emplace_back(scalar_t::random());
}
sig.r_g.resize_and_make_random(ring_size);
sig.r_x.resize_and_make_random(ring_size);
for(size_t j = 0, i = (secret_index + 1) % ring_size; j < ring_size - 1; ++j, i = (i + 1) % ring_size)
{
@ -713,6 +681,9 @@ namespace crypto
hsc.add_pub_key(pseudo_out_amount_commitment);
hsc.add_pub_key(extended_amount_commitment);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hsc.add_pub_key(sig.K2);
hsc.add_pub_key(sig.K3);
hash input_hash = hsc.calc_hash_no_reduce();
DBG_VAL_PRINT(input_hash);
@ -807,7 +778,7 @@ namespace crypto
return c_prev == sig.c;
}
*/
//---------------------------------------------------------------
@ -868,6 +839,10 @@ namespace crypto
hsc.add_point(c_scalar_1div8 * pseudo_out_blinded_asset_id);
hsc.add_point(c_scalar_1div8 * extended_amount_commitment);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hsc.add_pub_key(sig.K2);
hsc.add_pub_key(sig.K3);
hsc.add_pub_key(sig.K4);
hash input_hash = hsc.calc_hash_no_reduce();
DBG_VAL_PRINT(input_hash);
@ -976,15 +951,8 @@ namespace crypto
//DBG_PRINT("c[" << secret_index << "] = Hs(ih, " << alpha_g * c_point_G << ", " << alpha_g * ki_base << ", " << alpha_x * c_point_X << ", " << alpha_x * ki_base << ")");
scalar_t c_prev = hsc.calc_hash(); // c_{secret_index + 1}
sig.r_g.clear();
sig.r_x.clear();
sig.r_g.reserve(ring_size);
sig.r_x.reserve(ring_size);
for(size_t i = 0; i < ring_size; ++i)
{
sig.r_g.emplace_back(scalar_t::random());
sig.r_x.emplace_back(scalar_t::random());
}
sig.r_g.resize_and_make_random(ring_size);
sig.r_x.resize_and_make_random(ring_size);
for(size_t j = 0, i = (secret_index + 1) % ring_size; j < ring_size - 1; ++j, i = (i + 1) % ring_size)
{
@ -1048,6 +1016,10 @@ namespace crypto
hsc.add_pub_key(pseudo_out_blinded_asset_id);
hsc.add_pub_key(extended_amount_commitment);
hsc.add_key_image(ki);
hsc.add_pub_key(sig.K1);
hsc.add_pub_key(sig.K2);
hsc.add_pub_key(sig.K3);
hsc.add_pub_key(sig.K4);
hash input_hash = hsc.calc_hash_no_reduce();
DBG_VAL_PRINT(input_hash);

View file

@ -1,9 +1,10 @@
// Copyright (c) 2022-2023 Zano Project
// Copyright (c) 2022-2023 sowle (val@zano.org, crypto.sowle@gmail.com)
// Copyright (c) 2022-2024 Zano Project
// Copyright (c) 2022-2024 sowle (val@zano.org, crypto.sowle@gmail.com)
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
// This file contains implementation of CLSAG (s.a. https://eprint.iacr.org/2019/654.pdf by Goodel at el)
// This file contains implementation of the original d-CLSAG (s.a. https://eprint.iacr.org/2019/654.pdf by Goodel at el)
// and the extended d/v-CLSAG version (s.a. https://github.com/hyle-team/docs/blob/master/zano/dv-CLSAG-extension/ by sowle)
//
#pragma once
#include "crypto-sugar.h"
@ -57,11 +58,11 @@ namespace crypto
//
// 3-CLSAG
// 3/2-CLSAG
//
// 3-CLSAG signature (with respect to the group element G, G, X -- that's why 'GGX')
// 3/2-CLSAG signature (with respect to the group element G, G, X -- that's why 'GGX')
struct CLSAG_GGX_signature
{
scalar_t c;
@ -93,12 +94,13 @@ namespace crypto
const public_key& pseudo_out_asset_id, const key_image& ki, const CLSAG_GGX_signature& sig);
/*
//
// 4-CLSAG
// 4/2-CLSAG (eventually, it's not used in Zano)
//
// 4-CLSAG signature (with respect to the group element G, G, X, G -- that's why 'GGXG')
// 4/2-CLSAG signature (with respect to the group element G, G, X, G -- that's why 'GGXG')
struct CLSAG_GGXG_signature
{
scalar_t c;
@ -130,14 +132,14 @@ namespace crypto
bool verify_CLSAG_GGXG(const hash& m, const std::vector<CLSAG_GGXG_input_ref_t>& ring, const public_key& pseudo_out_amount_commitment,
const public_key& extended_amount_commitment, const key_image& ki, const CLSAG_GGXG_signature& sig);
*/
//
// 5-CLSAG
// 5/2-CLSAG
//
// 5-CLSAG signature (with respect to the group element G, G, X, X, G -- that's why 'GGXXG')
// 5/2-CLSAG signature (with respect to the group element G, G, X, X, G -- that's why 'GGXXG')
struct CLSAG_GGXXG_signature
{
scalar_t c;

View file

@ -40,7 +40,6 @@ DISABLE_VS_WARNINGS(4146 4244)
void fe_mul(fe, const fe, const fe);
void fe_sq(fe, const fe);
void fe_tobytes(unsigned char *, const fe);
static void ge_madd(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_msub(ge_p1p1 *, const ge_p3 *, const ge_precomp *);
static void ge_p2_0(ge_p2 *);
static void ge_p3_dbl(ge_p1p1 *, const ge_p3 *);
@ -119,7 +118,7 @@ Postconditions:
|h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/
static void fe_add(fe h, const fe f, const fe g) {
void fe_add(fe h, const fe f, const fe g) {
int32_t f0 = f[0];
int32_t f1 = f[1];
int32_t f2 = f[2];
@ -1425,7 +1424,7 @@ int ge_frombytes_vartime(ge_p3 *h, const unsigned char *s) {
r = p + q
*/
static void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q) {
fe t0;
fe_add(r->X, p->Y, p->X);
fe_sub(r->Y, p->Y, p->X);
@ -4314,3 +4313,171 @@ void ge_cached_to_p2(ge_p2 *r, const ge_cached *c)
fe_copy(r->Z, c->Z);
}
///////////////////////////
// EXPERIMENTAL
//
// With these select_vartime/ge_scalarmult_base_vartime I got ~25% speed up comparing to the select/ge_scalarmult_base -- sowle
static void select_vartime(ge_precomp *t, int pos, signed char b)
{
unsigned char bnegative = negative(b);
unsigned char babs = b - (((-bnegative) & b) << 1);
const ge_precomp* base;
if (babs == 0)
{
ge_precomp_0(t);
}
else if (bnegative == 0)
{
base = &ge_base[pos][babs - 1];
fe_copy(t->yplusx, base->yplusx);
fe_copy(t->yminusx, base->yminusx);
fe_copy(t->xy2d, base->xy2d);
}
else
{
base = &ge_base[pos][babs - 1];
fe_copy(t->yplusx, base->yminusx);
fe_copy(t->yminusx, base->yplusx);
fe_neg(t->xy2d, base->xy2d);
}
}
void ge_scalarmult_base_vartime(ge_p3 *h, const unsigned char *a)
{
signed char e[64];
signed char carry;
ge_p1p1 r;
ge_p2 s;
ge_precomp t;
int i;
for (i = 0; i < 32; ++i) {
e[2 * i + 0] = (a[i] >> 0) & 15;
e[2 * i + 1] = (a[i] >> 4) & 15;
}
/* each e[i] is between 0 and 15 */
/* e[63] is between 0 and 7 */
carry = 0;
for (i = 0; i < 63; ++i) {
e[i] += carry;
carry = e[i] + 8;
carry >>= 4;
e[i] -= carry << 4;
}
e[63] += carry;
/* each e[i] is between -8 and 8 */
ge_p3_0(h);
for (i = 1; i < 64; i += 2) {
select_vartime(&t, i / 2, e[i]);
ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r);
}
ge_p3_dbl(&r, h); ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r);
for (i = 0; i < 64; i += 2) {
select_vartime(&t, i / 2, e[i]);
ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r);
}
}
static void select_custom_precomp_vartime(ge_precomp *t, const precomp_data_t base_precomp, int pos, signed char b)
{
unsigned char bnegative = negative(b);
unsigned char babs = b - (((-bnegative) & b) << 1);
const ge_precomp* base;
if (babs == 0)
{
ge_precomp_0(t);
}
else if (bnegative == 0)
{
base = &base_precomp[pos][babs - 1];
fe_copy(t->yplusx, base->yplusx);
fe_copy(t->yminusx, base->yminusx);
fe_copy(t->xy2d, base->xy2d);
}
else
{
base = &base_precomp[pos][babs - 1];
fe_copy(t->yplusx, base->yminusx);
fe_copy(t->yminusx, base->yplusx);
fe_neg(t->xy2d, base->xy2d);
}
}
void ge_scalarmult_precomp_vartime(ge_p3 *h, const precomp_data_t base_precomp, const unsigned char *a)
{
signed char e[64];
signed char carry;
ge_p1p1 r;
ge_p2 s;
ge_precomp t;
int i;
for (i = 0; i < 32; ++i) {
e[2 * i + 0] = (a[i] >> 0) & 15;
e[2 * i + 1] = (a[i] >> 4) & 15;
}
/* each e[i] is between 0 and 15 */
/* e[63] is between 0 and 7 */
carry = 0;
for (i = 0; i < 63; ++i) {
e[i] += carry;
carry = e[i] + 8;
carry >>= 4;
e[i] -= carry << 4;
}
e[63] += carry;
/* each e[i] is between -8 and 8 */
ge_p3_0(h);
for (i = 1; i < 64; i += 2) {
select_custom_precomp_vartime(&t, base_precomp, i / 2, e[i]);
ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r);
}
ge_p3_dbl(&r, h); ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r);
ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r);
for (i = 0; i < 64; i += 2) {
select_custom_precomp_vartime(&t, base_precomp, i / 2, e[i]);
ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r);
}
}
void ge_p3_to_precomp(ge_precomp *r, const ge_p3* p)
{
fe recip;
fe x;
fe y;
//unsigned char s[32];
fe_invert(recip, p->Z);
fe_mul(x, p->X, recip);
fe_mul(y, p->Y, recip);
fe_sub(r->yminusx, y, x);
fe_add(r->yplusx, y, x);
fe_mul(r->xy2d, x, y);
fe_mul(r->xy2d, r->xy2d, fe_d2);
// to get canonical representation and obtain the very same beautiful numbers for ge_base in crypto-ops-data.c (maybe unnecessary, TODO -- sowle)
//fe_tobytes(s, r->yminusx); fe_frombytes(r->yminusx, s);
//fe_tobytes(s, r->yplusx); fe_frombytes(r->yplusx, s);
//fe_tobytes(s, r->xy2d); fe_frombytes(r->xy2d, s);
}

View file

@ -60,6 +60,7 @@ void ge_double_scalarmult_base_vartime(ge_p2 *, const unsigned char *, const ge_
extern const fe fe_sqrtm1;
extern const fe fe_d;
extern const fe fe_d2;
int ge_frombytes_vartime(ge_p3 *, const unsigned char *);
/* From ge_p1p1_to_p2.c */
@ -112,12 +113,19 @@ void ge_p2_to_p3(ge_p3 *r, const ge_p2 *t);
void ge_bytes_hash_to_ec(ge_p3 *, const void *, size_t);
void ge_bytes_hash_to_ec_32(ge_p3 *, const unsigned char *);
void ge_cached_to_p2(ge_p2 *r, const ge_cached *c);
void ge_madd(ge_p1p1 *r, const ge_p3 *p, const ge_precomp *q);
void ge_p3_0(ge_p3 *h);
void ge_sub(ge_p1p1 *, const ge_p3 *, const ge_cached *);
void ge_double_scalarmult_base_vartime_p3(ge_p3 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b);
void ge_scalarmult_vartime_p3(ge_p3 *r, const unsigned char *a, const ge_p3 *A);
void ge_scalarmult_vartime_p3_v2(ge_p3 *r, const unsigned char *a, const ge_p3 *A);
void ge_scalarmult_base_vartime(ge_p3 *h, const unsigned char *a);
/* precomp_data[i][j] = (j + 1) * 256^i * G */
typedef ge_precomp (precomp_data_t)[32][8];
void ge_scalarmult_precomp_vartime(ge_p3 *h, const precomp_data_t base_precomp, const unsigned char *a);
void ge_p3_to_precomp(ge_precomp *r, const ge_p3* p);
extern const fe fe_ma2;
extern const fe fe_ma;
@ -139,6 +147,7 @@ void sc_invert(unsigned char*, const unsigned char*);
void fe_sq(fe h, const fe f);
int fe_isnonzero(const fe f);
void fe_add(fe h, const fe f, const fe g);
void fe_sub(fe h, const fe f, const fe g);
void fe_mul(fe, const fe, const fe);
void fe_frombytes(fe h, const unsigned char *s);

File diff suppressed because it is too large Load diff

View file

@ -729,14 +729,15 @@ namespace crypto
friend point_t operator*(const scalar_t& lhs, const point_t& rhs)
{
point_t result;
ge_scalarmult_p3(&result.m_p3, lhs.m_s, &rhs.m_p3);
//ge_scalarmult_p3(&result.m_p3, lhs.m_s, &rhs.m_p3);
ge_scalarmult_vartime_p3(&result.m_p3, lhs.m_s, &rhs.m_p3);
return result;
}
point_t& operator*=(const scalar_t& rhs)
{
// TODO: ge_scalarmult_vartime_p3
ge_scalarmult_p3(&m_p3, rhs.m_s, &m_p3);
//ge_scalarmult_p3(&m_p3, rhs.m_s, &m_p3);
ge_scalarmult_vartime_p3(&m_p3, rhs.m_s, &m_p3);
return *this;
}
@ -745,7 +746,8 @@ namespace crypto
point_t result;
scalar_t reciprocal;
sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]);
ge_scalarmult_p3(&result.m_p3, &reciprocal.m_s[0], &lhs.m_p3);
//ge_scalarmult_p3(&result.m_p3, &reciprocal.m_s[0], &lhs.m_p3);
ge_scalarmult_vartime_p3(&result.m_p3, &reciprocal.m_s[0], &lhs.m_p3);
return result;
}
@ -907,7 +909,8 @@ namespace crypto
friend point_t operator*(const scalar_t& lhs, const point_g_t&)
{
point_t result;
ge_scalarmult_base(&result.m_p3, &lhs.m_s[0]);
//ge_scalarmult_base(&result.m_p3, &lhs.m_s[0]);
ge_scalarmult_base_vartime(&result.m_p3, &lhs.m_s[0]);
return result;
}
@ -916,7 +919,8 @@ namespace crypto
point_t result;
scalar_t reciprocal;
sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]);
ge_scalarmult_base(&result.m_p3, &reciprocal.m_s[0]);
//ge_scalarmult_base(&result.m_p3, &reciprocal.m_s[0]);
ge_scalarmult_base_vartime(&result.m_p3, &reciprocal.m_s[0]);
return result;
}
@ -925,6 +929,42 @@ namespace crypto
}; // struct point_g_t
void construct_precomp_data(precomp_data_t precomp_data, const point_t& point);
//
// point_pc_t -- point with 30kB of precomputed data, which make possible to do very fast single scalar multiplication
//
struct point_pc_t : public point_t
{
constexpr point_pc_t(const int32_t(&v)[40], const precomp_data_t* precomp_data_p)
: point_t(v)
, m_precomp_data_p(precomp_data_p)
{
//construct_precomp_data(m_precomp_data, *this);
}
friend point_t operator*(const scalar_t& lhs, const point_pc_t& self)
{
point_t result;
ge_scalarmult_precomp_vartime(&result.m_p3, *self.m_precomp_data_p, &lhs.m_s[0]);
return result;
}
friend point_t operator/(const point_pc_t& self, const scalar_t& rhs)
{
point_t result;
scalar_t reciprocal;
sc_invert(&reciprocal.m_s[0], &rhs.m_s[0]);
ge_scalarmult_precomp_vartime(&result.m_p3, *self.m_precomp_data_p, &reciprocal.m_s[0]);
return result;
}
static_assert(sizeof(crypto::public_key) == 32, "size error");
const precomp_data_t* m_precomp_data_p;
}; // struct point_pc_t
//
// vector of scalars
//
@ -1082,18 +1122,30 @@ namespace crypto
//
// Global constants (checked in crypto_constants test)
// Global constants (checked in crypto_constants and crypto_generators_precomp tests)
//
static constexpr point_t c_point_0 {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
static constexpr point_g_t c_point_G {{ 25485296, 5318399, 8791791, -8299916, -14349720, 6939349, -3324311, -7717049, 7287234, -6577708, -758052, -1832720, 13046421, -4857925, 6576754, 14371947, -13139572, 6845540, -2198883, -4003719, -947565, 6097708, -469190, 10704810, -8556274, -15589498, -16424464, -16608899, 14028613, -5004649, 6966464, -2456167, 7033433, 6781840, 28785542, 12262365, -2659449, 13959020, -21013759, -5262166 }};
static constexpr point_t c_point_H {{ 20574939, 16670001, -29137604, 14614582, 24883426, 3503293, 2667523, 420631, 2267646, -4769165, -11764015, -12206428, -14187565, -2328122, -16242653, -788308, -12595746, -8251557, -10110987, 853396, -4982135, 6035602, -21214320, 16156349, 977218, 2807645, 31002271, 5694305, -16054128, 5644146, -15047429, -568775, -22568195, -8089957, -27721961, -10101877, -29459620, -13359100, -31515170, -6994674 }};
static constexpr point_t c_point_H2 {{ 1318371, 14804112, 12545972, -13482561, -12089798, -16020744, -21221907, -8410994, -33080606, 11275578, 3807637, 11185450, -23227561, -12892068, 1356866, -1025012, -8022738, -8139671, -20315029, -13916324, -6475650, -7025596, 12403179, -5139984, -12068178, 10445584, -14826705, -4927780, 13964546, 12525942, -2314107, -10566315, 32243863, 15603849, 5154154, 4276633, -20918372, -15718796, -26386151, 8434696 }};
static constexpr point_t c_point_U {{ 30807552, 984924, 23426137, -5598760, 7545909, 16325843, 993742, 2594106, -31962071, -959867, 16454190, -4091093, 1197656, 13586872, -9269020, -14133290, 1869274, 13360979, -24627258, -10663086, 2212027, 1198856, 20515811, 15870563, -23833732, 9839517, -19416306, 11567295, -4212053, 348531, -2671541, 484270, -19128078, 1236698, -16002690, 9321345, 9776066, 10711838, 11187722, -16371275 }};
static constexpr point_t c_point_X {{ 25635916, -5459446, 5768861, 5666160, -6357364, -12939311, 29490001, -4543704, -31266450, -2582476, 23705213, 9562626, -716512, 16560168, 7947407, 2039790, -2752711, 4742449, 3356761, 16338966, 17303421, -5790717, -5684800, 12062431, -3307947, 8139265, -26544839, 12058874, 3452748, 3359034, 26514848, -6060876, 31255039, 11154418, -21741975, -3782423, -19871841, 5729859, 21754676, -12454027 }};
static constexpr point_t c_point_H_plus_G {{ 12291435, 3330843, -3390294, 13894858, -1099584, -6848191, 12040668, -15950068, -7494633, 12566672, -5526901, -16645799, -31081168, -1095427, -13082463, 4573480, -11255691, 4344628, 33477173, 11137213, -3837023, -12436594, -8471924, -814016, 10785607, 9492721, 10992667, 7406385, -5687296, -127915, -6229107, -9324867, 558657, 6493750, 4895261, 12642545, 9549220, 696086, 21894285, -10521807 }};
static constexpr point_t c_point_H_minus_G {{ -28347682, 3523701, -3380175, -14453727, 4238027, -6032522, 20235758, 4091609, 12557126, -8064113, 4212476, -13419094, -114185, -7650727, -24238, 16663404, 23676363, -6819610, 18286466, 8714527, -3837023, -12436594, -8471924, -814016, 10785607, 9492721, 10992667, 7406385, -5687296, -127915, -20450317, 13815641, -11604061, -447489, 27380225, 9400847, -8551293, -1173627, -28110171, 14241295 }};
namespace xdetails
{
extern const precomp_data_t c_point_H_precomp_data;
extern const precomp_data_t c_point_H2_precomp_data;
extern const precomp_data_t c_point_U_precomp_data;
extern const precomp_data_t c_point_X_precomp_data;
extern const precomp_data_t c_point_H_plus_G_precomp_data;
extern const precomp_data_t c_point_H_minus_G_precomp_data;
};
inline constexpr point_t c_point_0 {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
inline constexpr point_g_t c_point_G {{ 25485296, 5318399, 8791791, -8299916, -14349720, 6939349, -3324311, -7717049, 7287234, -6577708, -758052, -1832720, 13046421, -4857925, 6576754, 14371947, -13139572, 6845540, -2198883, -4003719, -947565, 6097708, -469190, 10704810, -8556274, -15589498, -16424464, -16608899, 14028613, -5004649, 6966464, -2456167, 7033433, 6781840, 28785542, 12262365, -2659449, 13959020, -21013759, -5262166 }};
inline constexpr point_pc_t c_point_H {{ 20574939, 16670001, -29137604, 14614582, 24883426, 3503293, 2667523, 420631, 2267646, -4769165, -11764015, -12206428, -14187565, -2328122, -16242653, -788308, -12595746, -8251557, -10110987, 853396, -4982135, 6035602, -21214320, 16156349, 977218, 2807645, 31002271, 5694305, -16054128, 5644146, -15047429, -568775, -22568195, -8089957, -27721961, -10101877, -29459620, -13359100, -31515170, -6994674 }, &xdetails::c_point_H_precomp_data };
inline constexpr point_pc_t c_point_H2 {{ 1318371, 14804112, 12545972, -13482561, -12089798, -16020744, -21221907, -8410994, -33080606, 11275578, 3807637, 11185450, -23227561, -12892068, 1356866, -1025012, -8022738, -8139671, -20315029, -13916324, -6475650, -7025596, 12403179, -5139984, -12068178, 10445584, -14826705, -4927780, 13964546, 12525942, -2314107, -10566315, 32243863, 15603849, 5154154, 4276633, -20918372, -15718796, -26386151, 8434696 }, &xdetails::c_point_H2_precomp_data };
inline constexpr point_pc_t c_point_U {{ 30807552, 984924, 23426137, -5598760, 7545909, 16325843, 993742, 2594106, -31962071, -959867, 16454190, -4091093, 1197656, 13586872, -9269020, -14133290, 1869274, 13360979, -24627258, -10663086, 2212027, 1198856, 20515811, 15870563, -23833732, 9839517, -19416306, 11567295, -4212053, 348531, -2671541, 484270, -19128078, 1236698, -16002690, 9321345, 9776066, 10711838, 11187722, -16371275 }, &xdetails::c_point_U_precomp_data };
inline constexpr point_pc_t c_point_X {{ 25635916, -5459446, 5768861, 5666160, -6357364, -12939311, 29490001, -4543704, -31266450, -2582476, 23705213, 9562626, -716512, 16560168, 7947407, 2039790, -2752711, 4742449, 3356761, 16338966, 17303421, -5790717, -5684800, 12062431, -3307947, 8139265, -26544839, 12058874, 3452748, 3359034, 26514848, -6060876, 31255039, 11154418, -21741975, -3782423, -19871841, 5729859, 21754676, -12454027 }, &xdetails::c_point_X_precomp_data };
inline constexpr point_pc_t c_point_H_plus_G {{ 12291435, 3330843, -3390294, 13894858, -1099584, -6848191, 12040668, -15950068, -7494633, 12566672, -5526901, -16645799, -31081168, -1095427, -13082463, 4573480, -11255691, 4344628, 33477173, 11137213, -3837023, -12436594, -8471924, -814016, 10785607, 9492721, 10992667, 7406385, -5687296, -127915, -6229107, -9324867, 558657, 6493750, 4895261, 12642545, 9549220, 696086, 21894285, -10521807 }, &xdetails::c_point_H_plus_G_precomp_data };
inline constexpr point_pc_t c_point_H_minus_G {{ -28347682, 3523701, -3380175, -14453727, 4238027, -6032522, 20235758, 4091609, 12557126, -8064113, 4212476, -13419094, -114185, -7650727, -24238, 16663404, 23676363, -6819610, 18286466, 8714527, -3837023, -12436594, -8471924, -814016, 10785607, 9492721, 10992667, 7406385, -5687296, -127915, -20450317, 13815641, -11604061, -447489, 27380225, 9400847, -8551293, -1173627, -28110171, 14241295 }, &xdetails::c_point_H_minus_G_precomp_data };
//
// hash functions' helper
//

View file

@ -205,7 +205,7 @@ namespace crypto
TRY_ENTRY()
{
DBG_PRINT("zarcanum_verify_proof");
bool r = false;
//bool r = false;
//std::cout << "===== zarcanum_verify_proof =====" << ENDL
// << "m: " << m << ENDL

View file

@ -2043,9 +2043,18 @@ bool blockchain_storage::is_reorganize_required(const block_extended_info& main_
wide_difficulty_type main_pow_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), connection_point.height - 1, false);
main_cumul_diff.pow_diff = main_pow_diff_end - main_pow_diff_begin;
//TODO: measurement of precise cumulative difficult
boost::multiprecision::uint1024_t alt = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, alt_cumul_diff, main_cumul_diff);
boost::multiprecision::uint1024_t main = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, main_cumul_diff, alt_cumul_diff);
boost::multiprecision::uint1024_t alt = 0;
boost::multiprecision::uint1024_t main = 0;
if (m_core_runtime_config.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, alt_chain_bei.height))
{
alt = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, alt_cumul_diff, main_cumul_diff);
main = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, main_cumul_diff, alt_cumul_diff);
}
else
{
alt = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, alt_cumul_diff, main_cumul_diff);
main = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, main_cumul_diff, alt_cumul_diff);
}
LOG_PRINT_L1("[FORK_CHOICE]: " << ENDL
<< "difficulty_pow_at_split_point:" << difficulty_pow_at_split_point << ENDL
<< "difficulty_pos_at_split_point:" << difficulty_pos_at_split_point << ENDL
@ -2062,6 +2071,21 @@ bool blockchain_storage::is_reorganize_required(const block_extended_info& main_
return false;
else
{
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM))
{
// prefer blocks with more summary fee(to motivate stakers include transactions)
// since we don't have "summary block fee" field yet, we can use this_block_tx_fee_median multiplied to transactions
// count as an indirect measure of sumarry paid fee. If this approach won't be doing it's job it's subject
// to reconsider and introducing additional field in block_extended_info structure
if (alt_chain_bei.this_block_tx_fee_median * alt_chain_bei.bl.tx_hashes.size() >
main_chain_bei.this_block_tx_fee_median * main_chain_bei.bl.tx_hashes.size())
{
//with the rest equal, alt block has more fees in it, prefer it
return true;
}
}
if (!is_pos_block(main_chain_bei.bl))
return false; // do not reorganize on the same cummul diff if it's a PoW block
@ -2657,6 +2681,215 @@ bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDO
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::get_target_outs_for_amount_prezarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map<uint64_t, uint64_t>& amounts_to_up_index_limit_cache) const
{
size_t decoys_count = details.offsets.size();
uint64_t amount = details.amount;
uint64_t outs_container_size = m_db_outputs.get_item_size(details.amount);
if (!outs_container_size)
{
LOG_ERROR("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist");
return false;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist
}
//it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split
//lets find upper bound of not fresh outs
size_t up_index_limit = 0;
auto it_limit = amounts_to_up_index_limit_cache.find(amount);
if (it_limit == amounts_to_up_index_limit_cache.end())
{
up_index_limit = find_end_of_allowed_index(amount);
amounts_to_up_index_limit_cache[up_index_limit];
}
else
{
up_index_limit = it_limit->second;
}
CHECK_AND_ASSERT_MES(up_index_limit <= outs_container_size, false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << outs_container_size);
if (up_index_limit >= decoys_count)
{
std::set<size_t> used;
used.insert(details.own_global_index);
for (uint64_t j = 0; j != decoys_count || used.size() >= up_index_limit;)
{
size_t g_index_initial = crypto::rand<size_t>() % up_index_limit;
size_t g_index = g_index_initial;
//enumerate via whole loop from g_index to up_index_limit and then from 0 to g_index
while (true)
{
if (!used.count(g_index))
break;
g_index++;
if (g_index >= up_index_limit)
g_index = 0;
if (g_index == g_index_initial)
{
// we enumerated full circle and couldn't find needed amount of outs
LOG_PRINT_YELLOW("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << decoys_count << ", added " << result_outs.outs.size() << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total", LOG_LEVEL_0);
return true;
}
}
bool added = add_out_to_get_random_outs(result_outs, amount, g_index, decoys_count, req.use_forced_mix_outs, req.height_upper_limit);
used.insert(g_index);
if (added)
++j;
}
if (result_outs.outs.size() < decoys_count)
{
LOG_PRINT_YELLOW("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << decoys_count << ", added " << result_outs.outs.size() << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total", LOG_LEVEL_0);
}
return true;
}
else
{
size_t added = 0;
for (size_t i = 0; i != up_index_limit; i++)
added += add_out_to_get_random_outs(result_outs, amount, i, decoys_count, req.use_forced_mix_outs, req.height_upper_limit) ? 1 : 0;
LOG_PRINT_YELLOW("Not enough inputs for amount " << print_money_brief(amount) << ", needed " << decoys_count << ", added " << added << " good outs from " << up_index_limit << " unlocked of " << outs_container_size << " total - respond with all good outs", LOG_LEVEL_0);
return true;
}
}
//------------------------------------------------------------------
bool blockchain_storage::get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map<uint64_t, uint64_t>& amounts_to_up_index_limit_cache) const
{
std::set<uint64_t> used;
used.insert(details.own_global_index);
for (auto offset : details.offsets)
{
//perfectly we would need to find transaction's output on the given height, with the given probability
//of being coinbase(coinbase outputs should be included less in decoy selection algorithm)
bool is_coinbase = (crypto::rand<uint64_t>() % 101) > req.coinbase_percents ? false : true;
//TODO: Consider including PoW coinbase to transactions(does it needed?)
// convert offset to estimated height
uint64_t estimated_h = this->get_current_blockchain_size() - 1 - offset;
//make sure it's after zc hardfork
if (estimated_h < m_core_runtime_config.hard_forks.m_height_the_hardfork_n_active_after[ZANO_HARDFORK_04_ZARCANUM])
{
LOG_ERROR("Wrong estimated offset(" << offset << "), it hits zone before zarcanum hardfork");
return false;
}
#define TARGET_RANDOM_OUTS_SELECTIOM_POOL_MIN 10
//try to find output around given H
std::vector<uint64_t> selected_global_indexes;
auto process_tx = [&](const crypto::hash& tx_id) {
auto tx_ptr = m_db_transactions.find(tx_id);
CHECK_AND_ASSERT_THROW_MES(tx_ptr, "internal error: tx_id " << tx_id << " around estimated_h = " << estimated_h << " not found in db");
//go through tx outputs
for (size_t i = 0; i != tx_ptr->tx.vout.size(); i++)
{
if (tx_ptr->tx.vout[i].type() != typeid(tx_out_zarcanum))
{
continue;
}
const tx_out_zarcanum& z_out = boost::get<tx_out_zarcanum>(tx_ptr->tx.vout[i]);
// NOTE: second part of condition (mix_attr >= CURRENCY_TO_KEY_OUT_FORCED_MIX_LOWER_BOUND && ..) might be not accurate
// since the wallet might want to request more inputs then it planning to do mixins. For now let's keep it this way and fix
// it if we see the problems about it.
if (z_out.mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX || (z_out.mix_attr >= CURRENCY_TO_KEY_OUT_FORCED_MIX_LOWER_BOUND && z_out.mix_attr < details.offsets.size()))
{
continue;
}
// skip spent outptus
if (tx_ptr->m_spent_flags[i])
{
continue;
}
if (used.find(tx_ptr->m_global_output_indexes[i]) != used.end())
{
continue;
}
// add output
// note: code that will process selected_global_indes will be revisiting transactions entries to obtain all
// needed data, that should work relatively effective because of on-top-of-db cache keep daya unserialized
selected_global_indexes.push_back(tx_ptr->m_global_output_indexes[i]);
}
};
while (selected_global_indexes.size() < TARGET_RANDOM_OUTS_SELECTIOM_POOL_MIN)
{
auto block_ptr = m_db_blocks.get(estimated_h);
if (is_coinbase && is_pos_block(block_ptr->bl) )
{
process_tx(get_transaction_hash(block_ptr->bl.miner_tx));
}
else
{
//looking for regular output of regular transactions
for (auto tx_id : block_ptr->bl.tx_hashes)
{
process_tx(tx_id);
}
}
if(estimated_h)
estimated_h--;
else
{
//likely unusual situation when blocks enumerated all way back to genesis
//let's check if we have at least something
if (!selected_global_indexes.size())
{
//need to regenerate offsets
return false;
}
}
}
//pick up a random output from selected_global_indes
uint64_t global_index = selected_global_indexes[crypto::rand<uint64_t>() % selected_global_indexes.size()];
bool res = add_out_to_get_random_outs(result_outs, details.amount, global_index, details.offsets.size(), req.use_forced_mix_outs, req.height_upper_limit);
CHECK_AND_ASSERT_THROW_MES(res, "Failed to add_out_to_get_random_outs([" << global_index << "]) at postzarcanum era");
used.insert(global_index);
}
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::get_random_outs_for_amounts2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res)const
{
CRITICAL_REGION_LOCAL(m_read_lock);
LOG_PRINT_L3("[get_random_outs_for_amounts] amounts: " << req.amounts.size());
std::map<uint64_t, uint64_t> amounts_to_up_index_limit_cache;
uint64_t count_zarcanum_blocks = 0;
if(is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM))
count_zarcanum_blocks = this->get_current_blockchain_size() - m_core_runtime_config.hard_forks.m_height_the_hardfork_n_active_after[ZANO_HARDFORK_04_ZARCANUM];
for (size_t i = 0; i != req.amounts.size(); i++)
{
uint64_t amount = req.amounts[i].amount;
//const std::vector<uint64_t>& offsets = req.amounts[i].offsets;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount());
result_outs.amount = amount;
bool r = false;
if (amount == 0 && count_zarcanum_blocks > 20000)
{
//zarcanum era inputs
r = get_target_outs_for_postzarcanum(req, req.amounts[i], result_outs, amounts_to_up_index_limit_cache);
}
else
{
//zarcanum era inputs
r = get_target_outs_for_amount_prezarcanum(req, req.amounts[i], result_outs, amounts_to_up_index_limit_cache);
}
if (!r)
return false;
}
return true;
}
//------------------------------------------------------------------
boost::multiprecision::uint128_t blockchain_storage::total_coins() const
{
CRITICAL_REGION_LOCAL(m_read_lock);
@ -4093,6 +4326,7 @@ uint64_t blockchain_storage::get_tx_fee_median() const
//------------------------------------------------------------------
uint64_t blockchain_storage::get_alias_coast(const std::string& alias) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
uint64_t median_fee = get_tx_fee_median();
//CHECK_AND_ASSERT_MES_NO_RET(median_fee, "can't calculate median");
@ -4103,9 +4337,29 @@ uint64_t blockchain_storage::get_alias_coast(const std::string& alias) const
return get_alias_coast_from_fee(alias, median_fee);
}
//------------------------------------------------------------------
uint64_t blockchain_storage::get_tx_fee_window_value_median() const
{
// calc it every time and cache it so it won't recalculated before next block
// it's effective because it's not affect sync time and needed only when node is synced
// and processing transactions
misc_utils::median_helper<uint64_t, uint64_t> mh;
for (uint64_t i = 0; i < CORE_FEE_BLOCKS_LOOKUP_WINDOW; i++)
{
uint64_t h = m_db_blocks.size() - 1 - i;
if (h >= m_db_blocks.size())
break;
auto block_ptr = m_db_blocks[h];
CHECK_AND_ASSERT_THROW_MES(block_ptr, "Unexpected missing block " << h << " in get_tx_fee_window_value_median");
mh.push_item(block_ptr->block_cumulative_size, 0);
}
return (mh.get_median() + mh.get_avg())/2;
}
//------------------------------------------------------------------
bool blockchain_storage::unprocess_blockchain_tx_attachments(const transaction& tx, uint64_t h, uint64_t timestamp)
{
size_t cnt_serv_attach = get_service_attachments_count_in_tx(tx);
if (cnt_serv_attach == 0)
return true;
@ -4417,7 +4671,7 @@ uint64_t blockchain_storage::tx_fee_median_for_height(uint64_t h)const
//------------------------------------------------------------------
bool blockchain_storage::validate_all_aliases_for_new_median_mode()
{
LOG_PRINT_L0("Started reinitialization of median fee...");
LOG_PRINT_L0("Started reinitialization of median fee...");
math_helper::once_a_time_seconds<10> log_idle;
uint64_t sz = m_db_blocks.size();
for (uint64_t i = 0; i != sz; i++)
@ -5584,9 +5838,12 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
return false;
}
size_t count_ado = 0;
//extra
for (const auto el : tx.extra)
{
if (el.type() == typeid(asset_descriptor_operation))
count_ado++;
if (!var_is_after_hardfork_1_zone && !is_allowed_before_hardfork1(el))
return false;
if (!var_is_after_hardfork_2_zone && !is_allowed_before_hardfork2(el))
@ -5611,18 +5868,38 @@ bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transacti
// TODO @#@# consider: 1) tx.proofs, 2) new proof data structures
if (var_is_after_hardfork_4_zone)
{
{
CHECK_AND_ASSERT_MES(tx.version > TRANSACTION_VERSION_PRE_HF4, false, "HF4: tx with version " << tx.version << " is not allowed");
CHECK_AND_ASSERT_MES(tx.vout.size() >= CURRENCY_TX_MIN_ALLOWED_OUTS, false, "HF4: tx.vout has " << tx.vout.size() << " element(s), while required minimum is " << CURRENCY_TX_MIN_ALLOWED_OUTS);
if (is_pos_miner_tx(tx))
CHECK_AND_ASSERT_MES(tx.vout.size() == 1 || tx.vout.size() >= CURRENCY_TX_MIN_ALLOWED_OUTS, false, "HF4: tx.vout has " << tx.vout.size() << " element(s), while 1 or >= " << CURRENCY_TX_MIN_ALLOWED_OUTS << " is expected for a PoS miner tx");
else
CHECK_AND_ASSERT_MES(tx.vout.size() >= CURRENCY_TX_MIN_ALLOWED_OUTS, false, "HF4: tx.vout has " << tx.vout.size() << " element(s), while required minimum is " << CURRENCY_TX_MIN_ALLOWED_OUTS);
if(!validate_inputs_sorting(tx))
{
return false;
}
bool mode_separate = get_tx_flags(tx) & TX_FLAG_SIGNATURE_MODE_SEPARATE? true:false;
if (is_coinbase(tx) && mode_separate)
{
LOG_ERROR("TX_FLAG_SIGNATURE_MODE_SEPARATE not allowed for coinbase tx");
return false;
}
if (count_ado > 1)
{
LOG_ERROR("More then 1 asset_descriptor_operation not allowed in tx");
return false;
}
if (mode_separate && count_ado > 0)
{
LOG_ERROR("asset_descriptor_operation not allowed in tx with TX_FLAG_SIGNATURE_MODE_SEPARATE");
return false;
}
}
return true;
}
//------------------------------------------------------------------
@ -5734,6 +6011,7 @@ bool blockchain_storage::validate_pos_block(const block& b,
CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1, false, "incorrect number of stake input signatures: " << b.miner_tx.signatures.size());
CHECK_AND_ASSERT_MES(b.miner_tx.signatures[0].type() == typeid(zarcanum_sig), false, "incorrect sig 0 type: " << b.miner_tx.signatures[0].type().name());
//std::stringstream ss;
if (!for_altchain)
{
TIME_MEASURE_START_PD(pos_validate_get_out_keys_for_inputs);
@ -5745,6 +6023,18 @@ bool blockchain_storage::validate_pos_block(const block& b,
uint64_t dummy_source_max_unlock_time_for_pos_coinbase_dummy = 0; // won't be used
scan_for_keys_context scan_contex = AUTO_VAL_INIT(scan_contex);
r = get_output_keys_for_input_with_checks(b.miner_tx, stake_input, dummy_output_keys, max_related_block_height, dummy_source_max_unlock_time_for_pos_coinbase_dummy, scan_contex);
//#define ADD_ITEM_TO_SS(item) ss << " " #item ": " << m_performance_data.item.get_last_val() << ENDL
// ADD_ITEM_TO_SS(tx_check_inputs_loop_scan_outputkeys_get_item_size);
// ADD_ITEM_TO_SS(tx_check_inputs_loop_scan_outputkeys_relative_to_absolute);
// ADD_ITEM_TO_SS(tx_check_inputs_loop_scan_outputkeys_loop);
// ADD_ITEM_TO_SS(tx_check_inputs_loop_scan_outputkeys_loop_iteration);
// ss << " tx_check_inputs_loop_scan_outputkeys_loop_iteration (avg): " << m_performance_data.tx_check_inputs_loop_scan_outputkeys_loop_iteration.get_avg() << ENDL;
// ADD_ITEM_TO_SS(tx_check_inputs_loop_scan_outputkeys_loop_get_subitem);
// ADD_ITEM_TO_SS(tx_check_inputs_loop_scan_outputkeys_loop_find_tx);
// ADD_ITEM_TO_SS(tx_check_inputs_loop_scan_outputkeys_loop_handle_output);
//#undef ADD_ITEM_TO_SS
CHECK_AND_ASSERT_MES(r, false, "get_output_keys_for_input_with_checks failed for stake input");
CHECK_AND_ASSERT_MES(scan_contex.zc_outs.size() == stake_input.key_offsets.size(), false, "incorrect number of referenced outputs found: " << scan_contex.zc_outs.size() << ", while " << stake_input.key_offsets.size() << " is expected.");
// make sure that all referring inputs are either older then, or the same age as, the most resent PoW block.
@ -5765,6 +6055,8 @@ bool blockchain_storage::validate_pos_block(const block& b,
r = crypto::zarcanum_verify_proof(id, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, basic_diff, sig, &err);
TIME_MEASURE_FINISH_PD(pos_validate_zvp);
CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << (int)err);
//std::stringstream ss;
//std::cout << " validate_pos_block > get_output_keys_for_input_with_checks: " << ENDL << ss.str();
}
return true;
@ -6351,11 +6643,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
bvc.m_verification_failed = true;
return false;
}
bei.cumulative_diff_adjusted += cumulative_diff_delta;
//this used only in pre-hardfork 1
bei.cumulative_diff_adjusted += cumulative_diff_delta;
//////////////////////////////////////////////////////////////////////////
// rebuild cumulative_diff_precise_adjusted for whole period
// cumulative_diff_precise_adjusted - native cumulative difficulty adjusted ONLY by sequence_factor
wide_difficulty_type diff_precise_adj = correct_difficulty_with_sequence_factor(sequence_factor, current_diffic);
bei.cumulative_diff_precise_adjusted = last_x_h ? m_db_blocks[last_x_h]->cumulative_diff_precise_adjusted + diff_precise_adj : diff_precise_adj;
@ -6449,7 +6742,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED " << (is_pos_bl ? "[PoS]" : "[PoW]") << "["<< static_cast<uint64_t>(bei.bl.major_version) << "." << static_cast<uint64_t>(bei.bl.minor_version) << "] "<< " Sq: " << sequence_factor
<< ENDL << "id:\t" << id << timestamp_str_entry.str()
<< ENDL << powpos_str_entry.str()
<< ENDL << "HEIGHT " << bei.height << ", difficulty: " << current_diffic << ", cumul_diff_precise: " << bei.cumulative_diff_precise << ", cumul_diff_adj: " << bei.cumulative_diff_adjusted << " (+" << cumulative_diff_delta << ")"
<< ENDL << "HEIGHT " << bei.height << ", difficulty: " << current_diffic << ", cumul_diff_precise: " << bei.cumulative_diff_precise << ", cumul_diff_precise_adj: " << bei.cumulative_diff_precise_adjusted << " (+" << cumulative_diff_delta << ")"
<< ENDL << "block reward: " << print_money_brief(base_reward + fee_summary) << " (" << print_money_brief(base_reward) << " + " << print_money_brief(fee_summary)
<< ")" << ", coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", tx_count: " << bei.bl.tx_hashes.size()
<< ", timing: " << block_processing_time_0_ms << "ms"
@ -6467,6 +6760,23 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
<< ")"
<< "))");
{
static epee::math_helper::average<uint64_t, 30> blocks_processing_time_avg_pos, blocks_processing_time_avg_pow;
(is_pos_bl ? blocks_processing_time_avg_pos : blocks_processing_time_avg_pow).push(block_processing_time_0_ms);
static std::deque<uint64_t> blocks_processing_time_median_pos, blocks_processing_time_median_pow;
std::deque<uint64_t>& d = (is_pos_bl ? blocks_processing_time_median_pos : blocks_processing_time_median_pow);
d.push_back(block_processing_time_0_ms);
if (d.size() > 200)
d.pop_front();
uint64_t median_pow = epee::misc_utils::median(blocks_processing_time_median_pow);
uint64_t median_pos = epee::misc_utils::median(blocks_processing_time_median_pos);
LOG_PRINT_YELLOW("last 30 blocks of type processing time (ms): PoW: " << std::setw(3) << (uint64_t)blocks_processing_time_avg_pow.get_avg() << ", PoS: " << (uint64_t)blocks_processing_time_avg_pos.get_avg(), LOG_LEVEL_1);
LOG_PRINT_YELLOW("last 200 blocks of type processing time (median, ms): PoW: " << std::setw(3) << median_pow << ", PoS: " << median_pos, LOG_LEVEL_1);
}
on_block_added(bei, id, block_summary_kimages);
bvc.m_added_to_main_chain = true;
@ -7031,8 +7341,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
CRITICAL_REGION_LOCAL(m_read_lock);
bool r = false;
if (p_max_related_block_height != nullptr)
*p_max_related_block_height = 0;
uint64_t max_related_block_height = 0;
CHECK_AND_ASSERT_MES(input_index < input_tx.vin.size(), false, "invalid input index: " << input_index);
@ -7279,7 +7588,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
const txout_to_key& out_tk = boost::get<txout_to_key>(t);
pk = out_tk.key;
bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(out_tk.mix_attr, abs_key_offsets.size() - 1);
bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(p->tx.version, out_tk.mix_attr, abs_key_offsets.size() - 1, this->get_core_runtime_config());
CHECK_AND_ASSERT_MES(mixattr_ok, false, "input offset #" << pk_n << " violates mixin restrictions: mix_attr = " << static_cast<uint32_t>(out_tk.mix_attr) << ", input's key_offsets.size = " << abs_key_offsets.size());
}
@ -7293,8 +7602,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
// case b4 (make sure source tx in the main chain is preceding split point, otherwise this referece is invalid)
CHECK_AND_ASSERT_MES(p->m_keeper_block_height < split_height, false, "input offset #" << pk_n << " refers to main chain tx " << tx_id << " at height " << p->m_keeper_block_height << " while split height is " << split_height);
if (p_max_related_block_height != nullptr && *p_max_related_block_height < p->m_keeper_block_height)
*p_max_related_block_height = p->m_keeper_block_height;
if (max_related_block_height < p->m_keeper_block_height)
max_related_block_height = p->m_keeper_block_height;
// TODO: consider checking p->tx for unlock time validity as it's checked in get_output_keys_for_input_with_checks()
// make sure it was actually found
@ -7340,6 +7649,20 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
VARIANT_SWITCH_END();
if (p_max_related_block_height != nullptr)
*p_max_related_block_height = max_related_block_height;
uint64_t alt_bl_h = split_height + alt_chain.size() + 1;
if (m_core_runtime_config.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, alt_bl_h))
{
if (alt_bl_h - max_related_block_height < CURRENCY_HF4_MANDATORY_MIN_COINAGE)
{
LOG_ERROR("Coinage rule broken(altblock): h = " << alt_bl_h << ", max_related_block_height=" << max_related_block_height << ", tx: " << input_tx_hash);
return false;
}
}
// TODO: consider checking input_tx for valid extra attachment info as it's checked in check_tx_inputs()
return true;
}
@ -7656,6 +7979,7 @@ bool blockchain_storage::validate_alt_block_txs(const block& b, const crypto::ha
CHECK_AND_ASSERT_MES(validate_tx_for_hardfork_specific_terms(b.miner_tx, null_hash, height), false, "miner tx hardfork-specific validation failed");
std::vector<uint64_t> fees;
for (auto tx_id : b.tx_hashes)
{
std::shared_ptr<transaction> tx_ptr;
@ -7667,6 +7991,9 @@ bool blockchain_storage::validate_alt_block_txs(const block& b, const crypto::ha
const transaction& tx = it == abei.onboard_transactions.end() ? *tx_ptr : it->second;
CHECK_AND_ASSERT_MES(tx.signatures.size() == tx.vin.size(), false, "invalid tx: signatures.size() == " << tx.signatures.size() << ", tx.vin.size() == " << tx.vin.size());
fees.push_back(get_tx_fee(tx));
for (size_t n = 0; n < tx.vin.size(); ++n)
{
if (tx.vin[n].type() == typeid(txin_to_key) || tx.vin[n].type() == typeid(txin_htlc) || tx.vin[n].type() == typeid(txin_zc_input))
@ -7699,6 +8026,7 @@ bool blockchain_storage::validate_alt_block_txs(const block& b, const crypto::ha
update_alt_out_indexes_for_tx_in_block(tx, abei);
}
abei.this_block_tx_fee_median = epee::misc_utils::median(fees);
return true;
}

View file

@ -286,6 +286,7 @@ namespace currency
bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp)const;
bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)const;
bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res)const;
bool get_random_outs_for_amounts2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res)const;
bool get_backward_blocks_sizes(size_t from_height, std::vector<size_t>& sz, size_t count)const;
bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector<uint64_t>& indexs)const;
bool get_alias_info(const std::string& alias, extra_alias_entry_base& info)const;
@ -329,6 +330,7 @@ namespace currency
boost::multiprecision::uint128_t total_coins()const;
bool is_pos_allowed()const;
uint64_t get_tx_fee_median()const;
uint64_t get_tx_fee_window_value_median() const;
uint64_t get_tx_expiration_median() const;
uint64_t validate_alias_reward(const transaction& tx, const std::string& ai)const;
void set_event_handler(i_core_event_handler* event_handler) const;
@ -589,8 +591,8 @@ namespace currency
mutable std::atomic<bool> m_deinit_is_done;
mutable uint64_t m_blockchain_launch_timestamp;
bool init_tx_fee_median();
bool update_tx_fee_median();
//bool init_tx_fee_median();
//bool update_tx_fee_median();
void store_db_solo_options_values();
bool set_lost_tx_unmixable();
bool set_lost_tx_unmixable_for_height(uint64_t height);
@ -641,6 +643,8 @@ namespace currency
bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id);
bool add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i, uint64_t mix_count, bool use_only_forced_to_mix = false, uint64_t height_upper_limit = 0) const;
bool get_target_outs_for_amount_prezarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map<uint64_t, uint64_t>& amounts_to_up_index_limit_cache) const;
bool get_target_outs_for_postzarcanum(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& details, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, std::map<uint64_t, uint64_t>& amounts_to_up_index_limit_cache) const;
bool add_block_as_invalid(const block& bl, const crypto::hash& h);
bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h);
size_t find_end_of_allowed_index(uint64_t amount)const;
@ -655,6 +659,7 @@ namespace currency
uint64_t get_tx_fee_median_effective_index(uint64_t h) const;
void on_abort_transaction();
void load_targetdata_cache(bool is_pos) const;
uint64_t get_adjusted_time()const;
@ -737,6 +742,8 @@ namespace currency
template<class visitor_t>
bool blockchain_storage::scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_v& verified_input, visitor_t& vis, uint64_t& max_related_block_height, scan_for_keys_context& scan_context) const
{
bool hf4 = this->is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM);
uint64_t amount = get_amount_from_variant(verified_input);
const std::vector<txout_ref_v>& key_offsets = get_key_offsets_from_txin_v(verified_input);
@ -752,7 +759,7 @@ namespace currency
TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_relative_to_absolute);
TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop);
size_t output_index = 0;
for(const txout_ref_v& o : absolute_offsets)
for (const txout_ref_v& o : absolute_offsets)
{
crypto::hash tx_id = null_hash;
size_t n = 0;
@ -807,8 +814,13 @@ namespace currency
//fix for burned money
patch_out_if_needed(const_cast<txout_to_key&>(outtk), tx_id, n);
bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(outtk.mix_attr, key_offsets.size() - 1);
CHECK_AND_ASSERT_MES(mixattr_ok, false, "tx input ref #" << output_index << " violates mixin restrictions: mix_attr = " << static_cast<uint32_t>(outtk.mix_attr) << ", key_offsets.size = " << key_offsets.size());
bool mixattr_ok = is_mixattr_applicable_for_fake_outs_counter(tx_ptr->tx.version, outtk.mix_attr, key_offsets.size() - 1, this->get_core_runtime_config());
CHECK_AND_ASSERT_MES(mixattr_ok, false, "tx input ref #" << output_index << " violates mixin restrictions: tx.version = " << tx_ptr->tx.version << ", mix_attr = " << static_cast<uint32_t>(outtk.mix_attr) << ", key_offsets.size = " << key_offsets.size());
if (hf4)
{
bool legit_output_key = validate_output_key_legit(outtk.key);
CHECK_AND_ASSERT_MES(legit_output_key, false, "tx input ref #" << output_index << " violates public key restrictions: tx.version = " << tx_ptr->tx.version << ", outtk.key = " << outtk.key);
}
}
else if (o.target.type() == typeid(txout_htlc))
{
@ -828,6 +840,12 @@ namespace currency
//HTLC IS expired, can be used ONLY by pkey_after_expiration and ONLY by to_key input
scan_context.htlc_is_expired = true;
}
if (hf4)
{
bool legit_output_key = validate_output_key_legit(scan_context.htlc_is_expired ? htlc_out.pkey_refund : htlc_out.pkey_redeem);
CHECK_AND_ASSERT_MES(legit_output_key, false, "tx input ref #" << output_index << " violates public key restrictions: tx.version = " << tx_ptr->tx.version << ", outtk.key = " << static_cast<const crypto::public_key&>(scan_context.htlc_is_expired ? htlc_out.pkey_refund : htlc_out.pkey_redeem));
}
}
else
{
@ -847,19 +865,23 @@ namespace currency
}
VARIANT_CASE_CONST(tx_out_zarcanum, out_zc)
bool r = is_output_allowed_for_input(out_zc, verified_input);
CHECK_AND_ASSERT_MES(r, false, "Input and output are incompatible");
CHECK_AND_ASSERT_MES(r, false, "Input and output are incompatible");
r = is_mixattr_applicable_for_fake_outs_counter(out_zc.mix_attr, key_offsets.size() - 1);
CHECK_AND_ASSERT_MES(r, false, "tx input ref #" << output_index << " violates mixin restrictions: mix_attr = " << static_cast<uint32_t>(out_zc.mix_attr) << ", key_offsets.size = " << key_offsets.size());
r = is_mixattr_applicable_for_fake_outs_counter(tx_ptr->tx.version, out_zc.mix_attr, key_offsets.size() - 1, this->get_core_runtime_config());
CHECK_AND_ASSERT_MES(r, false, "tx input ref #" << output_index << " violates mixin restrictions: tx.version = " << tx_ptr->tx.version << ", mix_attr = " << static_cast<uint32_t>(out_zc.mix_attr) << ", key_offsets.size = " << key_offsets.size());
TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output);
if (!vis.handle_output(tx_ptr->tx, validated_tx, out_zc, n))
{
size_t verified_input_index = std::find(validated_tx.vin.begin(), validated_tx.vin.end(), verified_input) - validated_tx.vin.begin();
LOG_PRINT_RED_L0("handle_output failed for output #" << n << " in " << tx_id << " referenced by input #" << verified_input_index << " in tx " << get_transaction_hash(validated_tx));
return false;
}
TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output);
bool legit_output_key = validate_output_key_legit(out_zc.stealth_address);
CHECK_AND_ASSERT_MES(legit_output_key, false, "tx input ref #" << output_index << " violates public key restrictions: tx.version = " << tx_ptr->tx.version << ", outtk.key = " << out_zc.stealth_address);
TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output);
if (!vis.handle_output(tx_ptr->tx, validated_tx, out_zc, n))
{
size_t verified_input_index = std::find(validated_tx.vin.begin(), validated_tx.vin.end(), verified_input) - validated_tx.vin.begin();
LOG_PRINT_RED_L0("handle_output failed for output #" << n << " in " << tx_id << " referenced by input #" << verified_input_index << " in tx " << get_transaction_hash(validated_tx));
return false;
}
TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output);
VARIANT_CASE_THROW_ON_OTHER();
VARIANT_SWITCH_END();
@ -867,9 +889,22 @@ namespace currency
if (max_related_block_height < tx_ptr->m_keeper_block_height)
max_related_block_height = tx_ptr->m_keeper_block_height;
++output_index;
}
if (m_core_runtime_config.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, this->get_current_blockchain_size()))
{
//with hard fork 4 make it network rule to have at least 10 confirmations
if (this->get_current_blockchain_size() - max_related_block_height < CURRENCY_HF4_MANDATORY_MIN_COINAGE)
{
LOG_ERROR("Coinage rule broken(mainblock): h = " << this->get_current_blockchain_size() << ", max_related_block_height=" << max_related_block_height << ", tx: " << get_transaction_hash(validated_tx));
return false;
}
}
TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_loop);
return true;
}

View file

@ -48,9 +48,9 @@ namespace currency
block bl;
uint64_t height;
uint64_t block_cumulative_size;
wide_difficulty_type cumulative_diff_adjusted;
wide_difficulty_type cumulative_diff_adjusted; // used only before hardfork 1
wide_difficulty_type cumulative_diff_precise;
wide_difficulty_type cumulative_diff_precise_adjusted;
wide_difficulty_type cumulative_diff_precise_adjusted; //used after hardfork 1 (cumulative difficulty adjusted only by sequence factor)
wide_difficulty_type difficulty;
boost::multiprecision::uint128_t already_generated_coins;
crypto::hash stake_hash; //TODO: unused field for PoW blocks, subject for refactoring

View file

@ -105,6 +105,7 @@ namespace currency
uint64_t max_alt_blocks;
crypto::public_key alias_validation_pubkey;
core_time_func_t get_core_time;
uint64_t hf4_minimum_mixins;
hard_forks_descriptor hard_forks;
@ -127,6 +128,7 @@ namespace currency
pc.tx_pool_min_fee = TX_MINIMUM_FEE;
pc.tx_default_fee = TX_DEFAULT_FEE;
pc.max_alt_blocks = CURRENCY_ALT_BLOCK_MAX_COUNT;
pc.hf4_minimum_mixins = CURRENCY_HF4_MANDATORY_DECOY_SET_SIZE;
// TODO: refactor the following
pc.hard_forks.set_hardfork_height(1, ZANO_HARDFORK_01_AFTER_HEIGHT);

View file

@ -60,7 +60,7 @@ namespace currency
// Using C++17 extended aggregate initialization (P0017R1). C++17, finally! -- sowle
const static crypto::public_key native_coin_asset_id = {{'\xd6', '\x32', '\x9b', '\x5b', '\x1f', '\x7c', '\x08', '\x05', '\xb5', '\xc3', '\x45', '\xf4', '\x95', '\x75', '\x54', '\x00', '\x2a', '\x2f', '\x55', '\x78', '\x45', '\xf6', '\x4d', '\x76', '\x45', '\xda', '\xe0', '\xe0', '\x51', '\xa6', '\x49', '\x8a'}}; // == crypto::c_point_H, checked in crypto_constants
const static crypto::public_key native_coin_asset_id_1div8 = {{'\x74', '\xc3', '\x2d', '\x3e', '\xaa', '\xfa', '\xfc', '\x62', '\x3b', '\xf4', '\x83', '\xe8', '\x58', '\xd4', '\x2e', '\x8b', '\xf4', '\xec', '\x7d', '\xf0', '\x64', '\xad', '\xa2', '\xe3', '\x49', '\x34', '\x46', '\x9c', '\xff', '\x6b', '\x62', '\x68'}}; // == 1/8 * crypto::c_point_H, checked in crypto_constants
const static crypto::point_t native_coin_asset_id_pt = crypto::c_point_H;
const static crypto::point_t native_coin_asset_id_pt {{ 20574939, 16670001, -29137604, 14614582, 24883426, 3503293, 2667523, 420631, 2267646, -4769165, -11764015, -12206428, -14187565, -2328122, -16242653, -788308, -12595746, -8251557, -10110987, 853396, -4982135, 6035602, -21214320, 16156349, 977218, 2807645, 31002271, 5694305, -16054128, 5644146, -15047429, -568775, -22568195, -8089957, -27721961, -10101877, -29459620, -13359100, -31515170, -6994674 }}; // c_point_H
const static wide_difficulty_type global_difficulty_pow_starter = DIFFICULTY_POW_STARTER;
const static wide_difficulty_type global_difficulty_pos_starter = DIFFICULTY_POS_STARTER;

View file

@ -37,6 +37,8 @@
#define CURRENT_BLOCK_MAJOR_VERSION 3
#define CURRENCY_DEFAULT_DECOY_SET_SIZE 10
#define CURRENCY_HF4_MANDATORY_DECOY_SET_SIZE 15
#define CURRENCY_HF4_MANDATORY_MIN_COINAGE 10
#define CURRENT_BLOCK_MINOR_VERSION 0
#define CURRENCY_BLOCK_FUTURE_TIME_LIMIT 60*60*2
@ -164,6 +166,8 @@
#define BLOCK_POS_STRICT_SEQUENCE_LIMIT 20 // the highest allowed sequence factor for a PoS block (i.e., the max total number of sequential PoS blocks is BLOCK_POS_STRICT_SEQUENCE_LIMIT + 1)
#define CORE_FEE_BLOCKS_LOOKUP_WINDOW 60 //number of blocks used to check if transaction flow is big enought to rise default fee
#define WALLET_FILE_SIGNATURE_OLD 0x1111012101101011LL // Bender's nightmare
#define WALLET_FILE_SIGNATURE_V2 0x1111011201101011LL // another Bender's nightmare
#define WALLET_FILE_BINARY_HEADER_VERSION_INITAL 1000
@ -246,8 +250,8 @@
#define BC_OFFERS_CURRENCY_MARKET_FILENAME "market.bin"
#ifndef TESTNET
#define WALLET_FILE_SERIALIZATION_VERSION 160
#define WALLET_FILE_LAST_SUPPORTED_VERSION 160
#define WALLET_FILE_SERIALIZATION_VERSION 161
#define WALLET_FILE_LAST_SUPPORTED_VERSION 161
#else
#define WALLET_FILE_LAST_SUPPORTED_VERSION (CURRENCY_FORMATION_VERSION+76)
#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+76)

View file

@ -129,7 +129,6 @@ namespace currency
//--------------------------------------------------------------------------------
bool verify_asset_surjection_proof(const transaction& tx, const crypto::hash& tx_id)
{
bool r = false;
if (tx.version <= TRANSACTION_VERSION_PRE_HF4)
return true;
@ -387,8 +386,10 @@ namespace currency
std::vector<uint64_t> out_amounts;
if (tx_version > TRANSACTION_VERSION_PRE_HF4)
{
// randomly split into CURRENCY_TX_MIN_ALLOWED_OUTS outputs
decompose_amount_randomly(block_reward, [&](uint64_t a){ out_amounts.push_back(a); }, CURRENCY_TX_MIN_ALLOWED_OUTS);
// randomly split into CURRENCY_TX_MIN_ALLOWED_OUTS outputs for PoW block, or for PoS block only if the stakeholder address differs
// (otherwise for PoS miner tx there will be ONE output with amount = stake_amount + reward)
if (!pos || miner_address != stakeholder_address)
decompose_amount_randomly(block_reward, [&](uint64_t a){ out_amounts.push_back(a); }, CURRENCY_TX_MIN_ALLOWED_OUTS);
}
else
{
@ -424,7 +425,8 @@ namespace currency
uint64_t stake_lock_time = 0;
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
stake_lock_time = pe.stake_unlock_time;
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address, stake_lock_time));
uint64_t amount = destinations.empty() ? pe.amount + block_reward : pe.amount; // combine stake and reward into one output if no destinations were generated above
destinations.push_back(tx_destination_entry(amount, stakeholder_address, stake_lock_time));
destinations.back().flags |= tx_destination_entry_flags::tdef_explicit_native_asset_id; // don't use asset id blinding as it's obvious which asset it is
}
@ -725,14 +727,24 @@ namespace currency
return total;
}
//---------------------------------------------------------------
bool is_mixattr_applicable_for_fake_outs_counter(uint8_t mix_attr, uint64_t fake_outputs_count)
bool is_mixattr_applicable_for_fake_outs_counter(uint64_t out_tx_version, uint8_t mix_attr, uint64_t fake_outputs_count, const core_runtime_config& rtc)
{
if (mix_attr >= CURRENCY_TO_KEY_OUT_FORCED_MIX_LOWER_BOUND)
return fake_outputs_count + 1 >= mix_attr;
else if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
return fake_outputs_count == 0;
if (out_tx_version >= TRANSACTION_VERSION_POST_HF4)
{
if (mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
return fake_outputs_count >= rtc.hf4_minimum_mixins;
else
return fake_outputs_count == 0; // CURRENCY_TO_KEY_OUT_FORCED_NO_MIX
}
else
return true;
{
if (mix_attr >= CURRENCY_TO_KEY_OUT_FORCED_MIX_LOWER_BOUND)
return fake_outputs_count + 1 >= mix_attr;
else if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
return fake_outputs_count == 0;
else
return true;
}
}
//---------------------------------------------------------------
bool parse_amount(uint64_t& amount, const std::string& str_amount_)
@ -3317,9 +3329,15 @@ namespace currency
att.push_back(tsa);
return true;
}
//---------------------------------------------------------------
bool validate_output_key_legit(const crypto::public_key& k)
{
if (currency::null_pkey == k)
{
return false;
}
return true;
}
//---------------------------------------------------------------
std::string print_money_brief(uint64_t amount, size_t decimal_point /* = CURRENCY_DISPLAY_DECIMAL_POINT */)
{
@ -4425,8 +4443,6 @@ namespace currency
a.k_image == b.k_image;
}
//--------------------------------------------------------------------------------
boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point,
const wide_difficulty_type& difficulty_pow_at_split_point,
const difficulties& a_diff,
@ -4459,6 +4475,51 @@ namespace currency
return res;
CATCH_ENTRY_WITH_FORWARDING_EXCEPTION();
}
//--------------------------------------------------------------------------------
// Note: we adjust formula and introduce multiplier,
// that let us never dive into floating point calculations (which we can't use in consensus)
// this multiplier should be greater than max multiprecision::uint128_t power 2
boost::multiprecision::uint1024_t get_adjuster_for_fork_choice_rule_hf4()
{
return boost::multiprecision::uint1024_t(std::numeric_limits<boost::multiprecision::uint128_t>::max()) * 10 * std::numeric_limits<boost::multiprecision::uint128_t>::max();
}
const boost::multiprecision::uint1024_t adjusting_multiplier = get_adjuster_for_fork_choice_rule_hf4();
boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty_hf4(const wide_difficulty_type& difficulty_pos_at_split_point,
const wide_difficulty_type& difficulty_pow_at_split_point,
const difficulties& a_diff,
const difficulties& b_diff)
{
static const wide_difficulty_type difficulty_pos_starter = DIFFICULTY_POS_STARTER;
static const wide_difficulty_type difficulty_pow_starter = DIFFICULTY_POW_STARTER;
const wide_difficulty_type& a_pos_cumulative_difficulty = a_diff.pos_diff > 0 ? a_diff.pos_diff : difficulty_pos_starter;
const wide_difficulty_type& b_pos_cumulative_difficulty = b_diff.pos_diff > 0 ? b_diff.pos_diff : difficulty_pos_starter;
const wide_difficulty_type& a_pow_cumulative_difficulty = a_diff.pow_diff > 0 ? a_diff.pow_diff : difficulty_pow_starter;
const wide_difficulty_type& b_pow_cumulative_difficulty = b_diff.pow_diff > 0 ? b_diff.pow_diff : difficulty_pow_starter;
boost::multiprecision::uint1024_t basic_sum_ = boost::multiprecision::uint1024_t(a_pow_cumulative_difficulty) + (boost::multiprecision::uint1024_t(a_pos_cumulative_difficulty)*difficulty_pow_at_split_point) / difficulty_pos_at_split_point;
boost::multiprecision::uint1024_t basic_sum_pow_minus2 = adjusting_multiplier /(basic_sum_ * basic_sum_);
boost::multiprecision::uint1024_t res =
(basic_sum_pow_minus2 * a_pow_cumulative_difficulty * a_pos_cumulative_difficulty) / (boost::multiprecision::uint1024_t(b_pow_cumulative_difficulty)*b_pos_cumulative_difficulty);
// if (res > boost::math::tools::max_value<wide_difficulty_type>())
// {
// ASSERT_MES_AND_THROW("[INTERNAL ERROR]: Failed to get_a_to_b_relative_cumulative_difficulty, res = " << res << ENDL
// << ", difficulty_pos_at_split_point: " << difficulty_pos_at_split_point << ENDL
// << ", difficulty_pow_at_split_point:" << difficulty_pow_at_split_point << ENDL
// << ", a_pos_cumulative_difficulty:" << a_pos_cumulative_difficulty << ENDL
// << ", b_pos_cumulative_difficulty:" << b_pos_cumulative_difficulty << ENDL
// << ", a_pow_cumulative_difficulty:" << a_pow_cumulative_difficulty << ENDL
// << ", b_pow_cumulative_difficulty:" << b_pow_cumulative_difficulty << ENDL
// );
// }
TRY_ENTRY();
// wide_difficulty_type short_res = res.convert_to<wide_difficulty_type>();
return res;
CATCH_ENTRY_WITH_FORWARDING_EXCEPTION();
}

View file

@ -362,7 +362,7 @@ namespace currency
tx_derivation_hint make_tx_derivation_hint_from_uint16(uint16_t hint);
std::string short_hash_str(const crypto::hash& h);
bool is_mixattr_applicable_for_fake_outs_counter(uint8_t mix_attr, uint64_t fake_attr_count);
bool is_mixattr_applicable_for_fake_outs_counter(uint64_t out_tx_version, uint8_t out_mix_attr, uint64_t fake_outputs_count, const core_runtime_config& rtc);
bool is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t current_blockchain_size, uint64_t current_time);
crypto::key_derivation get_encryption_key_derivation(bool is_income, const transaction& tx, const account_keys& acc_keys);
bool decrypt_payload_items(bool is_income, const transaction& tx, const account_keys& acc_keys, std::vector<payload_items_v>& decrypted_items);
@ -407,7 +407,7 @@ namespace currency
std::vector<txout_ref_v> relative_output_offsets_to_absolute(const std::vector<txout_ref_v>& off);
bool absolute_sorted_output_offsets_to_relative_in_place(std::vector<txout_ref_v>& offsets) noexcept;
bool validate_output_key_legit(const crypto::public_key& k);
// prints amount in format "3.14", "0.0"
std::string print_money_brief(uint64_t amount, size_t decimal_point = CURRENCY_DISPLAY_DECIMAL_POINT);
@ -421,6 +421,7 @@ namespace currency
bool add_padding_to_tx(transaction& tx, size_t count);
bool is_service_tx(const transaction& tx);
bool does_tx_have_only_mixin_inputs(const transaction& tx);
uint64_t get_hf4_inputs_key_offsets_count(const transaction& tx);
bool is_showing_sender_addres(const transaction& tx);
bool check_native_coins_amount_burnt_in_outs(const transaction& tx, const uint64_t amount, uint64_t* p_amount_burnt = nullptr);
std::string print_stake_kernel_info(const stake_kernel& sk);
@ -933,6 +934,13 @@ namespace currency
const difficulties& b_diff
);
boost::multiprecision::uint1024_t get_a_to_b_relative_cumulative_difficulty_hf4(const wide_difficulty_type& difficulty_pos_at_split_point,
const wide_difficulty_type& difficulty_pow_at_split_point,
const difficulties& a_diff,
const difficulties& b_diff
);
struct rpc_tx_payload_handler : public boost::static_visitor<bool>
{
tx_extra_rpc_entry& tv;

View file

@ -21,6 +21,7 @@
#include "crypto/hash.h"
#include "profile_tools.h"
#include "common/db_backend_selector.h"
#include "tx_semantic_validation.h"
DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated
@ -92,6 +93,15 @@ namespace currency
return true;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::check_tx_fee(const transaction &tx, uint64_t amount_fee)
{
if (amount_fee < m_blockchain.get_core_runtime_config().tx_pool_min_fee)
return false;
//m_blockchain.get
return true;
}
//---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(const transaction &tx, const crypto::hash &id, uint64_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool from_core)
{
bool r = false;
@ -158,20 +168,31 @@ namespace currency
//check key images for transaction if it is not kept by block
if(!from_core && !kept_by_block)
{
if(!validate_tx_semantic(tx, blob_size))
{
// tx semantics check failed
LOG_PRINT_RED_L0("Transaction " << id << " semantics check failed ");
tvc.m_verification_failed = true;
tvc.m_should_be_relayed = false;
tvc.m_added_to_pool = false;
return false;
}
crypto::key_image spent_ki = AUTO_VAL_INIT(spent_ki);
r = !have_tx_keyimges_as_spent(tx, &spent_ki);
CHECK_AND_ASSERT_MES(r, false, "Transaction " << id << " uses already spent key image " << spent_ki);
//transaction spam protection, soft rule
if (tx_fee < m_blockchain.get_core_runtime_config().tx_pool_min_fee)
if (!check_tx_fee(tx, tx_fee))
{
if (is_valid_contract_finalization_tx(tx))
{
//if (is_valid_contract_finalization_tx(tx))
//{
// that means tx has less fee then allowed by current tx pull rules, but this transaction is actually
// a finalization of contract, and template of this contract finalization tx was prepared actually before
// fee rules had been changed, so it's ok, let it in.
}
else
//}
//else
{
// this tx has no fee
LOG_PRINT_RED_L0("Transaction " << id << " has too small fee: " << print_money_brief(tx_fee) << ", minimum fee: " << print_money_brief(m_blockchain.get_core_runtime_config().tx_pool_min_fee));

View file

@ -145,6 +145,7 @@ namespace currency
bool remove_key_images(const crypto::hash &tx_id, const transaction& tx, bool kept_by_block);
bool insert_alias_info(const transaction& tx);
bool remove_alias_info(const transaction& tx);
bool check_tx_fee(const transaction &tx, uint64_t amount_fee);
bool is_valid_contract_finalization_tx(const transaction &tx)const;
void store_db_solo_options_values();

View file

@ -1939,6 +1939,17 @@ QString MainWindow::restore_wallet(const QString& param)
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
QString MainWindow::use_whitelisting(const QString& param)
{
TRY_ENTRY();
LOG_API_TIMING();
//return que_call2<view::restore_wallet_request>("restore_wallet", param, [this](const view::restore_wallet_request& owd, view::api_response& ar){
PREPARE_ARG_FROM_JSON(view::api_request_t<bool>, owd);
PREPARE_RESPONSE(view::api_responce_return_code, ar);
ar.error_code = m_backend.use_whitelisting(owd.wallet_id, owd.req_data);
return MAKE_RESPONSE(ar);
CATCH_ENTRY_FAIL_API_RESPONCE();
}
QString MainWindow::open_wallet(const QString& param)
{
TRY_ENTRY();

View file

@ -144,6 +144,7 @@ public:
QString webkit_launched_script();
QString get_smart_wallet_info(const QString& param);
QString restore_wallet(const QString& param);
QString use_whitelisting(const QString& param);
QString is_pos_allowed();
QString store_to_file(const QString& path, const QString& buff);
QString load_from_file(const QString& path);

View file

@ -384,6 +384,18 @@ namespace currency
return true;
}
bool core_rpc_server::on_get_random_outs2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res, connection_context& cntx)
{
CHECK_CORE_READY();
res.status = API_RETURN_CODE_FAIL;
if (!m_core.get_blockchain_storage().get_random_outs_for_amounts2(req, res))
{
return true;
}
res.status = API_RETURN_CODE_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx)
{

View file

@ -49,6 +49,7 @@ namespace currency
bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res, connection_context& cntx);
bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res, connection_context& cntx);
bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx);
bool on_get_random_outs2(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res, connection_context& cntx);
bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, connection_context& cntx);
bool on_set_maintainers_info(const COMMAND_RPC_SET_MAINTAINERS_INFO::request& req, COMMAND_RPC_SET_MAINTAINERS_INFO::response& res, connection_context& cntx);
bool on_get_tx_pool(const COMMAND_RPC_GET_TX_POOL::request& req, COMMAND_RPC_GET_TX_POOL::response& res, connection_context& cntx);
@ -109,6 +110,7 @@ namespace currency
MAP_URI_AUTO_BIN2("/getblocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST)
MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES)
MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
MAP_URI_AUTO_BIN2("/getrandom_outs2.bin", on_get_random_outs2, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2)
MAP_URI_AUTO_BIN2("/set_maintainers_info.bin", on_set_maintainers_info, COMMAND_RPC_SET_MAINTAINERS_INFO)
MAP_URI_AUTO_BIN2("/get_tx_pool.bin", on_get_tx_pool, COMMAND_RPC_GET_TX_POOL)
MAP_URI_AUTO_BIN2("/check_keyimages.bin", on_check_keyimages, COMMAND_RPC_CHECK_KEYIMAGES)
@ -141,6 +143,7 @@ namespace currency
MAP_JON_RPC ("get_all_pool_tx_list", on_get_all_pool_tx_list, COMMAND_RPC_GET_ALL_POOL_TX_LIST)
MAP_JON_RPC ("get_pool_info", on_get_pool_info, COMMAND_RPC_GET_POOL_INFO)
MAP_JON_RPC ("getrandom_outs", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS)
MAP_JON_RPC ("getrandom_outs2", on_get_random_outs2, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2)
MAP_JON_RPC ("get_votes", on_get_votes, COMMAND_RPC_GET_VOTES)
//assets api
MAP_JON_RPC ("get_asset_info", on_get_asset_info, COMMAND_RPC_GET_ASSET_INFO)

View file

@ -422,6 +422,42 @@ namespace currency
};
};
//-----------------------------------------------
struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2
{
struct offsets_distribution
{
uint64_t amount; //if amount is 0 then lookup in post-zarcanum zone only, if not 0 then pre-zarcanum only
std::vector<uint64_t> offsets; //[i] = height, estimated location where to pickup output of transaction
uint64_t own_global_index; //index to exclude from selection
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(offsets)
KV_SERIALIZE(own_global_index)
END_KV_SERIALIZE_MAP()
};
struct request
{
std::vector<offsets_distribution> amounts;
uint64_t height_upper_limit; // if nonzero, all the decoy outputs must be either older than, or the same age as this height
bool use_forced_mix_outs;
uint64_t coinbase_percents; //from 0 to 100, estimate percents of coinbase outputs included in decoy sets
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
KV_SERIALIZE(height_upper_limit)
KV_SERIALIZE(use_forced_mix_outs)
KV_SERIALIZE(coinbase_percents)
END_KV_SERIALIZE_MAP()
};
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response response;
};
//-----------------------------------------------
struct COMMAND_RPC_SET_MAINTAINERS_INFO
{

View file

@ -116,7 +116,7 @@ namespace ph = boost::placeholders;
fail_msg_writer() << "unknown error"; \
} \
#define CONFIRM_WITH_PASSWORD() if(!check_password_for_operation()) return true;
namespace
@ -140,6 +140,7 @@ namespace
const command_line::arg_descriptor<bool> arg_disable_tor_relay ( "disable-tor-relay", "Disable TOR relay", false);
const command_line::arg_descriptor<unsigned int> arg_set_timeout("set-timeout", "Set timeout for the wallet");
const command_line::arg_descriptor<std::string> arg_voting_config_file("voting-config-file", "Set voting config instead of getting if from daemon", "");
const command_line::arg_descriptor<bool> arg_no_password_confirmations("no-password-confirmation", "Enable/Disable password confirmation for transactions", false);
const command_line::arg_descriptor< std::vector<std::string> > arg_command ("command", "");
@ -445,6 +446,9 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
m_do_refresh_after_load = false;
}
m_password_salt = crypto::rand<uint64_t>();
m_password_hash = get_hash_from_pass_and_salt(pwd_container.password(), m_password_salt);
bool was_open = false;
if (!m_generate_new.empty())
{
@ -516,6 +520,12 @@ bool simple_wallet::deinit()
return close_wallet();
}
//----------------------------------------------------------------------------------------------------
crypto::hash simple_wallet::get_hash_from_pass_and_salt(const std::string& pass, uint64_t salt)
{
std::string pass_and_salt = pass + std::to_string(salt);
return crypto::cn_fast_hash(pass_and_salt.data(), pass_and_salt.size());
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::handle_command_line(const boost::program_options::variables_map& vm)
{
m_wallet_file = command_line::get_arg(vm, arg_wallet_file);
@ -529,8 +539,36 @@ void simple_wallet::handle_command_line(const boost::program_options::variables_
m_restore_wallet = command_line::get_arg(vm, arg_restore_wallet);
m_disable_tor = command_line::get_arg(vm, arg_disable_tor_relay);
m_voting_config_file = command_line::get_arg(vm, arg_voting_config_file);
m_no_password_confirmations = command_line::get_arg(vm, arg_no_password_confirmations);
}
//----------------------------------------------------------------------------------------------------
#define PASSWORD_CONFIRMATION_ATTEMPTS 3
bool simple_wallet::check_password_for_operation()
{
if (m_no_password_confirmations)
return true;
for (size_t i = 0; i != PASSWORD_CONFIRMATION_ATTEMPTS; i++)
{
tools::password_container pass_container;
if (!pass_container.read_password("Enter password to confirm operation:\n"))
{
fail_msg_writer() << "Failed to read password";
return false;
}
if (get_hash_from_pass_and_salt(pass_container.password(), m_password_salt) != m_password_hash)
{
fail_msg_writer() << "Wrong password";
continue;
}
return true;
}
fail_msg_writer() << "Confirmation failed with " << PASSWORD_CONFIRMATION_ATTEMPTS << " attempts";
return false;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::try_connect_to_daemon()
{
if (!m_wallet->check_connection())
@ -1529,6 +1567,7 @@ bool preprocess_asset_id(std::string& address_arg, crypto::public_key& asset_id)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::transfer(const std::vector<std::string> &args_)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (!try_connect_to_daemon())
return true;
@ -1692,6 +1731,7 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
//----------------------------------------------------------------------------------------------------
bool simple_wallet::show_seed(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
success_msg_writer() << "Please enter a password to secure this seed. Securing your seed is HIGHLY recommended. Leave password blank to stay unsecured.";
success_msg_writer(true) << "Remember, restoring a wallet from Secured Seed can only be done if you know its password.";
@ -1715,6 +1755,7 @@ bool simple_wallet::show_seed(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::spendkey(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
message_writer(epee::log_space::console_color_red, true, std::string())
<< "WARNING! Anyone who knows the following secret key can access your wallet and spend your coins.";
@ -1727,6 +1768,7 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::viewkey(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
message_writer(epee::log_space::console_color_yellow, false, std::string())
<< "WARNING! Anyone who knows the following secret key can view your wallet (but can not spend your coins).";
@ -1924,6 +1966,7 @@ bool simple_wallet::list_outputs(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sign_transfer(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
if (m_wallet->is_watch_only())
{
fail_msg_writer() << "You can't sign transaction in watch-only wallet";
@ -1997,6 +2040,7 @@ bool simple_wallet::tor_disable(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::deploy_new_asset(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
asset_descriptor_base adb = AUTO_VAL_INIT(adb);
if (!args.size() || args.size() > 1)
@ -2034,7 +2078,7 @@ bool simple_wallet::deploy_new_asset(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::emit_asset(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (args.size() != 2)
{
@ -2089,6 +2133,7 @@ bool simple_wallet::emit_asset(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::burn_asset(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (args.size() != 2)
{
@ -2137,6 +2182,7 @@ bool simple_wallet::burn_asset(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::update_asset(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (args.size() != 2)
{
@ -2186,6 +2232,7 @@ bool simple_wallet::update_asset(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::add_custom_asset_id(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (!args.size() || args.size() > 1)
{
@ -2221,6 +2268,7 @@ bool simple_wallet::add_custom_asset_id(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::generate_ionic_swap_proposal(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (args.size() != 2)
@ -2309,6 +2357,7 @@ bool simple_wallet::get_ionic_swap_proposal_info(const std::vector<std::string>
//----------------------------------------------------------------------------------------------------
bool simple_wallet::accept_ionic_swap_proposal(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (args.size() != 1)
@ -2350,6 +2399,7 @@ bool simple_wallet::accept_ionic_swap_proposal(const std::vector<std::string> &a
//----------------------------------------------------------------------------------------------------
bool simple_wallet::remove_custom_asset_id(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
if (!args.size() || args.size() > 1)
{
@ -2380,6 +2430,7 @@ bool simple_wallet::remove_custom_asset_id(const std::vector<std::string> &args)
//----------------------------------------------------------------------------------------------------
bool simple_wallet::sweep_below(const std::vector<std::string> &args)
{
CONFIRM_WITH_PASSWORD();
SIMPLE_WALLET_BEGIN_TRY_ENTRY();
bool r = false;
if (args.size() < 3 || args.size() > 4)
@ -2623,6 +2674,8 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_disable_tor_relay);
command_line::add_arg(desc_params, arg_set_timeout);
command_line::add_arg(desc_params, arg_voting_config_file);
command_line::add_arg(desc_params, arg_no_password_confirmations);

View file

@ -110,6 +110,8 @@ namespace currency
bool try_connect_to_daemon();
std::string get_tocken_info_string(const crypto::public_key& asset_id, uint64_t& decimal_point);
bool print_wti(const tools::wallet_public::wallet_transfer_info& wti);
bool check_password_for_operation();
crypto::hash get_hash_from_pass_and_salt(const std::string& pass, uint64_t salt);
//----------------- i_wallet2_callback ---------------------
virtual void on_new_block(uint64_t height, const currency::block& block) override;
@ -189,6 +191,10 @@ namespace currency
bool m_disable_tor;
std::string m_restore_wallet;
std::string m_voting_config_file;
bool m_no_password_confirmations = false;
crypto::hash m_password_hash;
uint64_t m_password_salt;
epee::console_handlers_binder m_cmd_binder;

View file

@ -77,6 +77,11 @@ namespace tools
return invoke_http_bin_remote_command2_update_is_disconnect("/getrandom_outs.bin", req, res);
}
//------------------------------------------------------------------------------------------------------------------------------
bool default_http_core_proxy::call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res)
{
return invoke_http_bin_remote_command2_update_is_disconnect("/getrandom_outs2.bin", req, res);
}
//------------------------------------------------------------------------------------------------------------------------------
bool default_http_core_proxy::call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& req, currency::COMMAND_RPC_SEND_RAW_TX::response& res)
{
return invoke_http_json_remote_command2_update_is_disconnect("/sendrawtransaction", req, res);

View file

@ -37,6 +37,7 @@ namespace tools
bool call_COMMAND_RPC_GET_TX_POOL(const currency::COMMAND_RPC_GET_TX_POOL::request& rqt, currency::COMMAND_RPC_GET_TX_POOL::response& rsp) override;
bool call_COMMAND_RPC_GET_ALIASES_BY_ADDRESS(const currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::request& rqt, currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::response& rsp) override;
bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& rsp) override;
bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& rsp) override;
bool call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& rqt, currency::COMMAND_RPC_SEND_RAW_TX::response& rsp) override;
bool call_COMMAND_RPC_FORCE_RELAY_RAW_TXS(const currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::request& rqt, currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::response& rsp) override;
bool call_COMMAND_RPC_GET_ALL_ALIASES(currency::COMMAND_RPC_GET_ALL_ALIASES::response& rsp) override;

View file

@ -58,6 +58,11 @@ namespace tools
return m_rpc.on_get_random_outs(req, res, m_cntxt_stub);
}
//------------------------------------------------------------------------------------------------------------------------------
bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& req, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& res) override
{
return m_rpc.on_get_random_outs2(req, res, m_cntxt_stub);
}
//------------------------------------------------------------------------------------------------------------------------------
bool call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& req, currency::COMMAND_RPC_SEND_RAW_TX::response& res) override
{
return m_rpc.on_send_raw_tx(req, res, m_cntxt_stub);

View file

@ -36,6 +36,7 @@ namespace tools
virtual bool call_COMMAND_RPC_GET_TX_POOL(const currency::COMMAND_RPC_GET_TX_POOL::request& rqt, currency::COMMAND_RPC_GET_TX_POOL::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GET_ALIASES_BY_ADDRESS(const currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::request& rqt, currency::COMMAND_RPC_GET_ALIASES_BY_ADDRESS::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::response& rsp) { return false; }
virtual bool call_COMMAND_RPC_SEND_RAW_TX(const currency::COMMAND_RPC_SEND_RAW_TX::request& rqt, currency::COMMAND_RPC_SEND_RAW_TX::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_FORCE_RELAY_RAW_TXS(const currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::request& rqt, currency::COMMAND_RPC_FORCE_RELAY_RAW_TXS::response& rsp){ return false; }
virtual bool call_COMMAND_RPC_GET_ALL_ALIASES(currency::COMMAND_RPC_GET_ALL_ALIASES::response& rsp){ return false; }

View file

@ -0,0 +1,128 @@
// Copyright (c) 2014-2023 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "decoy_selection.h"
#include "decoy_selection_default_distribution.hpp"
#include "crypto/crypto.h"
bool scaler::config_scale(uint64_t original, uint64_t scale_to)
{
m_x_m = original;
m_y_m = scale_to;
return true;
}
uint64_t scaler::scale(uint64_t h)
{
double k = double(m_x_m) / m_y_m;
double e_pow_minus_k = std::exp(-1 * k);
double a = e_pow_minus_k / (k - 1 + e_pow_minus_k);
double y = h * a + (1 - std::exp(-1 * (double(h) / double(m_y_m)) )) * m_y_m * (1 - a);
return static_cast<uint64_t>(std::round(y));
}
void decoy_selection_generator::init(uint64_t max_h)
{
load_distribution(g_default_distribution, max_h);
m_is_initialized = true;
}
bool decoy_selection_generator::load_distribution_from_file(const char* path)
{
return true;
}
#define TWO63 0x8000000000000000u
#define TWO64f (TWO63*2.0)
double map_uint_to_double(uint64_t u) {
double y = (double)u;
return y / TWO64f;
}
std::vector<uint64_t> decoy_selection_generator::generate_distribution(uint64_t count)
{
std::vector<uint64_t> res;
for (size_t i = 0; i != count; i++)
{
uint64_t r = 0;
crypto::generate_random_bytes(sizeof(r), &r);
double r_ = map_uint_to_double(r);
auto it = m_distribution_mapping.upper_bound(r_);
if (it == m_distribution_mapping.end())
{
throw(std::runtime_error(std::string("_r not found in m_distribution_mapping: ") + std::to_string(r_) ));
}
uint64_t h = it->second;
if (it != m_distribution_mapping.begin())
{
uint64_t h_0 = (--it)->second;
crypto::generate_random_bytes(sizeof(r), &r);
h = h_0 + r % (h - h_0) + 1;
}
//scale from nominal to max_h
res.push_back(h); }
return res;
}
uint64_t get_distance(const std::vector<decoy_selection_generator::distribution_entry> entries, size_t i)
{
if (i == 0)
return 1;
return entries[i].h - entries[i - 1].h;
}
bool decoy_selection_generator::load_distribution(const std::vector<decoy_selection_generator::distribution_entry>& original_distribution, uint64_t max_h)
{
//do prescale of distribution
std::vector<decoy_selection_generator::distribution_entry> derived_distribution;
scaler scl;
scl.config_scale(original_distribution.back().h, max_h);
uint64_t last_scaled_h = 0;
std::list<double> last_scaled_array;
for (size_t i = 0; i <= original_distribution.size(); i++)
{
if (i == original_distribution.size() || (scl.scale(original_distribution[i].h) != last_scaled_h && last_scaled_array.size()))
{
//put avg to data_scaled
double summ = 0;
for (auto item: last_scaled_array)
{
summ += item;
}
double avg = summ / last_scaled_array.size();
uint64_t prev_h = scl.scale(original_distribution[i - 1].h);
derived_distribution.push_back(decoy_selection_generator::distribution_entry{ prev_h, avg});
last_scaled_array.clear();
}
if (i == original_distribution.size())
{
break;
}
last_scaled_array.push_back(original_distribution[i].v);
last_scaled_h = scl.scale(original_distribution[i].h);
}
double total_v = 0;
for (size_t i = 0; i != derived_distribution.size(); i++)
{
total_v += derived_distribution[i].v * get_distance(derived_distribution, i);
}
double summ_current = 0;
for (size_t i = 0; i != derived_distribution.size(); i++)
{
double k = (derived_distribution[i].v * get_distance(derived_distribution, i))/ total_v;
summ_current += k;
m_distribution_mapping[summ_current] = derived_distribution[i].h;
}
return true;
}

View file

@ -0,0 +1,53 @@
// Copyright (c) 2014-2023 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <memory>
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/deque.hpp>
#include <boost/serialization/singleton.hpp>
#include <boost/serialization/extended_type_info.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/optional.hpp>
#include <atomic>
#include "include_base_utils.h"
#include "profile_tools.h"
#include "sync_locked_object.h"
class scaler
{
public:
//See the graph on https://www.desmos.com/calculator/zfx4bolfqx
bool config_scale(uint64_t original, uint64_t scale_to);
uint64_t scale(uint64_t h);
private:
uint64_t m_x_m;
uint64_t m_y_m;
};
class decoy_selection_generator
{
public:
struct distribution_entry
{
uint64_t h;
double v;
};
void init(uint64_t max_h);
bool load_distribution_from_file(const char* path);
std::vector<uint64_t> generate_distribution(uint64_t count);
bool is_initialized() { return m_is_initialized; }
private:
bool load_distribution(const std::vector<decoy_selection_generator::distribution_entry>& entries, uint64_t max_h);
bool m_is_initialized = false;
std::map<double, uint64_t> m_distribution_mapping;
};

File diff suppressed because it is too large Load diff

View file

@ -44,6 +44,7 @@ using namespace epee;
#include "currency_core/crypto_config.h"
#include "crypto/zarcanum.h"
#include "wallet_debug_events_definitions.h"
#include "decoy_selection.h"
using namespace currency;
@ -639,8 +640,17 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
auto it = m_key_images.find(ki);
if (it != m_key_images.end())
{
// Issue that has been discovered by Luke Parker (twitter: @kayabaNerve)
// An attacker can quickly issue transaction that use same outputs ephemeral keys + same tx key, as a result both
// transaction's outputs would have same key image, so the wallet should have smart approach to this situation, ie
// use output that offer biggest output value.(tokens?)
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->second < m_transfers.size(), "m_key_images entry has wrong m_transfers index, it->second: " << it->second << ", m_transfers.size(): " << m_transfers.size());
const transfer_details& local_td = m_transfers[it->second];
std::stringstream ss;
ss << "tx " << ptc.tx_hash() << " @ block " << height << " has output #" << o << " with key image " << ki << " that has already been seen in output #" <<
local_td.m_internal_output_index << " in tx " << get_transaction_hash(local_td.m_ptx_wallet_info->m_tx) << " @ block " << local_td.m_spent_height <<
@ -698,7 +708,8 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
if (ptc.coin_base_tx)
{
//last out in coinbase tx supposed to be change from coinstake
if (!(o == tx.vout.size() - 1 && !ptc.is_derived_from_coinbase)) // TODO: @#@# reconsider this condition
//for genesis block we'll count every input as WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER
if (td.m_ptx_wallet_info->m_block_height == 0 || !(o == tx.vout.size() - 1 && !ptc.is_derived_from_coinbase)) // TODO: @#@# reconsider this condition
{
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER;
}
@ -3338,7 +3349,16 @@ bool wallet2::balance(std::unordered_map<crypto::public_key, wallet_public::asse
if (is_transfer_unlocked(td))
e.unlocked += td.amount();
if (td.m_flags & WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER)
mined += td.amount();
{
if (td.m_ptx_wallet_info->m_block_height == 0)
{
//for genesis block we add actual amounts
mined += td.amount();
}
else {
mined += CURRENCY_BLOCK_REWARD; //this code would work only for cases where block reward is full. For reduced block rewards might need more flexible code (TODO)
}
}
}
}
@ -3413,10 +3433,13 @@ bool wallet2::balance(std::list<wallet_public::asset_balance_entry>& balances, u
auto it_cust = custom_assets_local.find(item.first);
if(it_cust == custom_assets_local.end())
{
if(!m_use_assets_whitelisting)
continue;
auto it_local = m_whitelisted_assets.find(item.first);
if(it_local == m_whitelisted_assets.end())
if(it_local == m_whitelisted_assets.end())
{
WLT_LOG_YELLOW("WARNING: unknown asset " << item.first << " found and skipped; it's NOT included in balance", LOG_LEVEL_0);
WLT_LOG_YELLOW("WARNING: unknown asset " << item.first << " found and skipped; it's NOT included in balance", LOG_LEVEL_1);
continue;
}
else
@ -3495,7 +3518,7 @@ bool wallet2::add_custom_asset_id(const crypto::public_key& asset_id, asset_desc
req.asset_id = asset_id;
bool r = m_core_proxy->call_COMMAND_RPC_GET_ASSET_INFO(req, resp);
if (resp.status == API_RETURN_CODE_OK)
if (r && resp.status == API_RETURN_CODE_OK)
{
m_custom_assets[asset_id] = resp.asset_descriptor;
asset_descriptor = resp.asset_descriptor;
@ -3636,7 +3659,7 @@ std::string wallet2::get_balance_str() const
balance(balances, mined);
for (const tools::wallet_public::asset_balance_entry& b : balances)
{
ss << " " << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(b.unlocked, b.asset_info.decimal_point);
ss << " " << std::left << std::setw(20) << print_fixed_decimal_point_with_trailing_spaces(b.unlocked, b.asset_info.decimal_point);
if (b.total == b.unlocked)
ss << " ";
else
@ -4265,7 +4288,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, uint64_t ful
uint64_t secret_index = 0; // index of the real stake output
// get decoys outputs and construct miner tx
static size_t required_decoys_count = 4; // TODO @#@# set them somewhere else
const size_t required_decoys_count = m_core_runtime_config.hf4_minimum_mixins == 0 ? 4 /* <-- for tests */ : m_core_runtime_config.hf4_minimum_mixins;
static bool use_only_forced_to_mix = false; // TODO @#@# set them somewhere else
if (required_decoys_count > 0 && !is_auditable())
{
@ -5465,17 +5488,19 @@ bool wallet2::create_ionic_swap_proposal(const wallet_public::ionic_swap_proposa
{
std::vector<uint64_t> selected_transfers_for_template;
build_ionic_swap_template(proposal_details, destination_addr, proposal, selected_transfers_for_template);
return build_ionic_swap_template(proposal_details, destination_addr, proposal, selected_transfers_for_template);
//const uint32_t mask_to_mark_escrow_template_locked_transfers = WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION;
//mark_transfers_with_flag(selected_transfers_for_template, mask_to_mark_escrow_template_locked_transfers, "preparing ionic_swap");
return true;
//return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::build_ionic_swap_template(const wallet_public::ionic_swap_proposal_info& proposal_detais, const currency::account_public_address& destination_addr,
wallet_public::ionic_swap_proposal& proposal,
std::vector<uint64_t>& selected_transfers)
{
WLT_THROW_IF_FALSE_WITH_CODE(proposal_detais.fee_paid_by_a >= get_current_minimum_network_fee(), "Error at build_ionic_swap_template, ", API_RETURN_CODE_WALLET_FEE_TOO_LOW);
construct_tx_param ctp = get_default_construct_tx_param();
ctp.fake_outputs_count = proposal_detais.mixins;
@ -5864,30 +5889,80 @@ bool wallet2::prepare_tx_sources(size_t fake_outputs_count, std::vector<currency
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
if (fake_outputs_count)
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req);
uint64_t zarcanum_start_from = m_core_runtime_config.hard_forks.m_height_the_hardfork_n_active_after[ZANO_HARDFORK_04_ZARCANUM];
uint64_t current_size = m_chain.get_blockchain_current_size();
decoy_selection_generator zarcanum_decoy_set_generator;
if (current_size - 1 >= zarcanum_start_from)
{
//in Zarcanum era
const uint64_t test_scale_size = current_size - 1 - zarcanum_start_from;
zarcanum_decoy_set_generator.init(test_scale_size - 1);
}
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::request req = AUTO_VAL_INIT(req);
req.height_upper_limit = m_last_pow_block_h; // request decoys to be either older than, or the same age as stake output's height
req.use_forced_mix_outs = false; // TODO: add this feature to UI later
req.decoys_count = fake_outputs_count + 1; // one more to be able to skip a decoy in case it hits the real output
//req.decoys_count = fake_outputs_count + 1; // one more to be able to skip a decoy in case it hits the real output
for (uint64_t i: selected_indicies)
{
req.amounts.push_back(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution());
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2::offsets_distribution& rdisttib = req.amounts.back();
auto it = m_transfers.begin() + i;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it->m_ptx_wallet_info->m_tx.vout.size() > it->m_internal_output_index,
"m_internal_output_index = " << it->m_internal_output_index <<
" is greater or equal to outputs count = " << it->m_ptx_wallet_info->m_tx.vout.size());
req.amounts.push_back(it->is_zc() ? 0 : it->m_amount);
rdisttib.own_global_index = it->m_global_output_index;
//check if we have Zarcanum era output of pre-Zarcanum
if (it->is_zc())
{
//Zarcanum era
rdisttib.amount = 0;
//generate distribution in Zarcanum hardfork
THROW_IF_FALSE_WALLET_INT_ERR_EX(zarcanum_decoy_set_generator.is_initialized(), "zarcanum_decoy_set_generator are not initialized");
rdisttib.offsets = zarcanum_decoy_set_generator.generate_distribution(fake_outputs_count);
}
else
{
//for prezarcanum era use flat distribution
rdisttib.amount = it->m_amount;
rdisttib.offsets.resize(fake_outputs_count, 0);
}
}
bool r = m_core_proxy->call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(req, daemon_resp);
THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs.bin");
THROW_IF_FALSE_WALLET_EX(daemon_resp.status != API_RETURN_CODE_BUSY, error::daemon_busy, "getrandom_outs.bin");
THROW_IF_FALSE_WALLET_EX(daemon_resp.status == API_RETURN_CODE_OK, error::get_random_outs_error, daemon_resp.status);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(daemon_resp.outs.size() == selected_indicies.size(),
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " << daemon_resp.outs.size() << ", expected: " << selected_indicies.size());
size_t attempt_count = 0;
while (true)
{
daemon_resp = COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response();
bool r = m_core_proxy->call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS2(req, daemon_resp);
THROW_IF_FALSE_WALLET_EX(r, error::no_connection_to_daemon, "getrandom_outs2.bin");
if(daemon_resp.status == API_RETURN_CODE_FAIL)
{
if (attempt_count < 10)
{
attempt_count++;
continue;
}
else
{
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(daemon_resp.outs.size() == selected_indicies.size(),
"unable to exacute getrandom_outs.bin after 10 attempts with code API_RETURN_CODE_FAIL, there must be problems with mixins");
}
}
THROW_IF_FALSE_WALLET_EX(daemon_resp.status != API_RETURN_CODE_BUSY, error::daemon_busy, "getrandom_outs.bin");
THROW_IF_FALSE_WALLET_EX(daemon_resp.status == API_RETURN_CODE_OK, error::get_random_outs_error, daemon_resp.status);
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(daemon_resp.outs.size() == selected_indicies.size(),
"daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " << daemon_resp.outs.size() << ", expected: " << selected_indicies.size());
break;
}
std::vector<COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount> scanty_outs;
for(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs : daemon_resp.outs)
{
if (amount_outs.outs.size() < req.decoys_count)
if (amount_outs.outs.size() < fake_outputs_count)
{
scanty_outs.push_back(amount_outs);
}
@ -6481,7 +6556,7 @@ bool wallet2::is_transfer_able_to_go(const transfer_details& td, uint64_t fake_o
uint8_t mix_attr = CURRENCY_TO_KEY_OUT_RELAXED;
if (get_mix_attr_from_tx_out_v(out_v, mix_attr))
{
if (!currency::is_mixattr_applicable_for_fake_outs_counter(mix_attr, fake_outputs_count))
if (!currency::is_mixattr_applicable_for_fake_outs_counter(td.m_ptx_wallet_info->m_tx.version, mix_attr, fake_outputs_count, m_core_runtime_config))
return false;
}

View file

@ -47,7 +47,7 @@
#include "view_iface.h"
#include "wallet2_base.h"
#define WALLET_DEFAULT_TX_SPENDABLE_AGE 10
#define WALLET_DEFAULT_TX_SPENDABLE_AGE CURRENCY_HF4_MANDATORY_MIN_COINAGE
#define WALLET_POS_MINT_CHECK_HEIGHT_INTERVAL 1
@ -153,6 +153,7 @@ namespace tools
uint64_t m_height_of_start_sync = 0;
std::atomic<uint64_t> m_last_sync_percent = 0;
mutable uint64_t m_current_wallet_file_size = 0;
bool m_use_assets_whitelisting = true;
//===============================================================
@ -216,6 +217,7 @@ namespace tools
a & m_custom_assets;
a & m_rollback_events;
a & m_whitelisted_assets;
a & m_use_assets_whitelisting;
}
};
@ -646,6 +648,7 @@ namespace tools
construct_tx_param get_default_construct_tx_param_inital();
void set_disable_tor_relay(bool disable);
uint64_t get_default_fee() {return TX_DEFAULT_FEE;}
uint64_t get_current_minimum_network_fee() { return TX_DEFAULT_FEE; }
void export_transaction_history(std::ostream& ss, const std::string& format, bool include_pos_transactions = true);
bool add_custom_asset_id(const crypto::public_key& asset_id, currency::asset_descriptor_base& asset_descriptor);
@ -890,7 +893,6 @@ private:
bool m_use_deffered_global_outputs;
bool m_disable_tor_relay;
bool m_use_assets_whitelisting = true;
mutable current_operation_context m_current_context;
std::string m_votes_config_path;

View file

@ -731,7 +731,7 @@ if (!(cond))
{ \
exception_handler(); \
std::stringstream ss; \
ss << std::endl << mess; \
ss << std::endl << "[" << error_code << "]" << mess ; \
LOG_ERROR(#cond << ". THROW EXCEPTION: " << error_code << ss.str()); \
tools::error::throw_wallet_ex<tools::error::wallet_error>(std::string(__FILE__ ":" STRINGIZE(__LINE__)), ss.str(), error_code); \
}

View file

@ -274,6 +274,21 @@ namespace wallet_public
};
struct wallet_transfer_info_old : public wallet_transfer_info
{
uint64_t amount = 0;
bool is_income = false;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(is_income)
KV_SERIALIZE(amount)
KV_CHAIN_BASE(wallet_transfer_info)
END_KV_SERIALIZE_MAP()
};
struct asset_balance_entry_base
{
uint64_t total = 0;
@ -486,25 +501,25 @@ namespace wallet_public
#define ORDER_FROM_BEGIN_TO_END "FROM_BEGIN_TO_END"
#define ORDER_FROM_FROM_END_TO_BEGIN "FROM_END_TO_BEGIN"
struct COMMAND_RPC_GET_RECENT_TXS_AND_INFO
struct COMMAND_RPC_GET_RECENT_TXS_AND_INFO2
{
struct request
{
/*
if offset is 0, then GET_RECENT_TXS_AND_INFO return
unconfirmed transactions as the first first items of "transfers",
if offset is 0, then GET_RECENT_TXS_AND_INFO return
unconfirmed transactions as the first first items of "transfers",
this unconfirmed transactions is not counted regarding "count" parameter
*/
uint64_t offset;
uint64_t count;
/*
need_to_get_info - should backend re-calculate balance(could be relatively heavy,
and not needed when getting long tx history with multiple calls
/*
need_to_get_info - should backend re-calculate balance(could be relatively heavy,
and not needed when getting long tx history with multiple calls
of GET_RECENT_TXS_AND_INFO with offsets)
*/
bool update_provision_info;
bool update_provision_info;
bool exclude_mining_txs;
bool exclude_unconfirmed;
std::string order; // "FROM_BEGIN_TO_END" or "FROM_END_TO_BEGIN"
@ -535,6 +550,30 @@ namespace wallet_public
};
};
struct COMMAND_RPC_GET_RECENT_TXS_AND_INFO
{
typedef COMMAND_RPC_GET_RECENT_TXS_AND_INFO2::request request;
struct response
{
wallet_provision_info pi;
std::vector<wallet_transfer_info_old> transfers;
uint64_t total_transfers;
uint64_t last_item_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(pi)
KV_SERIALIZE(transfers)
KV_SERIALIZE(total_transfers)
KV_SERIALIZE(last_item_index)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_REGISTER_ALIAS
{
struct request
@ -561,9 +600,9 @@ namespace wallet_public
struct transfer_destination
{
uint64_t amount;
uint64_t amount = 0;
std::string address;
crypto::public_key asset_id;
crypto::public_key asset_id = currency::native_coin_asset_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(address)

View file

@ -240,6 +240,7 @@ namespace tools
WALLET_RPC_CATCH_TRY_ENTRY();
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getwallet_info(const wallet_public::COMMAND_RPC_GET_WALLET_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
@ -258,6 +259,7 @@ namespace tools
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getwallet_restore_info(const wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
@ -265,6 +267,7 @@ namespace tools
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_seed_phrase_info(const wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::request& req, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
@ -272,7 +275,36 @@ namespace tools
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_recent_txs_and_info(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
//this is legacy api, should be removed after successful transition to HF4
wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO2::response rsp2 = AUTO_VAL_INIT(rsp2);
WALLET_RPC_BEGIN_TRY_ENTRY();
on_get_recent_txs_and_info2(req, rsp2, er, cntx);
res.pi = rsp2.pi;
res.total_transfers = rsp2.total_transfers;
res.last_item_index = rsp2.last_item_index;
for (const auto& item : rsp2.transfers)
{
res.transfers.push_back(wallet_public::wallet_transfer_info_old());
*static_cast<wallet_public::wallet_transfer_info*>(&res.transfers.back()) = item;
for (const auto& subitem : item.subtransfers)
{
if (subitem.asset_id == currency::native_coin_asset_id)
{
res.transfers.back().amount = subitem.amount;
res.transfers.back().is_income = subitem.is_income;
}
}
}
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_recent_txs_and_info2(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO2::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO2::response& res, epee::json_rpc::error& er, connection_context& cntx)
{
WALLET_RPC_BEGIN_TRY_ENTRY();
if (req.update_provision_info)
@ -1021,7 +1053,7 @@ namespace tools
{
WALLET_RPC_BEGIN_TRY_ENTRY();
std::string buff = epee::string_encoding::base64_decode(req.buff);
bool r = w.get_wallet()->encrypt_buffer(buff, res.res_buff);
w.get_wallet()->encrypt_buffer(buff, res.res_buff);
res.res_buff = epee::string_encoding::base64_encode(res.res_buff);
return true;
WALLET_RPC_CATCH_TRY_ENTRY();
@ -1031,7 +1063,7 @@ namespace tools
{
WALLET_RPC_BEGIN_TRY_ENTRY();
std::string buff = epee::string_encoding::base64_decode(req.buff);
bool r = w.get_wallet()->encrypt_buffer(buff, res.res_buff);
w.get_wallet()->encrypt_buffer(buff, res.res_buff);
res.res_buff = epee::string_encoding::base64_encode(res.res_buff);
return true;
WALLET_RPC_CATCH_TRY_ENTRY();

View file

@ -94,7 +94,8 @@ namespace tools
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_public::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_public::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("get_wallet_info", on_getwallet_info, wallet_public::COMMAND_RPC_GET_WALLET_INFO)
MAP_JON_RPC_WE("get_recent_txs_and_info", on_get_recent_txs_and_info, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO)
MAP_JON_RPC_WE("get_recent_txs_and_info", on_get_recent_txs_and_info, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO) //LEGACY
MAP_JON_RPC_WE("get_recent_txs_and_info2", on_get_recent_txs_and_info2, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO2)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_public::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("store", on_store, wallet_public::COMMAND_RPC_STORE)
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_public::COMMAND_RPC_GET_PAYMENTS)
@ -150,6 +151,7 @@ namespace tools
bool on_getwallet_restore_info(const wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::request& req, wallet_public::COMMAND_RPC_GET_WALLET_RESTORE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_get_seed_phrase_info(const wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::request& req, wallet_public::COMMAND_RPC_GET_SEED_PHRASE_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_get_recent_txs_and_info(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_get_recent_txs_and_info2(const wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO2::request& req, wallet_public::COMMAND_RPC_GET_RECENT_TXS_AND_INFO2::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_transfer(const wallet_public::COMMAND_RPC_TRANSFER::request& req, wallet_public::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_store(const wallet_public::COMMAND_RPC_STORE::request& req, wallet_public::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx);
bool on_get_payments(const wallet_public::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_public::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx);

View file

@ -548,7 +548,8 @@ bool wallets_manager::init_local_daemon()
//chain calls to rpc server
m_prpc_chain_handler = &m_wallet_rpc_server;
m_rpc_server.set_rpc_chain_handler(this);
//disable this until we get full support of authentication with network
//m_rpc_server.set_rpc_chain_handler(this);
LOG_PRINT_L0("Starting core rpc server...");
@ -1805,6 +1806,12 @@ std::string wallets_manager::reset_wallet_password(uint64_t wallet_id, const std
else
return API_RETURN_CODE_FAIL;
}
std::string wallets_manager::use_whitelisting(uint64_t wallet_id, bool use)
{
GET_WALLET_OPT_BY_ID(wallet_id, w);
w.w->get()->set_use_assets_whitelisting(use);
return API_RETURN_CODE_OK;
}
std::string wallets_manager::add_custom_asset_id(uint64_t wallet_id, const crypto::public_key& asset_id, currency::asset_descriptor_base& asset_descriptor)
{
GET_WALLET_OPT_BY_ID(wallet_id, w);

View file

@ -137,6 +137,7 @@ public:
std::string get_wallet_restore_info(uint64_t wallet_id, std::string& seed_phrase, const std::string& seed_password);
std::string backup_wallet(uint64_t wallet_id, const std::wstring& path);
std::string reset_wallet_password(uint64_t wallet_id, const std::string& pass);
std::string use_whitelisting(uint64_t wallet_id, bool use);
std::string is_wallet_password_valid(uint64_t wallet_id, const std::string& pass);
std::string create_ionic_swap_proposal(uint64_t wallet_id, const tools::wallet_public::create_ionic_swap_proposal_request& proposal, std::string& result_proposal_hex);
std::string get_ionic_swap_proposal_info(uint64_t wallet_id, std::string&raw_tx_template_hex, tools::wallet_public::ionic_swap_proposal_info& proposal);

View file

@ -1068,7 +1068,7 @@ bool gen_alias_too_small_reward::generate(std::vector<test_event_entry>& events)
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
DO_CALLBACK(events, "init_runtime_config");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW+20);
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx_with_many_outputs(m_hardforks, events, blk_0r, miner_acc.get_keys(), miner_acc.get_public_address(), 3 * aliases_count * TESTS_DEFAULT_FEE * 100, 3 * aliases_count, TESTS_DEFAULT_FEE, tx_1);
@ -1271,7 +1271,8 @@ bool gen_alias_switch_and_check_block_template::generate(std::vector<test_event_
uint64_t miner_amount = get_outs_money_amount(blk_0r.miner_tx, miner_acc.get_keys()) * 4;
// alice get some money
MAKE_TX_LIST(events, tx_list, miner_acc, alice, miner_amount / 2, blk_0r); // 2N+3
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, tx_list); // 2N+4
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1_, blk_0r, miner_acc, tx_list); // 2N+4
REWIND_BLOCKS_N_WITH_TIME(events, blk_1, blk_1_, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1); // 2N = CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2
tx_list.clear();
// Alice registers an alias

View file

@ -448,6 +448,16 @@ bool test_generator::build_wallets(const blockchain_vector& blockchain,
{
for (uint64_t amount : rqt.amounts)
{
uint64_t height_upper_limit_local = rqt.height_upper_limit;
if (amount == 0)
{
//for hardfork 4 we need to have at least 10 confirmations on hard rule level
//rqt.height_upper_limit > - 10
if (m_blockchain.size() < CURRENCY_HF4_MANDATORY_MIN_COINAGE)
return false;
if (height_upper_limit_local > m_blockchain.size() - CURRENCY_HF4_MANDATORY_MIN_COINAGE)
height_upper_limit_local = m_blockchain.size() - CURRENCY_HF4_MANDATORY_MIN_COINAGE;
}
rsp.outs.resize(rsp.outs.size() + 1);
auto& rsp_entry = rsp.outs.back();
rsp_entry.amount = amount;
@ -463,7 +473,7 @@ bool test_generator::build_wallets(const blockchain_vector& blockchain,
for (size_t gindex : random_mapping)
{
const out_index_info& oii = it->second[gindex];
if (rqt.height_upper_limit != 0 && oii.block_height > rqt.height_upper_limit)
if (height_upper_limit_local != 0 && oii.block_height > height_upper_limit_local)
continue;
const transaction& tx = oii.in_block_tx_index == 0 ? m_blockchain[oii.block_height]->b.miner_tx : m_blockchain[oii.block_height]->m_transactions[oii.in_block_tx_index - 1];
auto& out_v = tx.vout[oii.in_tx_out_index];
@ -1193,8 +1203,6 @@ namespace
bool init_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, const currency::account_keys& acc_keys)
{
bool r = false;
for (const block& blk : blockchain)
{
uint64_t height = get_block_height(blk);
@ -1487,6 +1495,11 @@ bool fill_tx_sources(std::vector<currency::tx_source_entry>& sources, const std:
if (unlock_time > head_block_ts + DIFFICULTY_TOTAL_TARGET)
continue;
}
}
if (blk_head.miner_tx.version >= TRANSACTION_VERSION_POST_HF4 && next_block_height - get_block_height(*oi.p_blk) < CURRENCY_HF4_MANDATORY_MIN_COINAGE)
{
//ignore outs that doesn't fit the HF4 rule
continue;
}
@ -2338,7 +2351,6 @@ bool shuffle_source_entries(std::vector<tx_source_entry>& sources)
// creates destinations.size() + 1 outputs if the total sum of amounts is less than the original premine amount (the last one will have amount = old_premine - sum)
bool replace_coinbase_in_genesis_block(const std::vector<currency::tx_destination_entry>& destinations, test_generator& generator, std::vector<test_event_entry>& events, currency::block& genesis_block)
{
bool r = false;
generator.remove_block_info(genesis_block);
events.pop_back();
@ -2478,6 +2490,7 @@ bool test_chain_unit_enchanced::configure_core(currency::core& c, size_t ev_inde
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
pc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
pc.hf4_minimum_mixins = 0;
pc.hard_forks = m_hardforks;
c.get_blockchain_storage().set_core_runtime_config(pc);
return true;

View file

@ -197,7 +197,8 @@ bool generate_and_play(const char* const genclass_name, size_t hardfork_id = SIZ
std::vector<test_event_entry> events;
bool generated = false;
bool result = false;
std::cout << ENDL << concolor::bright_white << "#TEST# >>>> " << genclass_name << " <<<<" << ENDL << ENDL;
//std::cout << ENDL << concolor::bright_white << "#TEST# >>>> " << genclass_name << " <<<<" << ENDL << ENDL;
LOG_PRINT_MAGENTA( "#TEST# >>>> " << genclass_name << " <<<<" << ENDL, LOG_LEVEL_0);
LOG_PRINT2("get_object_blobsize.log", "#TEST# " << genclass_name, LOG_LEVEL_3);
@ -231,16 +232,16 @@ bool generate_and_play(const char* const genclass_name, size_t hardfork_id = SIZ
generated = g.generate(events);
if (generated)
{
std::cout << concolor::normal << events.size() << " events generated successfully" << std::endl;
LOG_PRINT_MAGENTA( events.size() << " events generated successfully" << std::endl, LOG_LEVEL_0);
if (has_non_default_hardforks || g.get_hardforks() != tcub.get_hardforks())
{
size_t configure_core_events_count = std::count_if(events.begin(), events.end(), [](auto& ev){ return ev.type() == typeid(callback_entry) && boost::get<callback_entry>(ev).callback_name == "configure_core"; });
CHECK_AND_ASSERT_THROW_MES(configure_core_events_count != 0, "Test " << genclass_name << " has non-default hardfork settings and therefore must use 'configure_core' callback");
}
std::cout << concolor::bright_white << std::string(100, '=') << std::endl <<
LOG_PRINT_MAGENTA(std::string(100, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< start replaying events" << std::endl <<
std::string(100, '=') << concolor::normal << std::endl;
std::string(100, '=') << std::endl, LOG_LEVEL_0);
result = do_replay_events(events, g);
}
@ -263,18 +264,18 @@ bool generate_and_play(const char* const genclass_name, size_t hardfork_id = SIZ
if (result)
{
std::cout << concolor::green << std::string(100, '=') << std::endl <<
LOG_PRINT_GREEN(std::string(100, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< Succeeded" << std::endl <<
std::string(100, '=') << concolor::normal << std::endl;
std::string(100, '=') << std::endl, LOG_LEVEL_0 );
}
else
{
std::cout << concolor::red << std::string(100, '=') << std::endl <<
LOG_PRINT_RED( std::string(100, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< FAILED" << std::endl <<
std::string(100, '=') << concolor::normal << std::endl;
std::string(100, '=') << std::endl, LOG_LEVEL_0);
result = false;
}
std::cout << std::endl;
//std::cout << std::endl;
return result;
}
@ -1231,7 +1232,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(hard_fork_2_tx_payer_in_wallet);
GENERATE_AND_PLAY(hard_fork_2_tx_receiver_in_wallet);
GENERATE_AND_PLAY(hard_fork_2_tx_extra_alias_entry_in_wallet);
GENERATE_AND_PLAY(hard_fork_2_auditable_addresses_basics);
GENERATE_AND_PLAY_HF(hard_fork_2_auditable_addresses_basics, "2-*");
GENERATE_AND_PLAY(hard_fork_2_no_new_structures_before_hf);
GENERATE_AND_PLAY(hard_fork_2_awo_wallets_basic_test<true>);
GENERATE_AND_PLAY(hard_fork_2_awo_wallets_basic_test<false>);
@ -1249,7 +1250,7 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(atomic_test_altchain_simple);
GENERATE_AND_PLAY(atomic_test_check_hardfork_rules);
GENERATE_AND_PLAY(isolate_auditable_and_proof);
GENERATE_AND_PLAY_HF(isolate_auditable_and_proof, "2-*");
GENERATE_AND_PLAY(zarcanum_basic_test);

View file

@ -612,7 +612,6 @@ bool hard_fork_2_tx_extra_alias_entry_in_wallet::c1(currency::core& c, size_t ev
//------------------------------------------------------------------------------
hard_fork_2_auditable_addresses_basics::hard_fork_2_auditable_addresses_basics()
: hard_fork_2_base_test(23)
{
REGISTER_CALLBACK_METHOD(hard_fork_2_auditable_addresses_basics, c1);
}
@ -644,6 +643,8 @@ bool hard_fork_2_auditable_addresses_basics::generate(std::vector<test_event_ent
// make sure all Bob's outputs has mix_attr = 1
for (auto& out : tx_1.vout)
{
if (out.type() != typeid(tx_out_bare))
continue; // skip if we're in postzarcanum era
if (boost::get<tx_out_bare>(out).amount != MK_TEST_COINS(5))
continue; // skip change
uint8_t mix_attr = boost::get<txout_to_key>(boost::get<tx_out_bare>(out).target).mix_attr;
@ -677,6 +678,9 @@ bool hard_fork_2_auditable_addresses_basics::c1(currency::core& c, size_t ev_ind
// make sure all Bob's outputs has mix_attr = 1
for (auto& out : tx.vout)
{
if (out.type() != typeid(tx_out_bare))
continue; // skip if we're in postzarcanum era
if (boost::get<tx_out_bare>(out).amount != MK_TEST_COINS(1))
continue; // skip change
uint8_t mix_attr = boost::get<txout_to_key>(boost::get<tx_out_bare>(out).target).mix_attr;
@ -702,6 +706,9 @@ bool hard_fork_2_auditable_addresses_basics::c1(currency::core& c, size_t ev_ind
// make sure all Bob's outputs has mix_attr = 1
for (auto& out : tx.vout)
{
if (out.type() != typeid(tx_out_bare))
continue; // skip if we're in postzarcanum era
if (boost::get<tx_out_bare>(out).amount != MK_TEST_COINS(1))
continue; // skip change
uint8_t mix_attr = boost::get<txout_to_key>(boost::get<tx_out_bare>(out).target).mix_attr;

View file

@ -41,7 +41,7 @@ struct hard_fork_2_tx_extra_alias_entry_in_wallet : public hard_fork_2_base_test
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct hard_fork_2_auditable_addresses_basics : public hard_fork_2_base_test
struct hard_fork_2_auditable_addresses_basics : public wallet_test //hard_fork_2_base_test
{
hard_fork_2_auditable_addresses_basics();
bool generate(std::vector<test_event_entry>& events) const;

View file

@ -375,7 +375,7 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", adb.total_max_supply, 0, adb.total_max_supply, 0, 0, asset_id), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*carol_wlt, "Carol", MK_TEST_COINS(21), 0, MK_TEST_COINS(21), 0, 0), false, "");
size_t current_blockchain_size = c.get_current_blockchain_size();
//size_t current_blockchain_size = c.get_current_blockchain_size();
// Normal ionic swap between Alice and Bob: (Alice has only coins with explicit asset id)
// before:
@ -399,7 +399,9 @@ bool ionic_swap_exact_amounts_test::c1(currency::core& c, size_t ev_index, const
proposal_decoded_info.to_initiator == proposal_details.to_initiator &&
proposal_decoded_info.fee_paid_by_a == proposal_details.fee_paid_by_a &&
proposal_decoded_info.mixins == proposal_details.mixins,
false, "actual and decoded proposal mismatch");
false, "actual and decoded proposal mismatch \nproposal_decoded_info: "
<< epee::serialization::store_t_to_json(proposal_decoded_info) <<
"\nproposal_details" << epee::serialization::store_t_to_json(proposal_details));
currency::transaction tx_is{};
r = bob_wlt->accept_ionic_swap_proposal(proposal, tx_is);

View file

@ -716,8 +716,6 @@ bool assets_and_pos_mining::generate(std::vector<test_event_entry>& events) cons
{
// Test idea: ensure that post-HF4 Zarcanum staking functions correctly with outputs that have a nonzero asset id blinding mask (i.e., outputs with a non-explicit asset id)
bool r = false;
uint64_t ts = test_core_time::get_time();
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts);

View file

@ -3452,7 +3452,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
// (also make sure that unlocked balance is zero)
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "bob_wlt", m_bob_initial_balance + CURRENCY_BLOCK_REWARD, CURRENCY_BLOCK_REWARD + TESTS_DEFAULT_FEE, 0), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt.get(), "bob_wlt", m_bob_initial_balance + CURRENCY_BLOCK_REWARD, INVALID_BALANCE_VAL, 0), false, "");
// 2. Try to mine a PoS block and defragment some of UTXO
@ -3465,7 +3465,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde
CHECK_AND_ASSERT_MES(blocks_fetched == 2, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
// (also make sure that only two UTXOs is unlocked)
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD, CURRENCY_BLOCK_REWARD + TESTS_DEFAULT_FEE, m_single_amount * 2), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD, INVALID_BALANCE_VAL, m_single_amount * 2), false, "");
// 3. Try to mine a PoS block and defragment with huge decoy set. Make sure block is mined successfully without a defragmentation tx
@ -3479,7 +3479,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
// Alice's unlocked balance should consist only of one UTXO
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD * 2, CURRENCY_BLOCK_REWARD * 2 + TESTS_DEFAULT_FEE, m_single_amount), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD * 2, INVALID_BALANCE_VAL, m_single_amount), false, "");
// 4. Finally mine a PoS and defragment the last one unlocked UTXO
@ -3492,7 +3492,7 @@ bool packing_outputs_on_pos_minting_wallet::c1(currency::core& c, size_t ev_inde
CHECK_AND_ASSERT_MES(blocks_fetched == 1, false, "Incorrect numbers of blocks fetched: " << blocks_fetched);
// Alice's unlocked balance should be zero
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD * 3, CURRENCY_BLOCK_REWARD * 3 + TESTS_DEFAULT_FEE, 0), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt.get(), "alice_wlt", m_alice_initial_balance + CURRENCY_BLOCK_REWARD * 3, INVALID_BALANCE_VAL, 0), false, "");
return true;
}

View file

@ -67,7 +67,7 @@ bool wallet_test::check_balance(currency::core& c, size_t ev_index, const std::v
bool has_aliases = false;
w->scan_tx_pool(has_aliases);
if (!check_balance_via_wallet(*w.get(), get_test_account_name_by_id(pcb.account_index).c_str(), pcb.total_balance, pcb.mined_balance, pcb.unlocked_balance, pcb.awaiting_in, pcb.awaiting_out))
if (!check_balance_via_wallet(*w.get(), get_test_account_name_by_id(pcb.account_index).c_str(), pcb.total_balance, INVALID_BALANCE_VAL, pcb.unlocked_balance, pcb.awaiting_in, pcb.awaiting_out))
return false;
return true;

View file

@ -215,8 +215,8 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect
uint64_t mined_amount = (batches_to_Alice_count - 1) * COIN;
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*staker_benefeciary_acc_wlt, "staker_benefeciary", mined_amount, mined_amount, mined_amount), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_benefeciary_acc_wlt, "miner_benefeciary", mined_amount, mined_amount, mined_amount), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*staker_benefeciary_acc_wlt, "staker_benefeciary", mined_amount, INVALID_BALANCE_VAL, mined_amount), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*miner_benefeciary_acc_wlt, "miner_benefeciary", mined_amount, INVALID_BALANCE_VAL, mined_amount), false, "");
staker_benefeciary_acc_wlt->transfer(transfer_amount2, bob_wlt->get_account().get_public_address());
@ -234,7 +234,7 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect
bob_wlt->refresh();
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", transfer_amount2*3, UINT64_MAX, transfer_amount2*3), false, "");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", transfer_amount2*3, INVALID_BALANCE_VAL, transfer_amount2*3), false, "");
//try to make pre-zarcanum block after hardfork 4
currency::core_runtime_config rc = alice_wlt->get_core_runtime_config();
@ -367,14 +367,16 @@ bool zarcanum_gen_time_balance::generate(std::vector<test_event_entry>& events)
uint64_t bob_amount = MK_TEST_COINS(15);
MAKE_TX(events, tx_1, alice_acc, bob_acc, bob_amount, blk_1r);
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1);
MAKE_NEXT_BLOCK_TX1(events, blk_2__, blk_1r, miner_acc, tx_1);
// check Bob's balance in play time...
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(BOB_ACC_IDX, bob_amount, 0, 0, 0, 0));
REWIND_BLOCKS_N_WITH_TIME(events, blk_2, blk_2__, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// ... and in gen time
CREATE_TEST_WALLET(bob_wlt, bob_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, bob_wlt, blk_2, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, bob_wlt, blk_2, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 15);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(bob_wlt, bob_amount);
// try to construct tx with only one output (that is wrong for HF4)
@ -399,14 +401,16 @@ bool zarcanum_gen_time_balance::generate(std::vector<test_event_entry>& events)
MAKE_TX_FEE_MIX(events, tx_2, bob_acc, alice_acc, bob_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, blk_2);
CHECK_AND_ASSERT_MES(tx_2.vout.size() != 1, false, "tx_2.vout.size() = " << tx_2.vout.size());
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_acc, tx_2);
REWIND_BLOCKS_N_WITH_TIME(events, blk_4, blk_3, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_3, 2);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_4, 22);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, alice_amount - 2 * TESTS_DEFAULT_FEE);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, bob_wlt, blk_3, 1);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, bob_wlt, blk_4, 11);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(bob_wlt, 0);
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, alice_amount - 2 * TESTS_DEFAULT_FEE, 0, 0, 0, 0));
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, alice_amount - 2 * TESTS_DEFAULT_FEE, alice_amount - 2 * TESTS_DEFAULT_FEE, 0, 0, 0));
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(BOB_ACC_IDX, 0, 0, 0, 0, 0));
@ -422,8 +426,6 @@ zarcanum_pos_block_math::zarcanum_pos_block_math()
bool zarcanum_pos_block_math::generate(std::vector<test_event_entry>& events) const
{
bool r = false;
GENERATE_ACCOUNT(miner_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks
@ -602,8 +604,6 @@ zarcanum_in_alt_chain::zarcanum_in_alt_chain()
bool zarcanum_in_alt_chain::generate(std::vector<test_event_entry>& events) const
{
bool r = false;
uint64_t ts = test_core_time::get_time();
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts);
@ -628,7 +628,10 @@ bool zarcanum_in_alt_chain::generate(std::vector<test_event_entry>& events) cons
uint64_t bob_amount = COIN * 100;
MAKE_TX(events, tx_1, miner_acc, bob_acc, bob_amount, blk_3);
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner_acc, tx_1);
//make another tx just to create more decoys to fit hf4 rules of 16 decoys
account_base carol_acc; carol_acc.generate();
MAKE_TX(events, tx_1_1, miner_acc, carol_acc, bob_amount, blk_3);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3, miner_acc, std::list<transaction>({ tx_1, tx_1_1 }));
// HF4
// |
@ -712,8 +715,13 @@ bool zarcanum_in_alt_chain::c1(currency::core& c, size_t ev_index, const std::ve
uint64_t transfer_amount = COIN;
uint64_t transfer_fee = TESTS_DEFAULT_FEE * 3;
size_t nmix = 38;
bob_wlt->transfer(transfer_amount, nmix, m_accounts[ALICE_ACC_IDX].get_public_address(), transfer_fee);
size_t nmix = 36;
try {
bob_wlt->transfer(transfer_amount, nmix, m_accounts[ALICE_ACC_IDX].get_public_address(), transfer_fee);
}
catch (...)
{
}
CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 3, false, "Incorrect txs count in the pool: " << c.get_pool_transactions_count());
@ -745,8 +753,6 @@ bool zarcanum_block_with_txs::generate(std::vector<test_event_entry>& events) co
{
// Test idea: make sure Zarcanum PoS block can have txs and the sum of fees is correctly added to the block reward
bool r = false;
uint64_t ts = test_core_time::get_time();
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts);
@ -788,14 +794,15 @@ bool zarcanum_block_with_txs::generate(std::vector<test_event_entry>& events) co
//
// after HF4
//
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
MAKE_NEXT_BLOCK(events, blk_3_, blk_2, miner_acc);
MAKE_NEXT_BLOCK(events, blk_3, blk_3_, miner_acc);
DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast<size_t>(ZANO_HARDFORK_04_ZARCANUM));
MAKE_TX(events, tx_2, miner_acc, alice_acc, MK_TEST_COINS(200), blk_3);
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner_acc, tx_2);
m_alice_balance += MK_TEST_COINS(200);
REWIND_BLOCKS_N_WITH_TIME(events, blk_4r, blk_4, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
REWIND_BLOCKS_N_WITH_TIME(events, blk_4r, blk_4, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW+5);
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, m_alice_balance, m_alice_balance, mined_amount, 0, 0));
// then miner sends few coins to Bob via a tx with a big fee amount

View file

@ -1658,7 +1658,7 @@ TEST(crypto, scalar_get_bits)
for(size_t i = 0; i < 256; ++i)
ASSERT_EQ(x.get_bits(i, 0), 0);
for(size_t i = 0; i < 256; ++i)
ASSERT_EQ(x.get_bits(i, std::min(255ull, i + 65)), 0);
ASSERT_EQ(x.get_bits(i, std::min((size_t)255, i + 65)), 0);
ASSERT_EQ(x.get_bits(0, 64), x.m_u64[0]);
ASSERT_EQ(x.get_bits(64, 64), x.m_u64[1]);
@ -1695,6 +1695,32 @@ TEST(crypto, scalar_get_bits)
return true;
}
TEST(crypto, scalarmult_base_vartime)
{
auto check_for_x = [&](const scalar_t& x) -> bool {
point_t P, P2;
ge_scalarmult_base_vartime(&P.m_p3, x.m_s);
ge_scalarmult_base(&P2.m_p3, x.m_s);
return (P - P2).is_zero();
};
ASSERT_TRUE(check_for_x(c_scalar_0));
ASSERT_TRUE(check_for_x(c_scalar_1));
ASSERT_TRUE(check_for_x(c_scalar_1div8));
ASSERT_TRUE(check_for_x(c_scalar_Lm1));
ASSERT_TRUE(check_for_x(c_scalar_L));
for(size_t i = 0; i < 1000; ++i)
{
scalar_t x = scalar_t::random();
ASSERT_TRUE(check_for_x(x));
}
return true;
}
template<typename CT>
bool crypto_msm_runner(size_t N, size_t low_bits_to_clear, size_t high_bits_to_clear)
{
@ -1762,6 +1788,116 @@ TEST(crypto, msm)
}
inline std::ostream &operator <<(std::ostream &o, const crypto::ge_precomp v)
{
o << "{{";
for(size_t i = 0; i < 9; ++i)
o << v.yplusx[i] << ", ";
o << v.yplusx[9] << "}, {";
for(size_t i = 0; i < 9; ++i)
o << v.yminusx[i] << ", ";
o << v.yminusx[9] << "}, {";
for(size_t i = 0; i < 9; ++i)
o << v.xy2d[i] << ", ";
o << v.xy2d[9] << "}}";
return o;
}
bool calc_and_print_generator_precomp(const point_pc_t& generator, const char* generator_var_name)
{
precomp_data_t precomp_data = {};
construct_precomp_data(precomp_data, generator);
std::cout << " const precomp_data_t " << generator_var_name << "_precomp_data = {" << ENDL;
for(size_t i = 0; i < 32; ++i)
{
std::cout << " {" << ENDL;
for(size_t j = 0; j < 8; ++j)
std::cout << " " << precomp_data[i][j] << (j != 7 ? "," : "" ) << ENDL;
std::cout << " }" << (i != 31 ? "," : "" ) << ENDL;
}
std::cout << " };" << ENDL;
return true;
}
TEST(print, generators_precomp)
{
#define CALC_PRECOMP(G) calc_and_print_generator_precomp(G, #G)
CALC_PRECOMP(c_point_H);
CALC_PRECOMP(c_point_H2);
CALC_PRECOMP(c_point_U);
CALC_PRECOMP(c_point_X);
CALC_PRECOMP(c_point_H_plus_G);
CALC_PRECOMP(c_point_H_minus_G);
return true;
#undef CALC_PRECOMP
}
bool check_generator_precomp(const point_pc_t& generator, const char* generator_var_name)
{
point_t generator_pt = generator; // to avoid using precomputed data in scalar multiplications
point_t random_point = hash_helper_t::hp(scalar_t::random());
point_t A = generator_pt;
for(size_t i = 0; i < 32; ++i)
{
point_t B = c_point_0;
for(size_t j = 0; j < 8; ++j)
{
B += A;
// restore ge_p3 from ge_precomp using native NaCl functions...
point_t restored_pt{};
ge_p1p1 p1p1{};
ge_madd(&p1p1, &random_point.m_p3, &((*generator.m_precomp_data_p)[i][j]));
ge_p1p1_to_p3(&restored_pt.m_p3, &p1p1);
restored_pt -= random_point;
// ...and compare it with the calculated one
if (B != restored_pt)
{
std::cout << "ERROR: " << generator_var_name << ", i: " << i << ", j: " << j << ENDL;
return false;
}
}
if (i != 31)
A.modify_mul_pow_2(8);
}
std::cout << " " << std::left << std::setw(32) << generator_var_name << " OK" << ENDL;
return true;
}
TEST(crypto, generators_precomp)
{
#define CHECK_PRECOMP(G) ASSERT_TRUE(check_generator_precomp(G, #G))
CHECK_PRECOMP(c_point_H);
CHECK_PRECOMP(c_point_H2);
CHECK_PRECOMP(c_point_U);
CHECK_PRECOMP(c_point_X);
CHECK_PRECOMP(c_point_H_plus_G);
CHECK_PRECOMP(c_point_H_minus_G);
return true;
#undef CHECK_PRECOMP
}
//
// test's runner
//

View file

@ -1,5 +1,5 @@
// Copyright (c) 2022 Zano Project (https://zano.org/)
// Copyright (c) 2022 sowle (val@zano.org, crypto.sowle@gmail.com)
// Copyright (c) 2022-2024 Zano Project (https://zano.org/)
// Copyright (c) 2022-2024 sowle (val@zano.org, crypto.sowle@gmail.com)
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
@ -166,11 +166,11 @@ TEST(clsag, bad_pub_keys)
ASSERT_TRUE(cc.generate());
ASSERT_TRUE(cc.verify());
// torsion component in K1 should not affect protocol
// torsion component in K1 should break the protocol
cc = cc_orig;
ASSERT_TRUE(cc.generate());
cc.sig.K1 = (point_t(cc.sig.K1) + tor).to_public_key();
ASSERT_TRUE(cc.verify());
ASSERT_FALSE(cc.verify());
// torsion component in stealth_address for secret_index (i.e. for P = xG) must break the protocol
// 1
@ -399,9 +399,9 @@ TEST(clsag_ggx, basics)
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// CLSAG GGXG
// CLSAG GGXG (eventually not used in Zano)
//
/*
struct clsag_ggxg_sig_check_t
{
crypto::hash prefix_hash;
@ -537,7 +537,7 @@ TEST(clsag_ggxg, basics)
ASSERT_TRUE(cc.verify());
return true;
}
}*/
///////////////////////////////////////////////////////////////////////////////////////////////////
//

View file

@ -2,7 +2,7 @@
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include <numeric>
uint64_t get_bits_v1(const scalar_t& s, uint8_t bit_index_first, uint8_t bits_count)
{
@ -20,27 +20,42 @@ uint64_t get_bits_v1(const scalar_t& s, uint8_t bit_index_first, uint8_t bits_co
}
inline std::ostream &operator <<(std::ostream &o, const crypto::ge_precomp v)
TEST(crypto, ge_precomp)
{
o << "{{";
for(size_t i = 0; i < 9; ++i)
o << v.yplusx[i] << ", ";
//precomp_data_t G_precomp = {};
//construct_precomp_data(G_precomp, c_point_G);
//std::cout << "size of G_precomp: " << sizeof G_precomp << " bytes" << ENDL;
//for(size_t i = 0; i < 32; ++i)
// for(size_t j = 0; j < 8; ++j)
// std::cout << "i: " << i << ", j: " << j << ", precomp: " << ENDL << G_precomp[i][j] << ENDL;
o << v.yplusx[9] << "},\n {";
for(size_t i = 0; i < 9; ++i)
o << v.yminusx[i] << ", ";
o << v.yminusx[9] << "},\n {";
for(size_t i = 0; i < 9; ++i)
o << v.xy2d[i] << ", ";
o << v.xy2d[9] << "}}\n";
return o;
precomp_data_t H_precomp = {};
construct_precomp_data(H_precomp, c_point_H);
auto check_for_x = [&](const scalar_t& x) -> bool {
point_t P;
ge_scalarmult_precomp_vartime(&P.m_p3, H_precomp, x.m_s);
return P == x * c_point_H;
};
ASSERT_TRUE(check_for_x(c_scalar_0));
ASSERT_TRUE(check_for_x(c_scalar_1));
ASSERT_TRUE(check_for_x(c_scalar_1div8));
ASSERT_TRUE(check_for_x(c_scalar_Lm1));
ASSERT_TRUE(check_for_x(c_scalar_L));
for(size_t i = 0; i < 1000; ++i)
{
scalar_t x = scalar_t::random();
ASSERT_TRUE(check_for_x(x));
}
return true;
}
TEST(perf, primitives)
{
struct helper
@ -315,7 +330,7 @@ TEST(perf, primitives)
return HASH_64_VEC(points_cached);
});
run("ge_add(p3 + p3)", 50000, [](timer_t& t, size_t rounds) {
run("ge_add(p1p1 = p3 + cached)", 50000, [](timer_t& t, size_t rounds) {
std::vector<size_t> rnd_indecies;
helper::make_rnd_indicies(rnd_indecies, rounds);
std::vector<ge_cached> points_cached(rounds);
@ -530,7 +545,7 @@ TEST(perf, primitives)
return HASH_64_VEC(points_p3);
});
run("ge_scalarmult_base()", 5000, [](timer_t& t, size_t rounds) {
run("ge_scalarmult_base()", 10000, [](timer_t& t, size_t rounds) {
std::vector<size_t> rnd_indecies;
helper::make_rnd_indicies(rnd_indecies, rounds);
@ -555,6 +570,87 @@ TEST(perf, primitives)
return HASH_64_VEC(points_p3);
});
run("construct_precomp_data()", 300, [](timer_t& t, size_t rounds) {
std::vector<size_t> rnd_indecies;
helper::make_rnd_indicies(rnd_indecies, rounds);
unsigned char s[32] = {};
std::vector<point_t> random_points(rounds);
for (size_t i = 0; i < rounds; ++i)
{
s[0] = i;
ge_p2 p2;
ge_fromfe_frombytes_vartime(&p2, s);
ge_p2_to_p3(&random_points[i].m_p3, &p2);
}
std::vector<ge_p3> points_p3(rounds);
precomp_data_t precomp_data;
uint64_t result = 0;
t.start();
for (size_t i = 0; i < rounds; ++i)
{
construct_precomp_data(precomp_data, random_points[rnd_indecies[i]]);
result ^= (precomp_data[1][1].xy2d[1] + precomp_data[31][7].xy2d[9]);
}
t.stop();
return result;
});
run("ge_scalarmult_precomp_vartime()", 10000, [](timer_t& t, size_t rounds) {
std::vector<size_t> rnd_indecies;
helper::make_rnd_indicies(rnd_indecies, rounds);
scalar_t x;
x.make_random();
std::vector<crypto::ec_scalar> scalars(rounds);
for (size_t i = 0; i < rounds; ++i)
{
scalar_t x = x + x + x;
memcpy(&scalars[i].data, x.data(), 32);
}
precomp_data_t precomp_data;
construct_precomp_data(precomp_data, x * c_point_X);
std::vector<ge_p3> points_p3(rounds);
t.start();
for (size_t i = 0; i < rounds; ++i)
{
ge_scalarmult_precomp_vartime(&points_p3[i], precomp_data, (const unsigned char*)&scalars[rnd_indecies[i]]);
}
t.stop();
return HASH_64_VEC(points_p3);
});
run("ge_scalarmult_base_vartime()", 10000, [](timer_t& t, size_t rounds) {
std::vector<size_t> rnd_indecies;
helper::make_rnd_indicies(rnd_indecies, rounds);
scalar_t x;
x.make_random();
std::vector<crypto::ec_scalar> scalars(rounds);
for (size_t i = 0; i < rounds; ++i)
{
scalar_t x = x + x + x;
memcpy(&scalars[i].data, x.data(), 32);
}
std::vector<ge_p3> points_p3(rounds);
t.start();
for (size_t i = 0; i < rounds; ++i)
{
ge_scalarmult_base_vartime(&points_p3[i], (const unsigned char*)&scalars[rnd_indecies[i]]);
}
t.stop();
return HASH_64_VEC(points_p3);
});
run("ge_mul8_p3()", 5000, [](timer_t& t, size_t rounds) {
std::vector<size_t> rnd_indecies;
helper::make_rnd_indicies(rnd_indecies, rounds);
@ -738,5 +834,402 @@ TEST(perf, primitives)
return HASH_64_VEC(result);
});
run("get_bits x 10", 20000, [](timer_t& t, size_t rounds) {
std::vector<size_t> rnd_indecies;
helper::make_rnd_indicies(rnd_indecies, rounds);
scalar_vec_t data;
data.resize_and_make_random(rounds);
std::vector<uint64_t> result(rounds);
t.start();
for (size_t i = 0; i < rounds; ++i)
{
auto& x = data[rnd_indecies[i]];
result[i] =
x.get_bits(x.m_s[11], x.m_s[21] % 65) ^
x.get_bits(x.m_s[12], x.m_s[22] % 65) ^
x.get_bits(x.m_s[13], x.m_s[23] % 65) ^
x.get_bits(x.m_s[14], x.m_s[24] % 65) ^
x.get_bits(x.m_s[15], x.m_s[25] % 65) ^
x.get_bits(x.m_s[16], x.m_s[26] % 65) ^
x.get_bits(x.m_s[17], x.m_s[27] % 65) ^
x.get_bits(x.m_s[18], x.m_s[28] % 65) ^
x.get_bits(x.m_s[19], x.m_s[29] % 65) ^
x.get_bits(x.m_s[20], x.m_s[30] % 65);
}
t.stop();
return HASH_64_VEC(result);
});
return true;
} // TEST
////////////////////////////////////////////////////////////////////////////////
///////////////// v3
///////////////// v4
template<typename CT>
bool msm_and_check_zero_pippenger_v4(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand, size_t c)
{
// TODO: with c = 8 and with direct access got much worse result than with c = 7 and get_bits(), consider checking again for bigger datasets (N>256)
CHECK_AND_ASSERT_MES(g_scalars.size() <= CT::c_bpp_mn_max, false, "g_scalars oversized");
CHECK_AND_ASSERT_MES(h_scalars.size() <= CT::c_bpp_mn_max, false, "h_scalars oversized");
CHECK_AND_ASSERT_MES(c < 10, false, "c is too big");
size_t C = 1ull << c;
// k_max * c + (c-1) >= max_bit_idx
//
// max_bit_idx - (c - 1) max_bit_idx - (c - 1) + (c - 1) max_bit_idx
// k_max = ceil ( --------------------- ) = floor ( ------------------------------ ) = floor ( ----------- )
// c c c
const size_t b = 253; // the maximum number of bits in x https://eprint.iacr.org/2022/999.pdf TODO: we may also scan for maximum bit used in all the scalars if all the scalars are small
const size_t max_bit_idx = b - 1;
const size_t k_max = max_bit_idx / c;
const size_t K = k_max + 1;
std::unique_ptr<point_t[]> buckets( new point_t[C * K] );
std::vector<bool> buckets_inited(C * K);
// first loop, calculate partial bucket sums
for (size_t n = 0; n < g_scalars.size(); ++n)
{
for (size_t k = 0; k < K; ++k)
{
uint64_t l = g_scalars[n].get_bits(k * c, c); // l in [0; 2^c-1]
if (l != 0)
{
size_t bucket_id = l * K + k;
if (buckets_inited[bucket_id])
buckets[bucket_id] += CT::get_generator(false, n);
else
{
buckets[bucket_id] = CT::get_generator(false, n);
buckets_inited[bucket_id] = true;
}
}
}
}
for (size_t n = 0; n < h_scalars.size(); ++n)
{
for (size_t k = 0; k < K; ++k)
{
uint64_t l = h_scalars[n].get_bits(k * c, c); // l in [0; 2^c-1]
if (l != 0)
{
size_t bucket_id = l * K + k;
if (buckets_inited[bucket_id])
buckets[bucket_id] += CT::get_generator(true, n);
else
{
buckets[bucket_id] = CT::get_generator(true, n);
buckets_inited[bucket_id] = true;
}
}
}
}
// the second loop
// S[l, k] = S[l-1, k] + B[l, k]
// G[k] = sum{1..C-1} S[l, k]
std::unique_ptr<point_t[]> Sk( new point_t[K] );
std::vector<bool> Sk_inited(K);
std::unique_ptr<point_t[]> Gk( new point_t[K] );
std::vector<bool> Gk_inited(K);
for (size_t l = C - 1; l > 0; --l)
{
for (size_t k = 0; k < K; ++k)
{
size_t bucket_id = l * K + k;
if (buckets_inited[bucket_id])
{
if (Sk_inited[k])
Sk[k] += buckets[bucket_id];
else
{
Sk[k] = buckets[bucket_id];
Sk_inited[k] = true;
}
}
if (Sk_inited[k])
{
if (Gk_inited[k])
Gk[k] += Sk[k];
else
{
Gk[k] = Sk[k];
Gk_inited[k] = true;
}
}
}
}
// the third loop: Horners rule
point_t result = Gk_inited[K - 1] ? Gk[K - 1] : c_point_0;
for (size_t k = K - 2; k != SIZE_MAX; --k)
{
result.modify_mul_pow_2(c);
if (Gk_inited[k])
result += Gk[k];
}
result += summand;
if (!result.is_zero())
{
LOG_PRINT_L0("msm result is non zero: " << result);
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
//template<typename CT>
//struct mes_msm_and_check_zero_pippenger_v1
//{
// static bool msm_and_check_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand, size_t c)
// {
// return msm_and_check_zero_pippenger_v1<CT>(g_scalars, h_scalars, summand, c);
// }
//};
//
//template<typename CT>
//struct mes_msm_and_check_zero_pippenger_v2
//{
// static bool msm_and_check_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand, size_t c)
// {
// return msm_and_check_zero_pippenger_v2<CT>(g_scalars, h_scalars, summand, c);
// }
//};
template<typename CT>
struct mes_msm_and_check_zero_pippenger_v3
{
static bool msm_and_check_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand, size_t c)
{
return msm_and_check_zero_pippenger_v3<CT>(g_scalars, h_scalars, summand, c);
}
};
template<typename CT>
struct mes_msm_and_check_zero_pippenger_v4
{
static bool msm_and_check_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand, size_t c)
{
return msm_and_check_zero_pippenger_v4<CT>(g_scalars, h_scalars, summand, c);
}
};
struct pme_runner_i
{
virtual ~pme_runner_i() {}
virtual bool iteration(bool warmup) = 0;
};
template<size_t N, typename CT, template<typename> typename selector_t>
struct pme_runner_t : public pme_runner_i
{
pme_runner_t(const char* testname_, size_t pip_partition_bits_c)
: testname(testname_)
, pip_partition_bits_c(pip_partition_bits_c)
{
testname += std::string(", ") + std::string(typeid(selector_t<CT>).name()).erase(0, 11) + std::string(", c = ") + epee::string_tools::num_to_string_fast(pip_partition_bits_c);
std::cout << testname << ENDL;
}
virtual ~pme_runner_t()
{
if (timings.empty())
return;
uint64_t median = 0;
auto median_it = timings.begin() + timings.size() / 2;
std::nth_element(timings.begin(), median_it, timings.end());
median = *median_it;
if (timings.size() % 2 == 0)
{
auto max_it = std::max_element(timings.begin(), median_it);
median = (median + *max_it) / 2;
}
uint64_t total_time = std::accumulate(timings.begin(), timings.end(), 0);
std::cout << std::left << std::setw(100) << testname << " : " << std::setw(5) << median << " (median), " << std::setw(5) << total_time / timings.size() << " (avg), mcs" << ENDL;
}
virtual bool iteration(bool warmup)
{
scalar_vec_t g_scalars, h_scalars;
g_scalars.resize_and_make_random(N);
g_scalars[0] = c_scalar_Lm1;
//std::cout << "bit 251: " << g_scalars[0].get_bit(251) << ", bit 252: " << g_scalars[0].get_bit(252) << ENDL;
h_scalars.resize_and_make_random(N);
point_t sum = c_point_0;
for(size_t i = 0; i < N; ++i)
{
//g_scalars[i].m_u64[3] = 0;
//h_scalars[i].m_u64[3] = 0;
//g_scalars[i].m_s[31] = 0;
//h_scalars[i].m_s[31] = 0;
sum += g_scalars[i] * CT::get_generator(false, i) + h_scalars[i] * CT::get_generator(true, i);
}
TIME_MEASURE_START(t);
bool r = selector_t<CT>::msm_and_check_zero(g_scalars, h_scalars, -sum, pip_partition_bits_c);
TIME_MEASURE_FINISH(t);
ASSERT_TRUE(r);
if (!warmup)
timings.push_back(t);
return true;
}
std::vector<uint64_t> timings;
std::string testname;
size_t pip_partition_bits_c;
};
TEST(perf, msm)
{
bool r = false;
std::deque<std::unique_ptr<pme_runner_i>> runners;
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 1));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 2));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 3));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 4));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 5));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 6));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 7));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 8));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 9));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 1));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 2));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 3));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 4));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 5));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 6));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 7));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 8));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 9));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 1));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 2));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 3));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 4));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 5));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 6));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 7));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 8));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v2> >("Zarcanum, BPPE, 128", 9));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 1));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 2));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 3));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 4));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 5));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 6));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 7));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 8));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v2> >("ZC out, BPP, 256", 9));
runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v3> >("Zarcanum, BPPE, 128 +++++++++++", 7));
runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v3> >("ZC out, BPP, 256 +++++++++++", 7));
runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v4> >("Zarcanum, BPPE, 128 ###########", 7));
runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v4> >("ZC out, BPP, 256 ###########", 7));
//runners.emplace_front(std::make_unique< pme_runner_t<128, bpp_crypto_trait_Zarcanum, mes_msm_and_check_zero_pippenger_v1> >("Zarcanum, BPPE, 128", 7));
//runners.emplace_front(std::make_unique< pme_runner_t<256, bpp_crypto_trait_ZC_out, mes_msm_and_check_zero_pippenger_v1> >("ZC out, BPP, 256", 7));
std::cout << "warm up..." << ENDL;
size_t runs_count = 30;
for(size_t k = 0; k < runs_count; ++k)
{
for(auto& runner : runners)
ASSERT_TRUE(runner->iteration(true));
}
runs_count = 200;
for(size_t k = 0; k < runs_count; ++k)
{
for(auto& runner : runners)
ASSERT_TRUE(runner->iteration(false));
size_t done_percent = 100 * k / runs_count;
if (100 * (k + 1) / runs_count > done_percent && done_percent % 5 == 0)
std::cout << done_percent << " %" << ENDL;
}
return true;
}
template<typename T>
bool perf_generators_runner(const T& generator, const char* title)
{
const size_t warmup_rounds = 20;
const size_t rounds = 500;
const size_t inner_rounds = 128;
uint64_t h = 0;
std::vector<uint64_t> timings;
size_t N = 1024;
scalar_vec_t scalars;
scalars.resize_and_make_random(N);
std::vector<point_t> points(N);
for(size_t i = 0; i < warmup_rounds; ++i)
for(size_t j = 0; j < inner_rounds; ++j)
points[(i + j) % N] = scalars[(i + j) % N] * generator;
h = hash_64(points.data(), points.size() * sizeof(point_t));
for(size_t i = 0; i < rounds; ++i)
{
TIME_MEASURE_START(t);
for(size_t j = 0; j < inner_rounds; ++j)
points[(i + j) % N] = scalars[(i + j) % N] * generator;
TIME_MEASURE_FINISH(t);
timings.push_back(t);
}
h ^= hash_64(points.data(), points.size() * sizeof(point_t));
std::cout << std::left << std::setw(20) << title << " : " << std::setw(5) << std::fixed << std::setprecision(1) << (double)epee::misc_utils::median(timings) / inner_rounds << " mcs, hash = " << h << ENDL;
return true;
}
TEST(perf, generators)
{
#define TEST_GENERATOR(G) ASSERT_TRUE(perf_generators_runner(G, #G))
TEST_GENERATOR(c_point_0);
TEST_GENERATOR(c_point_G);
TEST_GENERATOR(c_point_H);
TEST_GENERATOR(c_point_H2);
TEST_GENERATOR(c_point_U);
TEST_GENERATOR(c_point_X);
TEST_GENERATOR(c_point_H_plus_G);
TEST_GENERATOR(c_point_H_minus_G);
return true;
}

View file

@ -0,0 +1,43 @@
// Copyright (c) 2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <algorithm>
#include "gtest/gtest.h"
#include "wallet/decoy_selection.h"
TEST(decoy_selection_test, decoy_selection_test)
{
const uint64_t test_scale_size = 20000;
decoy_selection_generator dsg;
dsg.init(test_scale_size - 1);
std::map<uint64_t, uint64_t> hits;
//std::vector<uint64_t> hits(test_scale_size, 0);
// while (true)
// {
// std::vector<uint64_t> decoys = dsg.generate_distribution(15);
// for (auto d : decoys)
// {
// hits[d]++;
// }
//
// if (hits[10] > 500)
// break;
//
// }
// std::stringstream ss;
// for (auto it = hits.begin(); it != hits.end(); it++)
// {
// //if (hits[i] != 0)
// {
// ss << it->first << ", " << it->second << ENDL;
// }
// }
// epee::file_io_utils::save_string_to_file("distribution.csv", ss.str());
}

View file

@ -69,8 +69,81 @@ TEST(fork_choice_rule_test, fork_choice_rule_test_1)
ASSERT_FALSE(res);
res = if_alt_chain_stronger(200000, 14000);
ASSERT_TRUE(res);
}
bool if_alt_chain_stronger_hf4(const currency::wide_difficulty_type& pos, const currency::wide_difficulty_type& pow)
{
currency::difficulties main_cumul_diff;
main_cumul_diff.pos_diff.assign("1605973467987652534120344647");
main_cumul_diff.pow_diff.assign("3011264554002844981");
currency::difficulties alt_cumul_diff;
alt_cumul_diff.pow_diff = pow;
alt_cumul_diff.pos_diff = pos;
currency::wide_difficulty_type difficulty_pos_at_split_point = main_cumul_diff.pos_diff;
currency::wide_difficulty_type difficulty_pow_at_split_point = main_cumul_diff.pow_diff;
boost::multiprecision::uint1024_t main = currency::get_a_to_b_relative_cumulative_difficulty_hf4(difficulty_pos_at_split_point, difficulty_pow_at_split_point, main_cumul_diff, alt_cumul_diff);
boost::multiprecision::uint1024_t alt = currency::get_a_to_b_relative_cumulative_difficulty_hf4(difficulty_pos_at_split_point, difficulty_pow_at_split_point, alt_cumul_diff, main_cumul_diff);
if (alt > main)
return true;
return false;
}
TEST(fork_choice_rule_test, fork_choice_rule_test_hf4)
{
std::stringstream ss;
currency::wide_difficulty_type pos_start, pos_end, pos_step, pos_diveder;
pos_start.assign("16059734679876525341203446");
pos_end.assign ("16059734679876525341203446400");
pos_step.assign ("50000000000000000000000000");
pos_diveder.assign("100000000000000000000000");
currency::wide_difficulty_type pow_start, pow_end, pow_step, pow_diveder;
pow_start.assign("301126455400284498");
pow_end.assign ("30112645540028449810");
pow_step.assign ("500000000000000000");
pow_diveder.assign("1000000000000000");
for (currency::wide_difficulty_type pos = pos_start; pos < pos_end; pos += pos_step)
{
for (currency::wide_difficulty_type pow = pow_start; pow < pow_end; pow += pow_step)
{
bool r = if_alt_chain_stronger_hf4(pos, pow);
if(r)
ss << pos/ pos_diveder << "\t" << pow / pow_diveder << std::endl;
//ss << pos << "\t" << pow << "\t" << (r ? "1" : "0") << std::endl;
}
}
bool r = epee::file_io_utils::save_string_to_file("stat_hf4.txt", ss.str());
bool res = false;
res = if_alt_chain_stronger_hf4(1000000, 1000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger_hf4(1000000, 1500);
ASSERT_TRUE(res);
res = if_alt_chain_stronger_hf4(800000, 1700);
ASSERT_FALSE(res);
res = if_alt_chain_stronger_hf4(800000, 2000);
ASSERT_TRUE(res);
res = if_alt_chain_stronger_hf4(600000, 2200);
ASSERT_FALSE(res);
res = if_alt_chain_stronger_hf4(600000, 2800);
ASSERT_TRUE(res);
res = if_alt_chain_stronger_hf4(400000, 3999);
ASSERT_FALSE(res);
res = if_alt_chain_stronger_hf4(400000, 4001);
ASSERT_TRUE(res);
res = if_alt_chain_stronger_hf4(200000, 7000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger_hf4(200000, 7700);
ASSERT_TRUE(res);
res = if_alt_chain_stronger_hf4(200000, 7000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger_hf4(200000, 7700);
ASSERT_TRUE(res);
res = if_alt_chain_stronger_hf4(100000, 10000);
ASSERT_FALSE(res);
res = if_alt_chain_stronger_hf4(200000, 14000);
ASSERT_TRUE(res);
}