1
0
Fork 0
forked from lthn/blockchain

merge from zarcanum

This commit is contained in:
cryptozoidberg 2022-11-15 21:14:13 +01:00
commit c9255c33b8
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
47 changed files with 1888 additions and 899 deletions

View file

@ -94,6 +94,27 @@ 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()
};
} // namespace crypto
BLOB_SERIALIZER(crypto::chacha8_iv);

View file

@ -9,12 +9,15 @@
//#include "misc_log_ex.h"
#include "../currency_core/crypto_config.h"
DISABLE_GCC_AND_CLANG_WARNING(unused-function)
namespace crypto
{
#define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << std::endl
#define DBG_PRINT(x) (void(0)) // std::cout << x << std::endl
//static std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return o << pod_to_hex(v); }
static std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return o << pod_to_hex(v); }
static std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return o << pod_to_hex(v); }
bool generate_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki,
const scalar_t& secret_x, const scalar_t& secret_f, uint64_t secret_index, CLSAG_GG_signature& sig)
@ -183,7 +186,14 @@ namespace crypto
// calculate key images
point_t ki_base = hash_helper_t::hp(ring[secret_index].stealth_address);
point_t key_image = secret_0_xp * ki_base;
#ifndef NDEBUG
CRYPTO_CHECK_AND_THROW_MES(key_image == point_t(ki), "key image 0 mismatch");
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_3_q * c_point_G == 8 * point_t(ring[secret_index].concealing_point), "secret_3_q mismatch");
CRYPTO_CHECK_AND_THROW_MES(secret_2_x * c_point_X == extended_amount_commitment - 8 * point_t(ring[secret_index].amount_commitment) - 8 * point_t(ring[secret_index].concealing_point), "secret_3_q mismatch");
#endif
point_t K1_div8 = (c_scalar_1div8 * secret_1_f) * ki_base;
K1_div8.to_public_key(sig.K1);
@ -208,6 +218,7 @@ namespace crypto
hsc.add_pub_key(ring[i].stealth_address);
hsc.add_pub_key(ring[i].amount_commitment);
hsc.add_pub_key(ring[i].concealing_point);
DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", cp:" << ring[i].concealing_point);
}
hsc.add_point(c_scalar_1div8 * pseudo_out_amount_commitment);
hsc.add_point(c_scalar_1div8 * extended_amount_commitment);
@ -282,21 +293,33 @@ namespace crypto
// 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;
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)
point_t W_key_image_x = agg_coeff_2 * K2;
DBG_VAL_PRINT(K2);
DBG_VAL_PRINT(W_key_image_x);
#ifndef NDEBUG
CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * c_point_G == W_pub_keys_g[secret_index], "aggregated secret G and pub key mismatch");
CRYPTO_CHECK_AND_THROW_MES(w_sec_key_g * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_g, "aggregated secret G and key image mismatch");
CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * c_point_X == W_pub_keys_x[secret_index], "aggregated secret X and pub key mismatch");
CRYPTO_CHECK_AND_THROW_MES(w_sec_key_x * hash_helper_t::hp(ring[secret_index].stealth_address) == W_key_image_x, "aggregated secret X and key image mismatch");
#endif
// initial commitment
scalar_t alpha_g = scalar_t::random(); // randomness for layers 0,1,3
scalar_t alpha_x = scalar_t::random(); // randomness for layer 2
hsc.add_32_chars(CRYPTO_HDS_CLSAG_GGXG_CHALLENGE);
hsc.add_hash(input_hash);
hsc.add_point(alpha_g * c_point_G); DBG_VAL_PRINT(alpha_g * c_point_G);
hsc.add_point(alpha_g * ki_base); DBG_VAL_PRINT(alpha_g * ki_base);
hsc.add_point(alpha_x * c_point_X); DBG_VAL_PRINT(alpha_x * c_point_X);
hsc.add_point(alpha_x * ki_base); DBG_VAL_PRINT(alpha_x * ki_base);
hsc.add_point(alpha_g * c_point_G);
hsc.add_point(alpha_g * ki_base);
hsc.add_point(alpha_x * c_point_X);
hsc.add_point(alpha_x * ki_base);
//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();
@ -334,8 +357,8 @@ 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)
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)
{
DBG_PRINT("== verify_CLSAG_GGXG ==");
size_t ring_size = ring.size();
@ -360,6 +383,7 @@ namespace crypto
hsc.add_pub_key(ring[i].stealth_address);
hsc.add_pub_key(ring[i].amount_commitment);
hsc.add_pub_key(ring[i].concealing_point);
DBG_PRINT("ring[" << i << "]: sa:" << ring[i].stealth_address << ", ac:" << ring[i].amount_commitment << ", cp:" << ring[i].concealing_point);
}
hsc.add_pub_key(pseudo_out_amount_commitment);
hsc.add_pub_key(extended_amount_commitment);
@ -429,11 +453,14 @@ namespace crypto
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)
point_t W_key_image_x =
agg_coeff_2 * point_t(sig.K2).modify_mul8();
DBG_VAL_PRINT(point_t(sig.K2).modify_mul8());
DBG_VAL_PRINT(W_key_image_x);
@ -449,6 +476,7 @@ namespace crypto
hsc.add_point(sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x);
c_prev = hsc.calc_hash(); // c_{i + 1}
DBG_PRINT("c[" << i + 1 << "] = " << c_prev);
//DBG_PRINT("c[" << i + 1 << "] = Hs(ih, " << sig.r_g[i] * c_point_G + c_prev * W_pub_keys_g[i] << ", " << sig.r_g[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_g << ", " << sig.r_x[i] * c_point_X + c_prev * W_pub_keys_x[i] << ", " << sig.r_x[i] * hash_helper_t::hp(ring[i].stealth_address) + c_prev * W_key_image_x << ")");
}
return c_prev == sig.c;

View file

@ -32,15 +32,18 @@ namespace crypto
struct CLSAG_GG_input_ref_t
{
CLSAG_GG_input_ref_t(const public_key& stealth_address, const public_key& amount_commitment)
: stealth_address(stealth_address), amount_commitment(amount_commitment) {}
: stealth_address(stealth_address), amount_commitment(amount_commitment)
{}
const public_key& stealth_address; // not premultiplied by 1/8, TODO @#@#: make sure it's okay
const public_key& amount_commitment; // multiplied by 1/8
const public_key& stealth_address; // P, not premultiplied by 1/8, TODO @#@#: make sure it's okay
const public_key& amount_commitment; // A, premultiplied by 1/8
};
// pseudo_out_amount_commitment -- not premultiplied by 1/8
bool generate_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki,
const scalar_t& secret_x, const scalar_t& secret_f, uint64_t secret_index, CLSAG_GG_signature& sig);
// pseudo_out_amount_commitment -- premultiplied by 1/8
bool verify_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const public_key& pseudo_out_amount_commitment, const key_image& ki,
const CLSAG_GG_signature& sig);
@ -71,10 +74,15 @@ namespace crypto
const public_key& concealing_point; // Q, premultiplied by 1/8
};
// pseudo_out_amount_commitment -- not premultiplied by 1/8
// extended_amount_commitment -- not premultiplied by 1/8
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);
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);
// pseudo_out_amount_commitment -- premultiplied by 1/8
// extended_amount_commitment -- premultiplied by 1/8
// may throw an exception TODO @#@# make sure it's okay
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);
} // namespace crypto

View file

@ -137,15 +137,15 @@ namespace crypto
const point_t& bpp_crypto_trait_zano<N, values_max>::bpp_H = c_point_G;
template<size_t N, size_t values_max>
const point_t& bpp_crypto_trait_zano<N, values_max>::bpp_H2 = c_point_H2;
const point_t& bpp_crypto_trait_zano<N, values_max>::bpp_H2 = c_point_X;
// efficient multiexponentiation (naive stub implementation atm, TODO)
template<typename CT>
bool multiexp_and_check_being_zero(const scalar_vec_t& g_scalars, const scalar_vec_t& h_scalars, const point_t& summand)
{
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(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");
point_t result = summand;

View file

@ -45,9 +45,8 @@ namespace crypto
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("zarcanum_generate_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment,
bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector<CLSAG_GGXG_input_ref_t>& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
zarcanum_proof& result, uint8_t* p_err /* = nullptr */)
@ -117,7 +116,7 @@ namespace crypto
const scalar_vec_t masks2 = { bx }; // X component
const std::vector<const public_key*> E_1div8_vec_ptr = { &result.E };
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen<bpp_crypto_trait_zano<>>(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10);
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen<bpp_crypto_trait_zano<128>>(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10);
// = four-layers ring signature data outline =
// (j in [0, ring_size-1])
@ -144,6 +143,10 @@ namespace crypto
// Q[j]
// layer 3 secret (with respect to G)
// secret_q
point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G;
result.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
TRY_ENTRY()
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXG(m, ring, pseudo_out_amount_commitment, C, stake_ki,
secret_x, stake_blinding_mask - pseudo_out_blinding_mask, x0, secret_q, secret_index,
@ -152,11 +155,29 @@ namespace crypto
return true;
}
#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
bool zarcanum_verify_proof(const hash& kernel_hash, const public_key& commitment_1div8, const scalar_t& last_pow_block_id_hashed, const zarcanum_proof& proof, uint8_t* p_err /* = nullptr */)
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("zarcanum_verify_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector<CLSAG_GGXG_input_ref_t>& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const zarcanum_proof& sig, uint8_t* p_err /* = nullptr */)
{
return false;
bool r = false;
// TODO @#@#
std::vector<point_t> E_for_range_proof = { point_t(sig.E) };
std::vector<bppe_sig_commit_ref_t> range_proofs = { bppe_sig_commit_ref_t(sig.E_range_proof, E_for_range_proof) };
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_verify<bpp_crypto_trait_zano<128>>(range_proofs), 10);
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(verify_CLSAG_GGXG(m, ring, sig.pseudo_out_amount_commitment, sig.C, stake_ki, sig.clsag_ggxg), 1);
return true;
}

View file

@ -44,12 +44,14 @@ namespace crypto
CLSAG_GGXG_signature clsag_ggxg;
};
bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment,
bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXG_input_ref_t>& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
zarcanum_proof& result, uint8_t* p_err = nullptr);
bool zarcanum_verify_proof(const hash& kernel_hash, const public_key& commitment_1div8, const scalar_t& last_pow_block_id_hashed, const zarcanum_proof& proof, uint8_t* p_err = nullptr);
bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXG_input_ref_t>& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const zarcanum_proof& sig, uint8_t* p_err = nullptr);
} // namespace crypto

View file

@ -37,6 +37,7 @@
#include "tx_semantic_validation.h"
#include "crypto/RIPEMD160_helper.h"
#include "crypto/bitcoin/sha256_helper.h"
#include "crypto_config.h"
#undef LOG_DEFAULT_CHANNEL
@ -1280,7 +1281,10 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t
}
if (pos)
{
CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key), false, "coinstake transaction in the block has the wrong type");
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms
CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_zc_input), false, "coinstake tx has incorrect type of input #1: " << b.miner_tx.vin[1].type().name());
else
CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key), false, "coinstake tx has incorrect type of input #1: " << b.miner_tx.vin[1].type().name());
}
if (m_core_runtime_config.is_hardfork_active_for_height(1, height))
@ -1315,11 +1319,19 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t
return false;
}
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM))
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) // TODO @#@# consider moving to validate_tx_for_hardfork_specific_terms
{
CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 2, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2");
CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)");
CHECK_AND_ASSERT_MES(b.miner_tx.attachment[1].type() == typeid(zc_balance_proof), false, "coinbase transaction wrong attachmenttype #1 (expected: zc_balance_proof)");
if (pos)
{
CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 1, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2");
CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)");
}
else
{
CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 2, false, "coinbase transaction has incorrect number of attachments (" << b.miner_tx.attachment.size() << "), expected 2");
CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zc_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zc_outs_range_proof)");
CHECK_AND_ASSERT_MES(b.miner_tx.attachment[1].type() == typeid(zc_balance_proof), false, "coinbase transaction wrong attachmenttype #1 (expected: zc_balance_proof)");
}
}
else
{
@ -1394,24 +1406,25 @@ uint64_t blockchain_storage::get_current_comulative_blocksize_limit() const
return m_db_current_block_cumul_sz_limit;
}
//------------------------------------------------------------------
bool blockchain_storage::create_block_template(block& b,
const account_public_address& miner_address,
bool blockchain_storage::create_block_template(const account_public_address& miner_address,
const blobdata& ex_nonce,
block& b,
wide_difficulty_type& diffic,
uint64_t& height,
const blobdata& ex_nonce) const
uint64_t& height) const
{
return create_block_template(b, miner_address, miner_address, diffic, height, ex_nonce, false, pos_entry());
return create_block_template(miner_address, miner_address, ex_nonce, false, pos_entry(), nullptr, b, diffic, height);
}
//------------------------------------------------------------------
bool blockchain_storage::create_block_template(block& b,
const account_public_address& miner_address,
bool blockchain_storage::create_block_template(const account_public_address& miner_address,
const account_public_address& stakeholder_address,
wide_difficulty_type& diffic,
uint64_t& height,
const blobdata& ex_nonce,
bool pos,
const blobdata& ex_nonce,
bool pos,
const pos_entry& pe,
fill_block_template_func_t custom_fill_block_template_func /* = nullptr */) const
fill_block_template_func_t custom_fill_block_template_func,
block& b,
wide_difficulty_type& diffic,
uint64_t& height,
crypto::scalar_t* blinding_mask_sum_ptr /* = nullptr */) const
{
create_block_template_params params = AUTO_VAL_INIT(params);
params.miner_address = miner_address;
@ -1422,9 +1435,12 @@ bool blockchain_storage::create_block_template(block& b,
params.pcustom_fill_block_template_func = custom_fill_block_template_func;
create_block_template_response resp = AUTO_VAL_INIT(resp);
bool r = create_block_template(params, resp);
b = resp.b;
diffic = resp.diffic;
height = resp.height;
height = resp.height;
if (blinding_mask_sum_ptr)
*blinding_mask_sum_ptr = resp.blinding_mask_sum;
return r;
}
@ -1508,7 +1524,8 @@ bool blockchain_storage::create_block_template(const create_block_template_param
ex_nonce,
CURRENCY_MINER_TX_MAX_OUTS,
pos,
pe);
pe,
&resp.blinding_mask_sum);
CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance");
uint64_t coinbase_size = get_object_blobsize(b.miner_tx);
// "- 100" - to reserve room for PoS additions into miner tx
@ -5419,7 +5436,36 @@ bool blockchain_storage::validate_pos_block(const block& b,
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM))
{
return false; // not implemented yet, TODO @#@#
CHECK_AND_ASSERT_MES(b.miner_tx.version > TRANSACTION_VERSION_PRE_HF4, false, "Zarcanum PoS: miner tx with version " << b.miner_tx.version << " is not allowed");
CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_zc_input), false, "incorrect input 1 type: " << b.miner_tx.vin[1].type().name() << ", txin_zc_input expected");
const txin_zc_input& stake_input = boost::get<txin_zc_input>(b.miner_tx.vin[1]);
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());
const zarcanum_sig& sig = boost::get<zarcanum_sig>(b.miner_tx.signatures[0]);
const crypto::hash miner_tx_hash = get_transaction_hash(b.miner_tx);
// TODO @#@# do general input check for main chain blocks only?
uint64_t max_related_block_height = 0;
std::vector<crypto::public_key> dummy_output_keys; // won't be used
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);
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.");
// build a ring of references
vector<crypto::CLSAG_GGXG_input_ref_t> ring;
ring.reserve(scan_contex.zc_outs.size());
for(auto& zc_out : scan_contex.zc_outs)
ring.emplace_back(zc_out.stealth_address, zc_out.amount_commitment, zc_out.concealing_point);
crypto::scalar_t last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, sm.last_pow_id);
uint8_t err = 0;
r = crypto::zarcanum_verify_proof(miner_tx_hash, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, sig, &err);
CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << err);
final_diff = basic_diff; // just for logs
return true;
}
else
{
@ -5703,17 +5749,24 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
m_is_in_checkpoint_zone = false;
crypto::hash proof_hash = null_hash;
uint64_t pos_coinstake_amount = 0;
uint64_t pos_coinstake_amount = UINT64_MAX;
wide_difficulty_type this_coin_diff = 0;
bool is_pos_bl = is_pos_block(bl);
//check if PoS allowed in this height
CHECK_AND_ASSERT_MES_CUSTOM(!(is_pos_bl && m_db_blocks.size() < m_core_runtime_config.pos_minimum_heigh), false, bvc.m_verification_failed = true, "PoS block not allowed on height " << m_db_blocks.size());
if (!prevalidate_miner_transaction(bl, m_db_blocks.size(), is_pos_bl))
{
LOG_PRINT_L0("Block with id: " << id << " @ " << height << " failed to pass miner tx prevalidation");
bvc.m_verification_failed = true;
return false;
}
//check proof of work
TIME_MEASURE_START_PD(target_calculating_time_2);
wide_difficulty_type current_diffic = get_next_diff_conditional(is_pos_bl);
CHECK_AND_ASSERT_MES_CUSTOM(current_diffic, false, bvc.m_verification_failed = true, "!!!!!!!!! difficulty overhead !!!!!!!!!");
TIME_MEASURE_FINISH_PD(target_calculating_time_2);
TIME_MEASURE_FINISH_PD(target_calculating_time_2);
TIME_MEASURE_START_PD(longhash_calculating_time_3);
if (is_pos_bl)
@ -5737,16 +5790,10 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
return false;
}
}
TIME_MEASURE_FINISH_PD(longhash_calculating_time_3);
TIME_MEASURE_FINISH_PD(longhash_calculating_time_3);
size_t aliases_count_befor_block = m_db_aliases.size();
if (!prevalidate_miner_transaction(bl, m_db_blocks.size(), is_pos_bl))
{
LOG_PRINT_L0("Block with id: " << id << " @ " << height << " failed to pass miner tx prevalidation");
bvc.m_verification_failed = true;
return false;
}
size_t cumulative_block_size = 0;
size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx);
@ -6049,7 +6096,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
{ // PoS
int64_t actual_ts = get_block_datetime(bei.bl); // signed int is intentionally used here
int64_t ts_diff = actual_ts - m_core_runtime_config.get_core_time();
powpos_str_entry << "PoS:\t" << proof_hash << ", stake amount: " << print_money_brief(pos_coinstake_amount) << ", final_difficulty: " << this_coin_diff;
powpos_str_entry << "PoS:\t" << proof_hash << ", stake amount: ";
if (pos_coinstake_amount != UINT64_MAX)
powpos_str_entry << print_money_brief(pos_coinstake_amount);
else
powpos_str_entry << "hidden";
powpos_str_entry << ", final_difficulty: " << this_coin_diff;
timestamp_str_entry << ", actual ts: " << actual_ts << " (diff: " << std::showpos << ts_diff << "s) block ts: " << std::noshowpos << bei.bl.timestamp << " (shift: " << std::showpos << static_cast<int64_t>(bei.bl.timestamp) - actual_ts << ")";
}
else

View file

@ -260,8 +260,8 @@ namespace currency
wide_difficulty_type get_cached_next_difficulty(bool pos) const;
bool create_block_template(block& b, const account_public_address& miner_address, const account_public_address& stakeholder_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func = nullptr) const;
bool create_block_template(block& b, const account_public_address& miner_address, wide_difficulty_type& di, uint64_t& height, const blobdata& ex_nonce) const;
bool create_block_template(const account_public_address& miner_address, const blobdata& ex_nonce, block& b, wide_difficulty_type& di, uint64_t& height) const;
bool create_block_template(const account_public_address& miner_address, const account_public_address& stakeholder_address, const blobdata& ex_nonce, bool pos, const pos_entry& pe, fill_block_template_func_t custom_fill_block_template_func, block& b, wide_difficulty_type& di, uint64_t& height, crypto::scalar_t* blinding_mask_sum_ptr = nullptr) const;
bool create_block_template(const create_block_template_params& params, create_block_template_response& resp) const;
bool have_block(const crypto::hash& id) const;
@ -332,6 +332,9 @@ namespace currency
bool is_tx_expired(const transaction& tx) const;
std::shared_ptr<const transaction_chain_entry> find_key_image_and_related_tx(const crypto::key_image& ki, crypto::hash& id_result) const;
// returns true as soon as the hardfork is active for the NEXT upcoming block (not for the top block in the blockchain storage)
bool is_hardfork_active(size_t hardfork_id) const;
wide_difficulty_type block_difficulty(size_t i)const;
bool forecast_difficulty(std::vector<std::pair<uint64_t, wide_difficulty_type>> &out_height_2_diff_vector, bool pos) const;
bool prune_aged_alt_blocks();
@ -676,10 +679,6 @@ namespace currency
bool is_output_allowed_for_input(const txout_htlc& out_v, const txin_v& in_v, uint64_t top_minus_source_height)const;
bool is_output_allowed_for_input(const tx_out_zarcanum& out, const txin_v& in_v) const;
// returns true as soon as the hardfork is active for the NEXT upcoming block (not for the top block in the blockchain storage)
bool is_hardfork_active(size_t hardfork_id) const;
//POS

View file

@ -144,6 +144,7 @@ namespace currency
block b;
wide_difficulty_type diffic;
uint64_t height;
crypto::scalar_t blinding_mask_sum; // sum of all the outputs' blinding masks
};
typedef std::unordered_map<crypto::hash, transaction> transactions_map;

View file

@ -29,6 +29,13 @@ namespace currency
{
CHECK_AND_ASSERT_THROW_MES(hardfork_id < m_total_count, "invalid hardfork id: " << hardfork_id);
m_height_the_hardfork_n_active_after[hardfork_id] = height_the_hardfork_is_active_after;
// set all unset previous hardforks to this height
for(size_t hid = hardfork_id - 1; hid + 1 != 0; --hid)
{
if (m_height_the_hardfork_n_active_after[hid] == CURRENCY_MAX_BLOCK_NUMBER)
m_height_the_hardfork_n_active_after[hid] = height_the_hardfork_is_active_after;
}
}
bool is_hardfork_active_for_height(size_t hardfork_id, uint64_t height) const
@ -57,7 +64,7 @@ namespace currency
return 0;
}
uint8_t get_block_major_version_by_height(uint64_t height)
uint8_t get_block_major_version_by_height(uint64_t height) const
{
if (!this->is_hardfork_active_for_height(1, height))
return BLOCK_MAJOR_VERSION_INITIAL;

View file

@ -22,7 +22,6 @@
#define CRYPTO_HDS_CLSAG_GGXG_CHALLENGE "ZANO_HDS_CLSAG_GGXG_CHALLENGE__"
#define CRYPTO_HDS_ZARCANUM_LAST_POW_HASH "ZANO_HDS_ZARCANUM_LAST_POW_HASH"
#define CRYPTO_HDS_ZARCANUM_SECRET_Q "ZANO_HDS_ZARCANUM_SECRET_Q_____"
#define CRYPTO_HDS_ZARCANUM_PROOF_HASH "ZANO_HDS_ZARCANUM_PROOF_HASH___"
#define CRYPTO_HDS_ASSET_CONTROL_KEY "ZANO_HDS_ASSET_CONTROL_KEY_____"

View file

@ -493,13 +493,33 @@ namespace currency
BEGIN_SERIALIZE_OBJECT()
FIELD(d)
FIELD(C)
// TODO
FIELD(C_prime);
FIELD(E);
FIELD(c);
FIELD(y0);
FIELD(y1);
FIELD(y2);
FIELD(y3);
FIELD(y4);
FIELD((crypto::bppe_signature_serialized&)E_range_proof);
FIELD(pseudo_out_amount_commitment);
FIELD((crypto::CLSAG_GGXG_signature_serialized&)clsag_ggxg);
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(d)
BOOST_SERIALIZE(C)
// TODO
BOOST_SERIALIZE(C_prime);
BOOST_SERIALIZE(E);
BOOST_SERIALIZE(c);
BOOST_SERIALIZE(y0);
BOOST_SERIALIZE(y1);
BOOST_SERIALIZE(y2);
BOOST_SERIALIZE(y3);
BOOST_SERIALIZE(y4);
BOOST_SERIALIZE((crypto::bppe_signature_serialized&)E_range_proof);
BOOST_SERIALIZE(pseudo_out_amount_commitment);
BOOST_SERIALIZE((crypto::CLSAG_GGXG_signature_serialized&)clsag_ggxg);
END_BOOST_SERIALIZATION()
};

View file

@ -355,7 +355,7 @@ namespace currency
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const account_public_address& adr, const account_public_address& stakeholder_address, wide_difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce, bool pos, const pos_entry& pe)
{
return m_blockchain_storage.create_block_template(b, adr, stakeholder_address, diffic, height, ex_nonce, pos, pe);
return m_blockchain_storage.create_block_template(adr, stakeholder_address, ex_nonce, pos, pe, nullptr, b, diffic, height);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(const create_block_template_params& params, create_block_template_response& resp)
@ -511,11 +511,11 @@ namespace currency
if (h > 0)
{
auto& crc = m_blockchain_storage.get_core_runtime_config();
size_t hardfork_id_for_prev_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h - 1);
size_t hardfork_id_for_curr_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h);
size_t hardfork_id_for_prev_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h);
size_t hardfork_id_for_curr_block = crc.hard_forks.get_the_most_recent_hardfork_id_for_height(h + 1);
if (hardfork_id_for_prev_block != hardfork_id_for_curr_block)
{
LOG_PRINT_GREEN("Hardfork " << hardfork_id_for_curr_block << " activated at height " << h, LOG_LEVEL_0);
LOG_PRINT_GREEN("Hardfork " << hardfork_id_for_curr_block << " has been activated after the block at height " << h, LOG_LEVEL_0);
}
}

View file

@ -183,10 +183,13 @@ namespace currency
const account_public_address &stakeholder_address,
transaction& tx,
uint64_t tx_version,
const blobdata& extra_nonce,
size_t max_outs,
bool pos,
const pos_entry& pe)
const blobdata& extra_nonce /* = blobdata() */,
size_t max_outs /* = CURRENCY_MINER_TX_MAX_OUTS */,
bool pos /* = false */,
const pos_entry& pe /* = pos_entry() */, // only pe.stake_unlock_time and pe.stake_amount are used now, TODO: consider refactoring -- sowle
crypto::scalar_t* blinding_masks_sum_ptr /* = nullptr */,
const keypair* tx_one_time_key_to_use /* = nullptr */
)
{
bool r = false;
@ -248,7 +251,10 @@ namespace currency
tx = AUTO_VAL_INIT_T(transaction);
tx.version = tx_version;
keypair txkey = keypair::generate();
keypair txkey_local{};
if (!tx_one_time_key_to_use)
txkey_local = keypair::generate();
const keypair& txkey = tx_one_time_key_to_use ? *tx_one_time_key_to_use : txkey_local;
add_tx_pub_key_to_extra(tx, txkey.pub);
if (extra_nonce.size())
if (!add_tx_extra_userdata(tx, extra_nonce))
@ -269,26 +275,16 @@ namespace currency
{
if (tx.version > TRANSACTION_VERSION_PRE_HF4 /* && stake is zarcanum */)
{
// TODO: add Zarcanum part
//txin_zc_input stake_input = AUTO_VAL_INIT(stake_input);
//stake_input.key_offsets.push_back(pe.g_index);
//stake_input.k_image = pe.keyimage;
// just placeholders, they will be filled in wallet2::prepare_and_sign_pos_block()
tx.vin.emplace_back(std::move(txin_zc_input()));
//reserve place for ring signature
tx.signatures.emplace_back(std::move(zarcanum_sig()));
}
else
{
// old fashioned non-hidden amount direct spend PoS scheme
txin_to_key stake_input;
stake_input.amount = pe.amount;
stake_input.key_offsets.push_back(pe.g_index);
stake_input.k_image = pe.keyimage;
tx.vin.push_back(stake_input);
//reserve place for ring signature
NLSAG_sig nlsag;
nlsag.s.resize(stake_input.key_offsets.size());
tx.signatures.push_back(nlsag); // consider using emplace_back and avoid copying
// just placeholders, they will be filled in wallet2::prepare_and_sign_pos_block()
tx.vin.emplace_back(std::move(txin_to_key()));
tx.signatures.emplace_back(std::move(NLSAG_sig()));
}
}
@ -333,6 +329,9 @@ namespace currency
set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
}
if (blinding_masks_sum_ptr)
*blinding_masks_sum_ptr = blinding_masks_sum;
return true;
}
//------------------------------------------------------------------
@ -387,6 +386,9 @@ namespace currency
VARIANT_CASE_CONST(ZC_sig, zc_sig);
sum_of_pseudo_out_amount_commitments += crypto::point_t(zc_sig.pseudo_out_amount_commitment); // *1/8
++zc_sigs_count;
VARIANT_CASE_CONST(zarcanum_sig, sig);
sum_of_pseudo_out_amount_commitments += crypto::point_t(sig.pseudo_out_amount_commitment); // *1/8
++zc_sigs_count;
VARIANT_SWITCH_END();
}
sum_of_pseudo_out_amount_commitments.modify_mul8();
@ -927,12 +929,12 @@ namespace currency
crypto::derivation_to_scalar((const crypto::key_derivation&)derivation, output_index, h.as_secret_key()); // h = Hs(8 * r * V, i)
out.stealth_address = (h * crypto::c_point_G + crypto::point_t(apa.spend_public_key)).to_public_key();
out.concealing_point = (crypto::c_scalar_1div8 * crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(apa.view_public_key)).to_public_key(); // Q = 1/8 * Hs(domain_sep, h) * V
out.concealing_point = (crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(apa.view_public_key)).to_public_key(); // Q = 1/8 * Hs(domain_sep, Hs(8 * r * V, i) ) * 8 * V
crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h);
out.encrypted_amount = de.amount ^ amount_mask.m_u64[0];
out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
out_blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, Hs(8 * r * V, i) )
out.amount_commitment = (crypto::c_scalar_1div8 * de.amount * crypto::c_point_H + crypto::c_scalar_1div8 * out_blinding_mask * crypto::c_point_G).to_public_key(); // A = 1/8 * a * H + 1/8 * f * G
if (de.addr.front().is_auditable())
@ -1594,8 +1596,9 @@ namespace currency
// prepare inputs
struct input_generation_context_data
{
keypair in_ephemeral;
//std::vector<keypair> participants_derived_keys;
keypair in_ephemeral {}; // ephemeral output key (stealth_address and secret_x)
size_t real_out_index = SIZE_MAX; // index of real output in local outputs vector
std::vector<tx_source_entry::output_entry> outputs{}; // sorted by gindex
};
//--------------------------------------------------------------------------------
bool generate_ZC_sig(const crypto::hash& tx_hash_for_signature, size_t input_index, const tx_source_entry& se, const input_generation_context_data& in_context,
@ -1616,7 +1619,7 @@ namespace currency
#ifndef NDEBUG
{
crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * se.amount * crypto::c_point_H + crypto::c_scalar_1div8 * se.real_out_amount_blinding_mask * crypto::c_point_G;
CHECK_AND_ASSERT_MES(se.outputs[se.real_output].amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed");
CHECK_AND_ASSERT_MES(in_context.outputs[in_context.real_out_index].amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed");
}
#endif
@ -1651,10 +1654,10 @@ namespace currency
// se.real_out_amount_blinding_mask - blinding_mask;
std::vector<crypto::CLSAG_GG_input_ref_t> ring;
for(size_t j = 0; j < se.outputs.size(); ++j)
ring.emplace_back(se.outputs[j].stealth_address, se.outputs[j].amount_commitment);
for(size_t j = 0; j < in_context.outputs.size(); ++j)
ring.emplace_back(in_context.outputs[j].stealth_address, in_context.outputs[j].amount_commitment);
return crypto::generate_CLSAG_GG(tx_hash_for_signature, ring, pseudo_out_amount_commitment, in.k_image, in_context.in_ephemeral.sec, se.real_out_amount_blinding_mask - blinding_mask, se.real_output, sig.clsags_gg);
return crypto::generate_CLSAG_GG(tx_hash_for_signature, ring, pseudo_out_amount_commitment, in.k_image, in_context.in_ephemeral.sec, se.real_out_amount_blinding_mask - blinding_mask, in_context.real_out_index, sig.clsags_gg);
}
//--------------------------------------------------------------------------------
bool generate_NLSAG_sig(const crypto::hash& tx_hash_for_signature, const crypto::hash& tx_prefix_hash, size_t input_index, const tx_source_entry& src_entr,
@ -1679,22 +1682,22 @@ namespace currency
*pss_ring_s << "input #" << input_index << ", pub_keys:" << ENDL;
std::vector<const crypto::public_key*> keys_ptrs;
for(const tx_source_entry::output_entry& o : src_entr.outputs)
for(const tx_source_entry::output_entry& o : in_context.outputs)
{
keys_ptrs.push_back(&o.stealth_address);
if (pss_ring_s)
*pss_ring_s << o.stealth_address << ENDL;
}
sigs.resize(src_entr.outputs.size());
sigs.resize(in_context.outputs.size());
if (!watch_only_mode)
crypto::generate_ring_signature(tx_hash_for_signature, get_key_image_from_txin_v(tx.vin[input_index]), keys_ptrs, in_context.in_ephemeral.sec, src_entr.real_output, sigs.data());
crypto::generate_ring_signature(tx_hash_for_signature, get_key_image_from_txin_v(tx.vin[input_index]), keys_ptrs, in_context.in_ephemeral.sec, in_context.real_out_index, sigs.data());
if (pss_ring_s)
{
*pss_ring_s << "signatures:" << ENDL;
std::for_each(sigs.begin(), sigs.end(), [&pss_ring_s](const crypto::signature& s) { *pss_ring_s << s << ENDL; });
*pss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_context.in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output << ENDL;
*pss_ring_s << "prefix_hash: " << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_context.in_ephemeral.sec << ENDL << "real_output: " << in_context.real_out_index << ENDL;
}
}
@ -1832,7 +1835,11 @@ namespace currency
{
inputs_mapping[current_index] = current_index;
current_index++;
in_contexts.push_back(input_generation_context_data());
in_contexts.push_back(input_generation_context_data{});
input_generation_context_data& in_context = in_contexts.back();
in_context.outputs = prepare_outputs_entries_for_key_offsets(src_entr.outputs, src_entr.real_output, in_context.real_out_index);
if(src_entr.is_multisig())
{//multisig input
txin_multisig input_multisig = AUTO_VAL_INIT(input_multisig);
@ -1844,7 +1851,7 @@ namespace currency
else if (src_entr.htlc_origin.size())
{
//htlc redeem
keypair& in_ephemeral = in_contexts.back().in_ephemeral;
keypair& in_ephemeral = in_context.in_ephemeral;
//txin_to_key
if(src_entr.outputs.size() != 1)
{
@ -1859,11 +1866,11 @@ namespace currency
return false;
//check that derivated key is equal with real output key
if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].stealth_address))
if (!(in_ephemeral.pub == src_entr.outputs.front().stealth_address))
{
LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:"
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].stealth_address));
<< string_tools::pod_to_hex(src_entr.outputs.front().stealth_address));
return false;
}
@ -1872,47 +1879,37 @@ namespace currency
input_to_key.amount = src_entr.amount;
input_to_key.k_image = img;
input_to_key.hltc_origin = src_entr.htlc_origin;
input_to_key.key_offsets.push_back(src_entr.outputs.front().out_reference);
//fill outputs array and use relative offsets
for(const tx_source_entry::output_entry& out_entry : src_entr.outputs)
input_to_key.key_offsets.push_back(out_entry.out_reference);
input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
tx.vin.push_back(input_to_key);
}
else
{
//regular to key out
keypair& in_ephemeral = in_contexts.back().in_ephemeral;
//txin_to_key
if (src_entr.real_output >= src_entr.outputs.size())
{
LOG_ERROR("real_output index (" << src_entr.real_output << ") greater than or equal to output_keys.size()=" << src_entr.outputs.size());
return false;
}
// txin_to_key or txin_zc_input
CHECK_AND_ASSERT_MES(in_context.real_out_index < in_context.outputs.size(), false,
"real_output index (" << in_context.real_out_index << ") greater than or equal to in_context.outputs.size()=" << in_context.outputs.size());
summary_inputs_money += src_entr.amount;
//key_derivation recv_derivation;
crypto::key_image img;
if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img))
if (!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_context.in_ephemeral, img))
return false;
//check that derivated key is equal with real output key
if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].stealth_address))
if (!(in_context.in_ephemeral.pub == in_context.outputs[in_context.real_out_index].stealth_address))
{
LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:"
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].stealth_address));
<< string_tools::pod_to_hex(in_context.in_ephemeral.pub) << ENDL << "real output_public_key:"
<< string_tools::pod_to_hex(in_context.outputs[in_context.real_out_index].stealth_address));
return false;
}
//fill key_offsets array with relative offsets
std::vector<txout_ref_v> key_offsets;
for(const tx_source_entry::output_entry& out_entry : src_entr.outputs)
for(const tx_source_entry::output_entry& out_entry : in_context.outputs)
key_offsets.push_back(out_entry.out_reference);
key_offsets = absolute_output_offsets_to_relative(key_offsets);
//TODO: Might need some refactoring since this scheme is not the clearest one(did it this way for now to keep less changes to not broke anything)
//potentially this approach might help to support htlc and multisig without making to complicated code
if (src_entr.is_zarcanum())
@ -1936,15 +1933,10 @@ namespace currency
input_to_key.k_image = img;
input_to_key.key_offsets = std::move(key_offsets);
tx.vin.push_back(input_to_key);
//NLSAG_sources.push_back(&src_entr);
}
}
}
/*if (ins_zc.elements.size())
{
tx.vin.push_back(ins_zc);
}*/
uint64_t amount_of_assets = 0;
std::vector<tx_destination_entry> shuffled_dsts(destinations);
if (asset_id_for_destinations != currency::null_hash)
@ -2106,45 +2098,21 @@ namespace currency
}
LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3);
//input_index++;
//in_context_index++;
}
/*
for(const tx_source_entry& source_entry : sources)
{
crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, input_index, tx_prefix_hash);
CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "prepare_prefix_hash_for_sign failed");
std::stringstream ss_ring_s;
if (source_entry.is_zarcanum())
{
// ZC
// blinding_masks_sum is supposed to be sum(mask of all tx output) - sum(masks of all pseudo out commitments)
r = generate_ZC_sig(tx_hash_for_signature, input_index, source_entry, in_contexts[in_context_index], sender_account_keys, blinding_masks_sum, flags, local_blinding_masks_sum, tx);
CHECK_AND_ASSERT_MES(r, false, "generate_ZC_sigs failed");
}
else
{
// NLSAG
r = generate_NLSAG_sig(tx_hash_for_signature, tx_prefix_hash, input_index, source_entry, sender_account_keys, in_contexts[in_context_index], txkey, flags, tx, &ss_ring_s);
CHECK_AND_ASSERT_MES(r, false, "generate_NLSAG_sig failed");
}
//size_t prefix_size = get_object_blobsize(static_cast<const transaction_prefix&>(tx));
//size_t full_blob_size = t_serializable_object_to_blob(tx).size();
//size_t estimated_blob_size = get_object_blobsize(tx);
//CHECK_AND_ASSERT_MES(full_blob_size == estimated_blob_size, false, "!");
LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str(), LOG_LEVEL_3);
input_index++;
in_context_index++;
}
*/
return true;
}
//---------------------------------------------------------------
uint64_t get_tx_version(uint64_t h, const hard_forks_descriptor& hfd)
uint64_t get_tx_version(uint64_t tx_expected_block_height, const hard_forks_descriptor& hfd)
{
if (!hfd.is_hardfork_active_for_height(4, h))
if (!hfd.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, tx_expected_block_height))
{
return TRANSACTION_VERSION_PRE_HF4;
}
@ -2503,18 +2471,18 @@ namespace currency
return res;
}
//---------------------------------------------------------------
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index)
bool is_out_to_acc(const account_public_address& addr, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index)
{
crypto::public_key pk;
if (!derive_public_key(derivation, output_index, acc.account_address.spend_public_key, pk))
if (!derive_public_key(derivation, output_index, addr.spend_public_key, pk))
return false;
return pk == out_key.key;
}
//---------------------------------------------------------------
bool is_out_to_acc(const account_keys& acc, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index)
bool is_out_to_acc(const account_public_address& addr, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index)
{
crypto::public_key pk;
if (!derive_public_key(derivation, output_index, acc.account_address.spend_public_key, pk))
if (!derive_public_key(derivation, output_index, addr.spend_public_key, pk))
return false;
auto it = std::find(out_multisig.keys.begin(), out_multisig.keys.end(), pk);
if (out_multisig.keys.end() == it)
@ -2522,16 +2490,16 @@ namespace currency
return true;
}
bool is_out_to_acc(const account_keys& acc, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask)
bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask)
{
crypto::scalar_t h; // = crypto::hash_helper_t::hs(reinterpret_cast<const crypto::public_key&>(derivation), output_index); // h = Hs(8 * r * V, i)
crypto::derivation_to_scalar(derivation, output_index, h.as_secret_key()); // h = Hs(8 * r * V, i)
crypto::point_t P_prime = h * crypto::c_point_G + crypto::point_t(acc.account_address.spend_public_key); // P =? Hs(8rV, i) * G + S
crypto::point_t P_prime = h * crypto::c_point_G + crypto::point_t(addr.spend_public_key); // P =? Hs(8rV, i) * G + S
if (P_prime.to_public_key() != zo.stealth_address)
return false;
crypto::point_t Q_prime = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(acc.account_address.view_public_key); // Q' * 8 =? Hs(domain_sep, h) * V
crypto::point_t Q_prime = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * 8 * crypto::point_t(addr.view_public_key); // Q' * 8 =? Hs(domain_sep, Hs(8 * r * V, i) ) * 8 * V
if (Q_prime != crypto::point_t(zo.concealing_point).modify_mul8())
return false;
@ -2595,7 +2563,7 @@ namespace currency
const tx_out_bare& o = boost::get<tx_out_bare>(ov);
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "condition failed: o.target.type() == typeid(txout_to_key)");
if (is_out_to_acc(acc, boost::get<txout_to_key>(o.target), derivation, offset))
if (is_out_to_acc(acc.account_address, boost::get<txout_to_key>(o.target), derivation, offset))
{
outs.emplace_back(offset, o.amount);
money_transfered += o.amount;
@ -2632,25 +2600,25 @@ namespace currency
{
VARIANT_SWITCH_BEGIN(o.target);
VARIANT_CASE_CONST(txout_to_key, t)
if (is_out_to_acc(acc, t, derivation, output_index))
if (is_out_to_acc(acc.account_address, t, derivation, output_index))
{
outs.emplace_back(output_index, o.amount);
money_transfered += o.amount;
}
VARIANT_CASE_CONST(txout_multisig, t)
if (is_out_to_acc(acc, t, derivation, output_index))
if (is_out_to_acc(acc.account_address, t, derivation, output_index))
{
outs.emplace_back(output_index, o.amount); // TODO: @#@# consider this
//don't cout this money
}
VARIANT_CASE_CONST(txout_htlc, htlc)
htlc_info hi = AUTO_VAL_INIT(hi);
if (is_out_to_acc(acc, htlc.pkey_redeem, derivation, output_index))
if (is_out_to_acc(acc.account_address, htlc.pkey_redeem, derivation, output_index))
{
hi.hltc_our_out_is_before_expiration = true;
htlc_info_list.push_back(hi);
}
else if (is_out_to_acc(acc, htlc.pkey_refund, derivation, output_index))
else if (is_out_to_acc(acc.account_address, htlc.pkey_refund, derivation, output_index))
{
hi.hltc_our_out_is_before_expiration = false;
htlc_info_list.push_back(hi);
@ -2664,7 +2632,7 @@ namespace currency
VARIANT_CASE_CONST(tx_out_zarcanum, zo)
uint64_t amount = 0;
crypto::scalar_t blinding_mask = 0;
if (is_out_to_acc(acc, zo, derivation, output_index, amount, blinding_mask))
if (is_out_to_acc(acc.account_address, zo, derivation, output_index, amount, blinding_mask))
{
outs.emplace_back(output_index, amount, blinding_mask);
open_asset_id v = AUTO_VAL_INIT(v);
@ -3063,6 +3031,7 @@ namespace currency
return res;
}
//---------------------------------------------------------------
// DEPRECATED: consider using prepare_outputs_entries_for_key_offsets and absolute_sorted_output_offsets_to_relative_in_place instead
std::vector<txout_ref_v> absolute_output_offsets_to_relative(const std::vector<txout_ref_v>& off)
{
std::vector<txout_ref_v> res = off;
@ -3106,6 +3075,34 @@ namespace currency
return res;
}
//---------------------------------------------------------------
bool absolute_sorted_output_offsets_to_relative_in_place(std::vector<txout_ref_v>& offsets) noexcept
{
if (offsets.size() < 2)
return true;
size_t i = offsets.size() - 1;
while (i != 0 && offsets[i].type() == typeid(ref_by_id))
--i;
try
{
for (; i != 0; i--)
{
uint64_t& offset_i = boost::get<uint64_t>(offsets[i]);
uint64_t& offset_im1 = boost::get<uint64_t>(offsets[i - 1]);
if (offset_i <= offset_im1)
return false; // input was not properly sorted
offset_i -= offset_im1;
}
}
catch(...)
{
return false; // unexpected type in boost::get (all ref_by_id's must be at the end of 'offsets')
}
return true;
}
//---------------------------------------------------------------
bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b)
{
return parse_and_validate_object_from_blob(b_blob, b);
@ -3978,6 +3975,11 @@ namespace currency
return false;
}
//--------------------------------------------------------------------------------
bool operator ==(const currency::ref_by_id& a, const currency::ref_by_id& b)
{
return a.n == b.n && a.tx_id == b.tx_id;
}
//--------------------------------------------------------------------------------
bool verify_multiple_zc_outs_range_proofs(const std::vector<zc_outs_range_proofs_with_commitments>& range_proofs)
{
if (range_proofs.empty())

View file

@ -60,6 +60,7 @@ namespace currency
bool operator ==(const currency::void_sig& a, const currency::void_sig& b);
bool operator ==(const currency::ZC_sig& a, const currency::ZC_sig& b);
bool operator ==(const currency::zarcanum_sig& a, const currency::zarcanum_sig& b);
bool operator ==(const currency::ref_by_id& a, const currency::ref_by_id& b);
typedef boost::multiprecision::uint128_t uint128_tl;
@ -243,7 +244,9 @@ namespace currency
const blobdata& extra_nonce = blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
bool pos = false,
const pos_entry& pe = pos_entry());
const pos_entry& pe = pos_entry(),
crypto::scalar_t* blinding_masks_sum_ptr = nullptr,
const keypair* tx_one_time_key_to_use = nullptr);
//---------------------------------------------------------------
uint64_t get_string_uint64_hash(const std::string& str);
bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set<uint16_t>& deriv_cache, const account_keys& self, crypto::scalar_t& out_blinding_mask, finalized_tx& result, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED);
@ -288,7 +291,7 @@ namespace currency
bool shuffle = true,
uint64_t flags = 0);
uint64_t get_tx_version(uint64_t h, const hard_forks_descriptor& hfd);
uint64_t get_tx_version(uint64_t tx_expected_block_height, const hard_forks_descriptor& hfd); // returns tx version based on the height of the block where the transaction is expected to be
bool construct_tx(const account_keys& sender_account_keys, const finalize_tx_param& param, finalized_tx& result);
crypto::hash get_asset_id_from_descriptor(const asset_descriptor_base& adb);
@ -306,9 +309,9 @@ namespace currency
bool add_tx_extra_userdata(transaction& tx, const blobdata& extra_nonce);
crypto::hash get_multisig_out_id(const transaction& tx, size_t n);
bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index);
bool is_out_to_acc(const account_keys& acc, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index);
bool is_out_to_acc(const account_keys& acc, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount);
bool is_out_to_acc(const account_public_address& addr, const txout_to_key& out_key, const crypto::key_derivation& derivation, size_t output_index);
bool is_out_to_acc(const account_public_address& addr, const txout_multisig& out_multisig, const crypto::key_derivation& derivation, size_t output_index);
bool is_out_to_acc(const account_public_address& addr, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount, crypto::scalar_t& blinding_mask);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<wallet_out_info>& outs, uint64_t& money_transfered, crypto::key_derivation& derivation);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector<wallet_out_info>& outs, uint64_t& money_transfered, crypto::key_derivation& derivation, std::list<htlc_info>& htlc_info_list);
bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector<wallet_out_info>& outs, uint64_t& money_transfered, crypto::key_derivation& derivation);
@ -363,7 +366,11 @@ namespace currency
uint64_t get_block_height(const transaction& coinbase);
uint64_t get_block_height(const block& b);
std::vector<txout_ref_v> relative_output_offsets_to_absolute(const std::vector<txout_ref_v>& off);
// DEPRECATED: consider using prepare_outputs_entries_for_key_offsets and absolute_sorted_output_offsets_to_relative_in_place instead
std::vector<txout_ref_v> absolute_output_offsets_to_relative(const std::vector<txout_ref_v>& off);
bool absolute_sorted_output_offsets_to_relative_in_place(std::vector<txout_ref_v>& offsets) noexcept;
// prints amount in format "3.14", "0.0"
std::string print_money_brief(uint64_t amount);
@ -813,6 +820,17 @@ namespace currency
return boost::apply_visitor(input_amount_getter(), v);
}
//---------------------------------------------------------------
struct output_amount_getter : public boost::static_visitor<uint64_t>
{
template<class out_t>
uint64_t operator()(const out_t&) const { return 0; }
uint64_t operator()(const tx_out_bare& ob) const { return ob.amount; }
};
inline uint64_t get_amount_from_variant(const tx_out_v& out_v)
{
return boost::apply_visitor(output_amount_getter(), out_v);
}
//---------------------------------------------------------------
inline const tx_out_bare& get_tx_out_bare_from_out_v(const tx_out_v& o)
{
//this function will throw if type is not matching

View file

@ -129,8 +129,9 @@ namespace currency
return boost::get<specific_type_t>(ai);
}
}
ASSERT_MES_AND_THROW("Objec not found");
ASSERT_MES_AND_THROW("Object with type " << typeid(specific_type_t).name() << " was not found in a container");
}
//---------------------------------------------------------------
// if cb returns true, it means "continue", false -- means "stop"
template<typename specific_type_t, typename variant_container_t, typename callback_t>
bool process_type_in_variant_container(const variant_container_t& av, callback_t& cb, bool return_value_if_none_found = true)

View file

@ -215,12 +215,35 @@ namespace currency
return total;
}
//---------------------------------------------------------------
inline size_t get_input_expected_signature_size_local(const txin_v& tx_in, bool last_input_in_separately_signed_tx)
{
struct txin_signature_size_visitor : public boost::static_visitor<size_t>
{
txin_signature_size_visitor(size_t add) : a(add) {}
size_t a;
size_t operator()(const txin_gen& /*txin*/) const { return 0; }
size_t operator()(const txin_to_key& txin) const { return tools::get_varint_packed_size(txin.key_offsets.size() + a) + sizeof(crypto::signature) * (txin.key_offsets.size() + a); }
size_t operator()(const txin_multisig& txin) const { return tools::get_varint_packed_size(txin.sigs_count + a) + sizeof(crypto::signature) * (txin.sigs_count + a); }
size_t operator()(const txin_htlc& txin) const { return tools::get_varint_packed_size(1 + a) + sizeof(crypto::signature) * (1 + a); }
size_t operator()(const txin_zc_input& txin) const { return 96 + tools::get_varint_packed_size(txin.key_offsets.size()) + txin.key_offsets.size() * 32; }
};
return boost::apply_visitor(txin_signature_size_visitor(last_input_in_separately_signed_tx ? 1 : 0), tx_in);
}
//---------------------------------------------------------------
size_t get_object_blobsize(const transaction& t, uint64_t prefix_blob_size)
{
size_t tx_blob_size = prefix_blob_size;
if (is_coinbase(t))
{
if (is_pos_miner_tx(t) && t.version > TRANSACTION_VERSION_PRE_HF4)
{
// Zarcanum
return tx_blob_size;
}
return tx_blob_size;
}
// for purged tx, with empty signatures and attachments, this function should return the blob size
// which the tx would have if the signatures and attachments were correctly filled with actual data
@ -229,10 +252,12 @@ namespace currency
bool separately_signed_tx = get_tx_flags(t) & TX_FLAG_SIGNATURE_MODE_SEPARATE;
tx_blob_size += tools::get_varint_packed_size(t.vin.size()); // size of transaction::signatures (equals to total inputs count)
if (t.version > TRANSACTION_VERSION_PRE_HF4)
tx_blob_size += t.vin.size(); // for HF4 txs 'signatures' is a verctor of variants, so it's +1 byte per signature (assuming sigs count equals to inputs count)
for (size_t i = 0; i != t.vin.size(); i++)
{
size_t sig_size = get_input_expected_signature_size(t.vin[i], separately_signed_tx && i == t.vin.size() - 1);
size_t sig_size = get_input_expected_signature_size_local(t.vin[i], separately_signed_tx && i == t.vin.size() - 1);
tx_blob_size += sig_size;
}
@ -298,4 +323,65 @@ namespace currency
}
return true;
}
//---------------------------------------------------------------
// Prepapres vector of output_entry to be used in key_offsets in a transaction input:
// 1) sort all entries by gindex (while moving all ref_by_id to the end, keeping they relative order)
// 2) convert absolute global indices to relative key_offsets
std::vector<tx_source_entry::output_entry> prepare_outputs_entries_for_key_offsets(const std::vector<tx_source_entry::output_entry>& outputs, size_t old_real_index, size_t& new_real_index) noexcept
{
TRY_ENTRY()
std::vector<tx_source_entry::output_entry> result = outputs;
if (outputs.size() < 2)
{
new_real_index = old_real_index;
return result;
}
std::sort(result.begin(), result.end(), [](const tx_source_entry::output_entry& lhs, const tx_source_entry::output_entry& rhs)
{
if (lhs.out_reference.type() == typeid(uint64_t))
{
if (rhs.out_reference.type() == typeid(uint64_t))
return boost::get<uint64_t>(lhs.out_reference) < boost::get<uint64_t>(rhs.out_reference);
if (rhs.out_reference.type() == typeid(ref_by_id))
return true;
CHECK_AND_ASSERT_THROW_MES(false, "unexpected type in out_reference 1: " << rhs.out_reference.type().name());
}
else if (lhs.out_reference.type() == typeid(ref_by_id))
{
if (rhs.out_reference.type() == typeid(uint64_t))
return false;
if (rhs.out_reference.type() == typeid(ref_by_id))
return false; // don't change the order of ref_by_id elements
CHECK_AND_ASSERT_THROW_MES(false, "unexpected type in out_reference 2: " << rhs.out_reference.type().name());
}
return false;
});
// restore index of the selected element, if needed
if (old_real_index != SIZE_MAX)
{
CHECK_AND_ASSERT_THROW_MES(old_real_index < outputs.size(), "old_real_index is OOB");
auto it = std::find(result.begin(), result.end(), outputs[old_real_index]);
CHECK_AND_ASSERT_THROW_MES(it != result.end(), "internal error: cannot find old_real_index");
new_real_index = it - result.begin();
}
// find the last uint64_t entry - skip ref_by_id entries goint from the end to the beginnning
size_t i = result.size() - 1;
while (i != 0 && result[i].out_reference.type() == typeid(ref_by_id))
--i;
for (; i != 0; i--)
{
boost::get<uint64_t>(result[i].out_reference) -= boost::get<uint64_t>(result[i - 1].out_reference);
}
return result;
CATCH_ENTRY2(std::vector<tx_source_entry::output_entry>{});
}
//---------------------------------------------------------------
}

View file

@ -22,14 +22,16 @@ namespace currency
output_entry(const output_entry &) = default;
output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address)
: out_reference(out_reference), stealth_address(stealth_address), concealing_point(null_pkey), amount_commitment(null_pkey) {}
//output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address, const crypto::public_key& concealing_point, const crypto::public_key& amount_commitment)
// : out_reference(out_reference), stealth_address(stealth_address), concealing_point(concealing_point), amount_commitment(amount_commitment) {}
output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address, const crypto::public_key& concealing_point, const crypto::public_key& amount_commitment)
: out_reference(out_reference), stealth_address(stealth_address), concealing_point(concealing_point), amount_commitment(amount_commitment) {}
txout_ref_v out_reference; // either global output index or ref_by_id
crypto::public_key stealth_address; // a.k.a output's one-time public key
crypto::public_key concealing_point; // only for zarcaum outputs
crypto::public_key amount_commitment; // only for zarcaum outputs
bool operator==(const output_entry& rhs) const { return out_reference == rhs.out_reference; } // used in prepare_outputs_entries_for_key_offsets, it's okay to do partially comparison
BEGIN_SERIALIZE_OBJECT()
FIELD(out_reference)
FIELD(stealth_address)
@ -161,5 +163,5 @@ namespace currency
bool read_keyimages_from_tx(const transaction& tx, std::list<crypto::key_image>& kil);
bool validate_inputs_sorting(const transaction& tx);
std::vector<tx_source_entry::output_entry> prepare_outputs_entries_for_key_offsets(const std::vector<tx_source_entry::output_entry>& outputs, size_t old_real_index, size_t& new_real_index) noexcept;
}

View file

@ -0,0 +1,103 @@
// Copyright (c) 2022 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include "currency_basic.h"
#include "difficulty.h"
#include "pos_mining.h"
#include "wallet/wallet2.h"
#include "crypto/zarcanum.h"
#include "crypto_config.h"
namespace currency
{
void pos_mining_context::init(const wide_difficulty_type& pos_diff, const stake_modifier_type& sm, bool is_zarcanum)
{
this->basic_diff = pos_diff;
this->sk.stake_modifier = sm;
this->zarcanum = is_zarcanum;
if (is_zarcanum)
{
this->last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, this->sk.stake_modifier.last_pow_id);
this->z_l_div_z_D = crypto::zarcanum_precalculate_z_l_div_z_D(this->basic_diff);
}
}
void pos_mining_context::prepare_entry(uint64_t stake_amount, const crypto::key_image& stake_out_ki, const crypto::public_key& stake_source_tx_pub_key, uint64_t stake_out_in_tx_index,
const crypto::scalar_t& stake_out_blinding_mask, const crypto::secret_key& view_secret)
{
this->stake_amount = stake_amount;
this->sk.kimage = stake_out_ki;
if (this->zarcanum)
{
crypto::scalar_t v = view_secret;
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
bool r = crypto::generate_key_derivation(stake_source_tx_pub_key, view_secret, derivation); // 8 * v * R
CHECK_AND_ASSERT_MES_NO_RET(r, "generate_key_derivation failed");
crypto::scalar_t h = AUTO_VAL_INIT(h);
crypto::derivation_to_scalar(derivation, stake_out_in_tx_index, h.as_secret_key()); // h = Hs(8 * v * R, i)
// q = Hs(domain_sep, Hs(8 * v * R, i) ) * 8 * v
this->secret_q = v * 8 * crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h);
this->stake_out_blinding_mask = stake_out_blinding_mask;
}
}
bool pos_mining_context::do_iteration(uint64_t ts)
{
// update stake kernel and calculate it's hash
this->sk.block_timestamp = ts;
{
PROFILE_FUNC("calc_hash");
this->kernel_hash = crypto::cn_fast_hash(&this->sk, sizeof(this->sk));
}
bool found = false;
if (this->zarcanum /* && td.is_zc() */)
{
crypto::mp::uint256_t lhs;
crypto::mp::uint512_t rhs;
{
PROFILE_FUNC("check_zarcanum");
found = crypto::zarcanum_check_main_pos_inequality(this->kernel_hash, this->stake_out_blinding_mask, this->secret_q, this->last_pow_block_id_hashed, this->z_l_div_z_D, this->stake_amount, lhs, rhs);
}
if (found)
{
found = true;
LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(this->stake_amount) << /* ", gindex: " << td.m_global_output_index << */ ENDL
<< "difficulty: " << this->basic_diff << ENDL
<< "kernel info: " << ENDL
<< print_stake_kernel_info(this->sk)
<< "kernel_hash: " << this->kernel_hash << ENDL
<< "lhs: " << lhs << ENDL
<< "rhs: " << rhs
, LOG_LEVEL_0);
}
}
else
{
// old PoS with non-hidden amounts
currency::wide_difficulty_type final_diff = this->basic_diff / this->stake_amount;
{
PROFILE_FUNC("check_hash");
found = currency::check_hash(this->kernel_hash, final_diff);
}
if (found)
{
LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(this->stake_amount)<< /* ", gindex: " << td.m_global_output_index << */ ENDL
<< "difficulty: " << this->basic_diff << ", final_diff: " << final_diff << ENDL
<< "kernel info: " << ENDL
<< print_stake_kernel_info(this->sk)
<< "kernel_hash(proof): " << this->kernel_hash,
LOG_LEVEL_0);
}
}
return found;
}
}; // namespace currency

View file

@ -0,0 +1,32 @@
// Copyright (c) 2022 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
namespace currency
{
struct pos_mining_context
{
// Zarcanum notation:
wide_difficulty_type basic_diff; // D
stake_kernel sk;
crypto::scalar_t last_pow_block_id_hashed; // f'
crypto::scalar_t secret_q; // q
boost::multiprecision::uint256_t z_l_div_z_D; // z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252)
crypto::hash kernel_hash; // h
crypto::scalar_t stake_out_blinding_mask; // f
uint64_t stake_amount; // a
bool zarcanum; // false for pre-HF4 classic PoS with explicit amounts
void init(const wide_difficulty_type& pos_diff, const stake_modifier_type& sm, bool is_zarcanum);
void prepare_entry(uint64_t stake_amount, const crypto::key_image& stake_out_ki, const crypto::public_key& stake_source_tx_pub_key, uint64_t stake_out_in_tx_index,
const crypto::scalar_t& stake_out_blinding_mask, const crypto::secret_key& view_secret);
bool do_iteration(uint64_t ts);
};
};

View file

@ -863,6 +863,7 @@ namespace currency
blobdata block_blob = t_serializable_object_to_blob(resp.b);
res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob);
res.prev_hash = string_tools::pod_to_hex(resp.b.prev_id);
res.blinding_masks_sum = resp.blinding_mask_sum;
res.height = resp.height;
//calculate epoch seed
res.seed = currency::ethash_epoch_to_seed(currency::ethash_height_to_epoch(res.height));

View file

@ -332,6 +332,10 @@ namespace currency
#pragma pack (push, 1)
struct out_entry
{
out_entry() = default;
out_entry(uint64_t global_amount_index, const crypto::public_key& stealth_address, const crypto::public_key& amount_commitment, const crypto::public_key& concealing_point)
: global_amount_index(global_amount_index), stealth_address(stealth_address), amount_commitment(amount_commitment), concealing_point(concealing_point)
{}
uint64_t global_amount_index;
crypto::public_key stealth_address;
crypto::public_key concealing_point;
@ -805,28 +809,17 @@ namespace currency
blobdata explicit_transaction;
std::string extra_text;
std::string wallet_address;
std::string stakeholder_address;
bool pos_block; //is pos block
//uint64_t pos_amount; //do we still need it?
//uint64_t pos_g_index; //
//crypto::hash tx_id;
//uint64_t tx_out_index;
//uint64_t stake_unlock_time;
pos_entry pe; // for making PoS blocks
std::string stakeholder_address; // address for stake return (PoS blocks)
pos_entry pe; // for PoS blocks
bool pos_block; // is pos block
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_BLOB_AS_HEX_STRING(explicit_transaction)
KV_SERIALIZE(extra_text)
KV_SERIALIZE(wallet_address)
KV_SERIALIZE(stakeholder_address);
KV_SERIALIZE(pos_block)
//KV_SERIALIZE(pos_amount)
//KV_SERIALIZE(pos_g_index)
//KV_SERIALIZE_POD_AS_HEX_STRING(tx_id)
//KV_SERIALIZE(tx_out_index)
//KV_SERIALIZE(stake_unlock_time)
KV_SERIALIZE(pe)
KV_SERIALIZE(pos_block)
END_KV_SERIALIZE_MAP()
};
@ -837,6 +830,7 @@ namespace currency
crypto::hash seed;
blobdata blocktemplate_blob;
std::string prev_hash;
crypto::scalar_t blinding_masks_sum; // sum of outputs' blinding masks (for zc outs)
std::string status;
BEGIN_KV_SERIALIZE_MAP()
@ -845,6 +839,7 @@ namespace currency
KV_SERIALIZE_POD_AS_HEX_STRING(seed)
KV_SERIALIZE(blocktemplate_blob)
KV_SERIALIZE(prev_hash)
KV_SERIALIZE(blinding_masks_sum)
KV_SERIALIZE(status)
END_KV_SERIALIZE_MAP()
};

View file

@ -3168,7 +3168,7 @@ bool wallet2::generate_packing_transaction_if_needed(currency::transaction& tx,
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/) const
{
static const char* header = "index amount g_index flags block tx out# key image";
static const char* header = "index amount g_index flags block tx out# key image";
std::stringstream ss;
ss << header << ENDL;
size_t count = 0;
@ -3567,8 +3567,11 @@ bool wallet2::get_transfer_address(const std::string& adr_str, currency::account
return m_core_proxy->get_transfer_address(adr_str, addr, payment_id);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time) const
bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const
{
if (is_zarcanum_hf && !tr.is_zc())
return false;
if (!tr.is_spendable())
return false;
@ -3603,6 +3606,7 @@ void wallet2::get_mining_history(wallet_public::mining_history& hist, uint64_t t
//----------------------------------------------------------------------------------------------------
size_t wallet2::get_pos_entries_count()
{
bool is_zarcanum_hf = is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM);
size_t counter = 0;
for (size_t i = 0, size = m_transfers.size(); i < size; i++)
@ -3610,7 +3614,7 @@ size_t wallet2::get_pos_entries_count()
auto& tr = m_transfers[i];
uint64_t stake_unlock_time = 0;
if (!is_transfer_okay_for_pos(tr, stake_unlock_time))
if (!is_transfer_okay_for_pos(tr, is_zarcanum_hf, stake_unlock_time))
continue;
++counter;
@ -3621,12 +3625,13 @@ size_t wallet2::get_pos_entries_count()
//----------------------------------------------------------------------------------------------------
bool wallet2::get_pos_entries(std::vector<currency::pos_entry>& entries)
{
bool is_zarcanum_hf = is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM);
for (size_t i = 0; i != m_transfers.size(); i++)
{
auto& tr = m_transfers[i];
uint64_t stake_unlock_time = 0;
if (!is_transfer_okay_for_pos(tr, stake_unlock_time))
if (!is_transfer_okay_for_pos(tr, is_zarcanum_hf, stake_unlock_time))
continue;
pos_entry pe = AUTO_VAL_INIT(pe);
@ -3647,7 +3652,7 @@ bool wallet2::is_in_hardfork_zone(uint64_t hardfork_index) const
return m_core_runtime_config.is_hardfork_active_for_height(hardfork_index, get_blockchain_current_size());
}
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const pos_entry& pe) const
bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const pos_entry& pe, const crypto::scalar_t& blinding_masks_sum) const
{
bool r = false;
WLT_CHECK_AND_ASSERT_MES(pe.wallet_index < m_transfers.size(), false, "invalid pe.wallet_index: " << pe.wallet_index);
@ -3656,53 +3661,53 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
const crypto::public_key source_tx_pub_key = get_tx_pub_key_from_extra(source_tx);
WLT_CHECK_AND_ASSERT_MES(pe.tx_out_index < source_tx.vout.size(), false, "invalid pe.tx_out_index: " << pe.tx_out_index);
const currency::tx_out_v& stake_out_v = source_tx.vout[pe.tx_out_index];
// calculate stake_out_derivation and secret_x (derived ephemeral secret key)
crypto::key_derivation stake_out_derivation = AUTO_VAL_INIT(stake_out_derivation);
r = crypto::generate_key_derivation(source_tx_pub_key, m_account.get_keys().view_secret_key, stake_out_derivation); // d = 8 * v * R
WLT_CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed, tid: " << pe.wallet_index << ", pe.tx_id: " << pe.tx_id);
crypto::secret_key secret_x = AUTO_VAL_INIT(secret_x);
crypto::derive_secret_key(stake_out_derivation, pe.tx_out_index, m_account.get_keys().spend_secret_key, secret_x); // x = Hs(8 * v * R, i) + s
if (!cxt.zarcanum)
{
// old PoS with non-hidden amounts
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(currency::txin_gen), false, "Wrong input 0 type in transaction: " << b.miner_tx.vin[0].type().name());
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(currency::txin_to_key), false, "Wrong input 1 type in transaction: " << b.miner_tx.vin[1].type().name());
auto& txin = boost::get<currency::txin_to_key>(b.miner_tx.vin[1]);
txin.k_image = pe.keyimage;
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1 &&
b.miner_tx.signatures[0].type() == typeid(NLSAG_sig) &&
boost::get<NLSAG_sig>(b.miner_tx.signatures[0]).s.size() == txin.key_offsets.size(),
false, "Wrong signatures amount in coinbase transacton");
//derive secret key
crypto::key_derivation pos_coin_derivation = AUTO_VAL_INIT(pos_coin_derivation);
bool r = crypto::generate_key_derivation(source_tx_pub_key,
m_account.get_keys().view_secret_key,
pos_coin_derivation);
WLT_CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed, pe.tx_id: " << pe.tx_id);
crypto::secret_key derived_secret_ephemeral_key = AUTO_VAL_INIT(derived_secret_ephemeral_key);
crypto::derive_secret_key(pos_coin_derivation,
pe.tx_out_index,
m_account.get_keys().spend_secret_key,
derived_secret_ephemeral_key);
WLT_CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1 && b.miner_tx.signatures[0].type() == typeid(NLSAG_sig), false, "wrong sig prepared in a PoS block");
WLT_CHECK_AND_ASSERT_MES(stake_out_v.type() == typeid(tx_out_bare), false, "unexpected stake output type: " << stake_out_v.type().name() << ", expected: tx_out_bare");
const tx_out_bare& stake_out = boost::get<tx_out_bare>(stake_out_v);
WLT_CHECK_AND_ASSERT_MES(stake_out.target.type() == typeid(txout_to_key), false, "unexpected stake output target type: " << stake_out.target.type().name() << ", expected: txout_to_key");
NLSAG_sig& sig = boost::get<NLSAG_sig>(b.miner_tx.signatures[0]);
txin_to_key& stake_input = boost::get<txin_to_key>(b.miner_tx.vin[1]);
const txout_to_key& stake_out_target = boost::get<txout_to_key>(stake_out.target);
// fill stake input
stake_input.k_image = pe.keyimage;
stake_input.amount = pe.amount;
stake_input.key_offsets.push_back(pe.g_index);
// sign block actually in coinbase transaction
crypto::hash block_hash = currency::get_block_hash(b);
// get stake output pub key (stealth address) for ring signature generation
std::vector<const crypto::public_key*> keys_ptrs;
TRY_ENTRY()
keys_ptrs.push_back(&boost::get<currency::txout_to_key>(boost::get<tx_out_bare>(stake_out_v).target).key);
CATCH_ENTRY_CUSTOM("wallet2::prepare_and_sign_pos_block", { LOG_PRINT_RED_L0("unable to get output's pub key because of the exception"); }, false);
keys_ptrs.push_back(&stake_out_target.key);
// generate sring signature
sig.s.resize(1);
crypto::generate_ring_signature(block_hash,
txin.k_image,
stake_input.k_image,
keys_ptrs,
derived_secret_ephemeral_key,
secret_x,
0,
&boost::get<NLSAG_sig>(b.miner_tx.signatures[0]).s[0]);
&sig.s[0]);
WLT_LOG_L4("GENERATED RING SIGNATURE: block_id " << block_hash
<< "txin.k_image" << txin.k_image
WLT_LOG_L4("GENERATED RING SIGNATURE for PoS block coinbase: block_id " << block_hash
<< "txin.k_image" << stake_input.k_image
<< "key_ptr:" << *keys_ptrs[0]
<< "signature:" << boost::get<NLSAG_sig>(b.miner_tx.signatures[0]).s);
<< "signature:" << sig.s);
return true;
}
@ -3724,7 +3729,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
uint64_t secret_index = 0; // index of the real stake output
// get decoys outputs and construct miner tx
static size_t required_decoys_count = 8; // TODO @#@# set them somewhere else
static size_t required_decoys_count = 4; // TODO @#@# set them somewhere else
static bool use_only_forced_to_mix = false; // TODO @#@# set them somewhere else
if (required_decoys_count > 0)
{
@ -3743,8 +3748,10 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(decoys_resp.outs[0].outs.size() == required_decoys_count + 1, "for PoS stake tx got less decoys to mix than requested: " << decoys_resp.outs[0].outs.size() << " < " << required_decoys_count + 1);
auto& decoys = decoys_resp.outs[0].outs;
std::unordered_set<uint64_t> used_gindices{ td.m_global_output_index };
size_t good_decoys_count = 0;
decoys.emplace_front(td.m_global_output_index, stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point);
std::unordered_set<uint64_t> used_gindices;
size_t good_outs_count = 0;
for(auto it = decoys.begin(); it != decoys.end(); )
{
if (used_gindices.count(it->global_amount_index) != 0)
@ -3753,33 +3760,29 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
continue;
}
used_gindices.insert(it->global_amount_index);
if (++good_decoys_count == required_decoys_count)
if (++good_outs_count == required_decoys_count + 1)
{
decoys.erase(++it, decoys.end());
break;
}
++it;
}
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(decoys.size() == required_decoys_count, "for PoS stake got less good decoys than required: " << decoys.size() << " < " << required_decoys_count);
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(decoys.size() == required_decoys_count + 1, "for PoS stake got less good decoys than required: " << decoys.size() << " < " << required_decoys_count);
decoys.sort([](auto& l, auto& r){ return l.global_amount_index < r.global_amount_index; }); // sort them now (note absolute_sorted_output_offsets_to_relative_in_place() below)
secret_index = crypto::rand<uint64_t>() % (decoys.size());
uint64_t i = 0;
for(auto& el : decoys)
{
if (i++ == secret_index)
{
ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point);
stake_input.key_offsets.push_back(td.m_global_output_index);
}
uint64_t gindex = el.global_amount_index;
if (gindex == td.m_global_output_index)
secret_index = i;
++i;
ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point);
stake_input.key_offsets.push_back(el.global_amount_index);
}
if (i == secret_index)
{
ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point);
stake_input.key_offsets.push_back(td.m_global_output_index);
}
stake_input.key_offsets = absolute_output_offsets_to_relative(stake_input.key_offsets);
r = absolute_sorted_output_offsets_to_relative_in_place(stake_input.key_offsets);
WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(r, "absolute_sorted_output_offsets_to_relative_in_place failed");
}
else
{
@ -3793,24 +3796,15 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
{
crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * td.m_amount * crypto::c_point_H + crypto::c_scalar_1div8 * *td.m_opt_blinding_mask * crypto::c_point_G;
CHECK_AND_ASSERT_MES(stake_out.amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed");
CHECK_AND_ASSERT_MES(ring[secret_index].amount_commitment == stake_out.amount_commitment, false, "ring secret member doesn't match with the stake output");
}
#endif
crypto::scalar_t pseudo_out_blinding_mask = crypto::scalar_t::random();
crypto::point_t pseudo_out_amount_commitment = td.m_amount * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G;
sig.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
crypto::hash tx_hash_for_sig = get_transaction_hash(b.miner_tx);
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
r = crypto::generate_key_derivation(source_tx_pub_key, m_account.get_keys().view_secret_key, derivation);
WLT_CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed, tid: " << pe.wallet_index << ", pe.tx_id: " << pe.tx_id);
crypto::secret_key secret_x = AUTO_VAL_INIT(secret_x);
crypto::derive_secret_key(derivation, pe.tx_out_index, m_account.get_keys().spend_secret_key, secret_x);
crypto::hash tx_hash_for_sig = get_transaction_hash(b.miner_tx); // TODO @#@# change to block hash after the corresponding test is made
uint8_t err = 0;
r = crypto::zarcanum_generate_proof(tx_hash_for_sig, cxt.kernel_hash, ring, pseudo_out_amount_commitment, cxt.last_pow_block_id_hashed,
pe.keyimage, secret_x, cxt.secret_q, secret_index, pseudo_out_blinding_mask, td.m_amount, *td.m_opt_blinding_mask,
r = crypto::zarcanum_generate_proof(tx_hash_for_sig, cxt.kernel_hash, ring, cxt.last_pow_block_id_hashed, cxt.sk.kimage,
secret_x, cxt.secret_q, secret_index, blinding_masks_sum, cxt.stake_amount, cxt.stake_out_blinding_mask,
static_cast<crypto::zarcanum_proof&>(sig), &err);
WLT_CHECK_AND_ASSERT_MES(r, false, "zarcanum_generate_proof failed, err: " << (int)err);
@ -3821,26 +3815,17 @@ bool wallet2::fill_mining_context(mining_context& ctx)
{
currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request pos_details_req = AUTO_VAL_INIT(pos_details_req);
currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response pos_details_resp = AUTO_VAL_INIT(pos_details_resp);
ctx.status = API_RETURN_CODE_NOT_FOUND;
m_core_proxy->call_COMMAND_RPC_GET_POS_MINING_DETAILS(pos_details_req, pos_details_resp);
if (pos_details_resp.status != API_RETURN_CODE_OK)
return false;
ctx.basic_diff.assign(pos_details_resp.pos_basic_difficulty);
ctx.sk = AUTO_VAL_INIT(ctx.sk);
ctx.sk.stake_modifier = pos_details_resp.sm;
if (is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM))
{
// Zarcanum (PoS with hidden amounts)
ctx.zarcanum = true;
ctx.last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, ctx.sk.stake_modifier.last_pow_id);
ctx.z_l_div_z_D = crypto::zarcanum_precalculate_z_l_div_z_D(ctx.basic_diff);
}
ctx = mining_context{};
ctx.init(wide_difficulty_type(pos_details_resp.pos_basic_difficulty), pos_details_resp.sm, is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM));
ctx.last_block_hash = pos_details_resp.last_block_hash;
ctx.is_pos_allowed = pos_details_resp.pos_mining_allowed;
ctx.starter_timestamp = pos_details_resp.starter_timestamp;
ctx.status = API_RETURN_CODE_OK;
ctx.status = API_RETURN_CODE_NOT_FOUND;
return true;
}
//------------------------------------------------------------------
@ -3887,76 +3872,17 @@ void wallet2::do_pos_mining_prepare_entry(mining_context& context, size_t transf
CHECK_AND_ASSERT_MES_NO_RET(transfer_index < m_transfers.size(), "transfer_index is out of bounds: " << transfer_index);
const transfer_details& td = m_transfers[transfer_index];
// pre build kernel
context.sk.kimage = td.m_key_image;
crypto::scalar_t blinding_mask{};
if (td.m_opt_blinding_mask)
blinding_mask = *td.m_opt_blinding_mask;
if (context.zarcanum)
{
crypto::point_t R(get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx));
crypto::scalar_t v = m_account.get_keys().view_secret_key;
context.secret_q = v * crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_SECRET_Q, v * R);
}
context.prepare_entry(td.amount(), td.m_key_image, get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx), td.m_internal_output_index,
blinding_mask, m_account.get_keys().view_secret_key);
}
//------------------------------------------------------------------
bool wallet2::do_pos_mining_iteration(mining_context& context, size_t transfer_index, uint64_t ts)
{
CHECK_AND_NO_ASSERT_MES(transfer_index < m_transfers.size(), false, "transfer_index is out of bounds: " << transfer_index);
const transfer_details& td = m_transfers[transfer_index];
// update stake kernel and calculate it's hash
context.sk.block_timestamp = ts;
{
PROFILE_FUNC("calc_hash");
context.kernel_hash = crypto::cn_fast_hash(&context.sk, sizeof(context.sk));
}
const uint64_t stake_amount = td.amount();
bool found = false;
if (context.zarcanum && td.is_zc())
{
crypto::mp::uint256_t lhs;
crypto::mp::uint512_t rhs;
{
PROFILE_FUNC("check_zarcanum");
found = crypto::zarcanum_check_main_pos_inequality(context.kernel_hash, *td.m_opt_blinding_mask, context.secret_q, context.last_pow_block_id_hashed, context.z_l_div_z_D, stake_amount, lhs, rhs);
++context.iterations_processed;
}
if (found)
{
found = true;
LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(stake_amount) << ", gindex: " << td.m_global_output_index << ENDL
<< "difficulty: " << context.basic_diff << ENDL
<< "kernel info: " << ENDL
<< print_stake_kernel_info(context.sk)
<< "kernel_hash: " << context.kernel_hash << ENDL
<< "lhs: " << lhs << ENDL
<< "rhs: " << rhs
, LOG_LEVEL_0);
}
}
else
{
// old PoS with non-hidden amounts
currency::wide_difficulty_type final_diff = context.basic_diff / stake_amount;
{
PROFILE_FUNC("check_hash");
found = currency::check_hash(context.kernel_hash, final_diff);
++context.iterations_processed;
}
if (found)
{
LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(stake_amount)<< ", gindex: " << td.m_global_output_index << ENDL
<< "difficulty: " << context.basic_diff << ", final_diff: " << final_diff << ENDL
<< "kernel info: " << ENDL
<< print_stake_kernel_info(context.sk)
<< "kernel_hash(proof): " << context.kernel_hash,
LOG_LEVEL_0);
}
}
return found;
return context.do_iteration(ts);
}
//-------------------------------
bool wallet2::reset_history()
@ -4039,7 +3965,7 @@ bool wallet2::build_minted_block(const mining_context& cxt, const currency::acco
//else
//{
// old fashioned non-hidden amount PoS scheme
res = prepare_and_sign_pos_block(cxt, b, tmpl_req.pe);
res = prepare_and_sign_pos_block(cxt, b, tmpl_req.pe, tmpl_rsp.blinding_masks_sum);
WLT_CHECK_AND_ASSERT_MES(res, false, "Failed to prepare_and_sign_pos_block");
//}
@ -4375,7 +4301,7 @@ void wallet2::dump_trunsfers(std::stringstream& ss, bool verbose) const
else
{
boost::io::ios_flags_saver ifs(ss);
ss << "index amount spent_h g_index block block_ts flg tx out# key image" << ENDL;
ss << "index amount spent_h g_index block block_ts flg tx out# key image" << ENDL;
for (size_t i = 0; i != m_transfers.size(); i++)
{
const transfer_details& td = m_transfers[i];

View file

@ -43,6 +43,7 @@
#include "common/pod_array_file_container.h"
#include "wallet_chain_shortener.h"
#include "tor-connect/torlib/tor_lib_iface.h"
#include "currency_core/pos_mining.h"
#define WALLET_DEFAULT_TX_SPENDABLE_AGE 10
@ -412,28 +413,18 @@ namespace tools
uint64_t m_unlock_time = 0;
};
struct mining_context
struct mining_context : public currency::pos_mining_context
{
std::string status;
bool is_pos_allowed = false;;
bool zarcanum = false;
bool is_pos_allowed = false;
uint64_t index = 0; // index in m_transfers
uint64_t stake_unlock_time = 0;
//uint64_t block_timestamp;
uint64_t height = 0;
uint64_t starter_timestamp = 0;
crypto::hash last_block_hash = currency::null_hash;
crypto::scalar_t last_pow_block_id_hashed; // Zarcanum notation: f'
crypto::scalar_t secret_q; // Zarcanum notation: q
boost::multiprecision::uint256_t z_l_div_z_D; // Zarcanum notation: z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252)
crypto::hash kernel_hash; // Zarcanum notation: h
currency::wide_difficulty_type basic_diff;
currency::stake_kernel sk;
uint64_t iterations_processed = 0;
uint64_t total_items_checked = 0;
uint64_t total_amount_checked = 0;
@ -699,7 +690,7 @@ namespace tools
bool fill_mining_context(mining_context& ctx);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
std::string get_transfers_str(bool include_spent /*= true*/, bool include_unspent /*= true*/) const;
std::string get_transfers_str(bool include_spent = true, bool include_unspent = true) const;
// Returns all payments by given id in unspecified order
void get_payments(const std::string& payment_id, std::list<payment_details>& payments, uint64_t min_height = 0) const;
@ -840,7 +831,7 @@ namespace tools
//next functions in public area only becausce of test_generator
//TODO: Need refactoring - remove it back to private zone
void set_genesis(const crypto::hash& genesis_hash);
bool prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const currency::pos_entry& pe) const;
bool prepare_and_sign_pos_block(const mining_context& cxt, currency::block& b, const currency::pos_entry& pe, const crypto::scalar_t& blinding_masks_sum) const;
void process_new_blockchain_entry(const currency::block& b,
const currency::block_direct_data_entry& bche,
const crypto::hash& bl_id,
@ -968,7 +959,7 @@ private:
std::string get_alias_for_address(const std::string& addr);
std::vector<std::string> get_aliases_for_address(const std::string& addr);
bool is_connected_to_net();
bool is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time) const;
bool is_transfer_okay_for_pos(const transfer_details& tr, bool is_zarcanum_hf, uint64_t& stake_unlock_time) const;
bool scan_unconfirmed_outdate_tx();
const currency::transaction& get_transaction_by_id(const crypto::hash& tx_hash);
void rise_on_transfer2(const wallet_public::wallet_transfer_info& wti);
@ -1342,7 +1333,7 @@ namespace tools
auto& tr = m_transfers[transfer_index];
uint64_t stake_unlock_time = 0;
if (!is_transfer_okay_for_pos(tr, stake_unlock_time))
if (!is_transfer_okay_for_pos(tr, cxt.zarcanum, stake_unlock_time))
continue;

View file

@ -87,7 +87,7 @@ bool block_template_against_txs_size::c1(currency::core& c, size_t ev_index, con
wide_difficulty_type diff = 0;
uint64_t height = 0;
g_block_txs_total_size = txs_total_size; // passing an argument to custom_fill_block_template_func via global variable (not perfect but works well)
r = bcs.create_block_template(b, miner_addr, miner_addr, diff, height, ex_nonce, is_pos != 0, pe, &custom_fill_block_template_func);
r = bcs.create_block_template(miner_addr, miner_addr, ex_nonce, is_pos != 0, pe, &custom_fill_block_template_func, b, diff, height);
CHECK_AND_ASSERT_MES(r, false, "create_block_template failed, txs_total_size = " << txs_total_size);
CHECK_AND_ASSERT_MES(height == top_block_height + 1, false, "Incorrect height: " << height << ", expected: " << top_block_height + 1 << ", txs_total_size = " << txs_total_size);

View file

@ -136,7 +136,7 @@ bool gen_chain_switch_pow_pos::generate(std::vector<test_event_entry>& events) c
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), alice.get_public_address());

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2022 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
@ -25,8 +25,6 @@
#include "wallet_test_core_proxy.h"
#include "pos_block_builder.h"
//using namespace std;
using namespace epee;
using namespace currency;
@ -34,7 +32,7 @@ using namespace currency;
#define POS_DIFF_UP_TIMESTAMP_DELTA (DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN*2/3)
std::atomic<int64_t> test_core_time::m_time_shift;
test_gentime_settings test_generator::m_test_gentime_settings_default = test_gentime_settings(tests_digits_split_strategy, CURRENCY_MINER_TX_MAX_OUTS, WALLET_MAX_ALLOWED_OUTPUT_AMOUNT, DEFAULT_DUST_THRESHOLD);
const test_gentime_settings test_generator::m_test_gentime_settings_default{};
test_gentime_settings test_generator::m_test_gentime_settings = test_generator::m_test_gentime_settings_default;
crypto::signature create_invalid_signature()
@ -276,6 +274,7 @@ bool test_generator::construct_block(currency::block& blk,
blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx);
size_t target_block_size = txs_size + 0; // zero means no cost for ordinary coinbase
crypto::scalar_t blinding_masks_sum = 0;
while (true)
{
r = construct_miner_tx(height, misc_utils::median(block_sizes),
@ -289,7 +288,8 @@ bool test_generator::construct_block(currency::block& blk,
blobdata(),
test_generator::get_test_gentime_settings().miner_tx_max_outs,
static_cast<bool>(coin_stake_sources.size()),
pe);
pe,
&blinding_masks_sum);
CHECK_AND_ASSERT_MES(r, false, "construct_miner_tx failed");
size_t coinbase_size = get_object_blobsize(blk.miner_tx);
@ -330,7 +330,7 @@ bool test_generator::construct_block(currency::block& blk,
else
{
//need to build pos block
r = sign_block(blk, pe, *wallets[won_walled_index].wallet, wallets[won_walled_index].mining_context, blocks, oi);
r = sign_block(wallets[won_walled_index].mining_context, pe, *wallets[won_walled_index].wallet, blinding_masks_sum, blk);
CHECK_AND_ASSERT_MES(r, false, "Failed to find_kernel_and_sign()");
}
@ -347,14 +347,13 @@ bool test_generator::construct_block(currency::block& blk,
return true;
}
bool test_generator::sign_block(currency::block& b,
pos_entry& pe,
tools::wallet2& w,
const tools::wallet2::mining_context& mining_context,
const std::vector<const block_info*>& blocks,
const outputs_index& oi)
bool test_generator::sign_block(const tools::wallet2::mining_context& mining_context,
const pos_entry& pe,
const tools::wallet2& w,
const crypto::scalar_t& blinding_masks_sum,
currency::block& b)
{
bool r = w.prepare_and_sign_pos_block(mining_context, b, pe);
bool r = w.prepare_and_sign_pos_block(mining_context, b, pe, blinding_masks_sum);
CHECK_AND_ASSERT_MES(r, false, "prepare_and_sign_pos_block failed");
return true;
}
@ -576,8 +575,9 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo
std::vector<uint64_t>& coinbase_outs = txs_outs[currency::get_transaction_hash(blocks[h]->b.miner_tx)];
for (size_t out_i = 0; out_i != blocks[h]->b.miner_tx.vout.size(); out_i++)
{
coinbase_outs.push_back(index[boost::get<currency::tx_out_bare>(blocks[h]->b.miner_tx.vout[out_i]).amount].size());
index[boost::get<currency::tx_out_bare>(blocks[h]->b.miner_tx.vout[out_i]).amount].push_back(std::tuple<size_t, size_t, size_t>(h, 0, out_i));
uint64_t amount = get_amount_from_variant(blocks[h]->b.miner_tx.vout[out_i]);
coinbase_outs.push_back(index[amount].size());
index[amount].push_back(std::tuple<size_t, size_t, size_t>(h, 0, out_i));
}
for (size_t tx_index = 0; tx_index != blocks[h]->m_transactions.size(); tx_index++)
@ -585,14 +585,16 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo
std::vector<uint64_t>& tx_outs_indx = txs_outs[currency::get_transaction_hash(blocks[h]->m_transactions[tx_index])];
for (size_t out_i = 0; out_i != blocks[h]->m_transactions[tx_index].vout.size(); out_i++)
{
tx_outs_indx.push_back(index[boost::get<currency::tx_out_bare>(blocks[h]->m_transactions[tx_index].vout[out_i]).amount].size());
index[boost::get<currency::tx_out_bare>(blocks[h]->m_transactions[tx_index].vout[out_i]).amount].push_back(std::tuple<size_t, size_t, size_t>(h, tx_index + 1, out_i));
uint64_t amount = get_amount_from_variant(blocks[h]->m_transactions[tx_index].vout[out_i]);
tx_outs_indx.push_back(index[amount].size());
index[amount].push_back(std::tuple<size_t, size_t, size_t>(h, tx_index + 1, out_i));
}
}
}
return true;
}
//------------------------------------------------------------------
/* not used, consider removing
bool test_generator::get_output_details_by_global_index(const test_generator::blockchain_vector& blck_chain,
const test_generator::outputs_index& indexes,
uint64_t amount,
@ -628,6 +630,7 @@ bool test_generator::get_output_details_by_global_index(const test_generator::bl
output_key = boost::get<currency::txout_to_key>(boost::get<tx_out_bare>(tx->vout[tx_out_index]).target).key;
return true;
}
*/
//------------------------------------------------------------------
bool test_generator::build_stake_modifier(stake_modifier_type& sm, const test_generator::blockchain_vector& blck_chain)
@ -926,42 +929,44 @@ bool test_generator::construct_pow_block_with_alias_info_in_coinbase(const accou
//------------------------------------------------------------------------------
struct output_index {
const currency::txout_target_v out;
uint64_t amount;
size_t blk_height; // block height
size_t tx_no; // index of transaction in block
size_t out_no; // index of out in transaction
size_t idx;
bool spent;
const currency::block *p_blk;
const currency::transaction *p_tx;
struct output_index
{
const currency::tx_out_v out_v;
uint64_t amount;
size_t tx_no; // index of transaction in block
size_t out_no; // index of out in transaction
size_t idx; // global index
bool spent; // was it spent?
bool zc_out; // is it a ZC output?
const currency::block *p_blk;
const currency::transaction *p_tx;
crypto::scalar_t blinding_mask; // zc outs only
output_index(const currency::txout_target_v &_out, uint64_t _a, size_t _h, size_t tno, size_t ono, const currency::block *_pb, const currency::transaction *_pt)
: out(_out), amount(_a), blk_height(_h), tx_no(tno), out_no(ono), idx(0), spent(false), p_blk(_pb), p_tx(_pt) { }
output_index(const currency::tx_out_v &_out_v, uint64_t _a, size_t tno, size_t ono, const currency::block *_pb, const currency::transaction *_pt)
: out_v(_out_v), amount(_a), tx_no(tno), out_no(ono), idx(0), spent(false), zc_out(false), p_blk(_pb), p_tx(_pt), blinding_mask(0)
{}
output_index(const output_index &other)
: out(other.out), amount(other.amount), blk_height(other.blk_height), tx_no(other.tx_no), out_no(other.out_no), idx(other.idx), spent(other.spent), p_blk(other.p_blk), p_tx(other.p_tx) { }
output_index(const output_index &other) = default;
const std::string toString() const {
std::stringstream ss;
const std::string to_string() const
{
std::stringstream ss;
ss << "output_index{"
<< " tx_no=" << tx_no
<< " out_no=" << out_no
<< " amount=" << amount
<< " idx=" << idx
<< " spent=" << spent
<< " zc_out=" << zc_out
<< "}";
return ss.str();
}
ss << "output_index{blk_height=" << blk_height
<< " tx_no=" << tx_no
<< " out_no=" << out_no
<< " amount=" << amount
<< " idx=" << idx
<< " spent=" << spent
<< "}";
return ss.str();
}
output_index& operator=(const output_index& other)
{
new(this) output_index(other);
return *this;
}
output_index& operator=(const output_index& other) = default;
/*{
new(this) output_index(other);
return *this;
}*/
};
typedef std::map<uint64_t, std::vector<size_t> > map_output_t; // amount -> [N -> global out index]
@ -984,16 +989,18 @@ 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);
std::vector<const transaction*> vtx;
vtx.push_back(&blk.miner_tx);
for (const crypto::hash &h : blk.tx_hashes)
{
const map_hash2tx_t::const_iterator cit = mtx.find(h);
CHECK_AND_ASSERT_MES(cit != mtx.end(), false, "block at height " << get_block_height(blk) << " contains a reference to unknown tx " << h);
CHECK_AND_ASSERT_MES(cit != mtx.end(), false, "block at height " << height << " contains a reference to unknown tx " << h);
vtx.push_back(cit->second);
}
@ -1006,18 +1013,37 @@ bool init_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const
for (size_t j = 0; j < tx.vout.size(); ++j)
{
const tx_out_bare &out = boost::get<tx_out_bare>(tx.vout[j]);
output_index oi(out.target, out.amount, boost::get<txin_gen>(*blk.miner_tx.vin.begin()).height, i, j, &blk, vtx[i]);
VARIANT_SWITCH_BEGIN(tx.vout[j])
VARIANT_CASE_CONST(tx_out_bare, out)
if (out.target.type() == typeid(txout_to_key))
{
std::vector<output_index>& outs_vec = outs[out.amount];
size_t out_global_idx = outs_vec.size();
output_index oi(out, out.amount, i, j, &blk, vtx[i]);
oi.idx = out_global_idx;
outs_vec.emplace_back(std::move(oi));
// Is out to me?
if (is_out_to_acc(acc_keys.account_address, boost::get<txout_to_key>(out.target), derivation, j))
outs_mine[out.amount].push_back(out_global_idx);
}
VARIANT_CASE_CONST(tx_out_zarcanum, out)
std::vector<output_index>& outs_vec = outs[0]; // amount = 0 for ZC outs
size_t out_global_idx = outs_vec.size();
if (out.target.type() == typeid(txout_to_key))
{
outs[out.amount].push_back(oi);
size_t tx_global_idx = outs[out.amount].size() - 1;
outs[out.amount][tx_global_idx].idx = tx_global_idx;
// Is out to me?
if (is_out_to_acc(acc_keys, boost::get<txout_to_key>(out.target), derivation, j))
outs_mine[out.amount].push_back(tx_global_idx);
}
output_index oi(out, 0 /* amount */, i, j, &blk, vtx[i]);
oi.zc_out = true;
oi.idx = out_global_idx;
outs_vec.emplace_back(std::move(oi));
uint64_t decoded_amount = 0;
crypto::scalar_t decoded_blinding_mask{};
if (is_out_to_acc(acc_keys.account_address, out, derivation, j, decoded_amount, decoded_blinding_mask))
{
outs_vec.back().amount = decoded_amount;
outs_vec.back().blinding_mask = decoded_blinding_mask;
outs_mine[0].push_back(out_global_idx);
}
VARIANT_SWITCH_END()
}
}
}
@ -1027,59 +1053,56 @@ bool init_output_indices(map_output_idx_t& outs, map_output_t& outs_mine, const
bool init_spent_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& from)
{
for(const map_output_t::value_type &o : outs_mine)
// 1. make a hashset of spend key images
std::unordered_set<crypto::key_image> spent_key_images;
auto add_key_images_from_tx = [&](const transaction& tx) -> bool {
for(const txin_v& in: tx.vin)
{
for (size_t i = 0; i < o.second.size(); ++i)
{
output_index &oi = outs[o.first][o.second[i]];
// construct key image for this output
crypto::key_image out_ki;
keypair in_ephemeral;
generate_key_image_helper(from, get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, out_ki);
// lookup for this key image in the events std::vector
for(auto& tx_pair : mtx)
{
const transaction& tx = *tx_pair.second;
for(const txin_v &in : tx.vin)
{
if (typeid(txin_to_key) == in.type())
{
const txin_to_key &itk = boost::get<txin_to_key>(in);
if (itk.k_image == out_ki)
oi.spent = true;
}
}
}
// check whether this key image has been spent in miner tx of a PoS block
// TODO change this check to simply adding PoS miner tx to mtx map
for (auto& b : blockchain)
{
if (!is_pos_block(b))
continue;
for (const txin_v &in : b.miner_tx.vin)
{
if (in.type() == typeid(txin_to_key))
{
const txin_to_key &itk = boost::get<txin_to_key>(in);
if (itk.k_image == out_ki)
oi.spent = true;
}
}
}
}
crypto::key_image ki{};
if (get_key_image_from_txin_v(in, ki))
if (!spent_key_images.insert(ki).second)
return false;
}
return true;
};
for(auto& tx_pair : mtx)
add_key_images_from_tx(*tx_pair.second); // some key images may be added more than once (because invalid txs can't be detected here), ignore that
for (auto& b : blockchain)
{
if (is_pos_block(b))
CHECK_AND_ASSERT_MES(add_key_images_from_tx(b.miner_tx), false, "insertion of spent key image failed for miner tx " << get_transaction_hash(b.miner_tx));
}
// 2. check outputs from outs_mine against spent key images
if (spent_key_images.empty())
return true;
for(const map_output_t::value_type &o : outs_mine)
{
for (size_t i = 0; i < o.second.size(); ++i)
{
output_index &oi = outs[o.first][o.second[i]];
// construct key image for this output
crypto::key_image out_ki;
keypair in_ephemeral;
generate_key_image_helper(from, get_tx_pub_key_from_extra(*oi.p_tx), oi.out_no, in_ephemeral, out_ki); // TODO: store ki and secret ephemeral for further use
if (spent_key_images.count(out_ki) != 0)
oi.spent = true;
}
}
return true;
}
bool fill_output_entries(std::vector<output_index>& out_indices,
size_t sender_out, size_t nmix, uint64_t& real_entry_idx,
std::vector<tx_source_entry::output_entry>& output_entries,
bool use_ref_by_id)
bool fill_output_entries(const std::vector<output_index>& out_indices, size_t real_out_index, size_t nmix, bool check_for_unlocktime, bool use_ref_by_id,
uint64_t next_block_height, uint64_t head_block_ts, uint64_t& real_entry_idx, std::vector<tx_source_entry::output_entry>& output_entries)
{
// use_ref_by_id = true; // <-- HINT: this could be used to enforce using ref_by_id across all the tests if needed
if (out_indices.size() <= nmix)
return false;
@ -1092,7 +1115,7 @@ bool fill_output_entries(std::vector<output_index>& out_indices,
continue;
bool append = false;
if (i == sender_out)
if (i == real_out_index)
{
append = true;
sender_out_found = true;
@ -1100,8 +1123,30 @@ bool fill_output_entries(std::vector<output_index>& out_indices,
}
else if (0 < rest)
{
if(boost::get<txout_to_key>(oi.out).mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX || boost::get<txout_to_key>(oi.out).mix_attr > nmix+1)
continue;
uint8_t mix_attr = 0;
if (get_mix_attr_from_tx_out_v(oi.out_v, mix_attr))
{
if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX || mix_attr > nmix + 1)
continue;
if (check_for_unlocktime)
{
uint64_t unlock_time = get_tx_max_unlock_time(*oi.p_tx);
if (unlock_time < CURRENCY_MAX_BLOCK_NUMBER)
{
//interpret as block index
if (unlock_time > next_block_height)
continue;
}
else
{
//interpret as time
if (unlock_time > head_block_ts + DIFFICULTY_TOTAL_TARGET)
continue;
}
}
}
--rest;
append = true;
@ -1109,20 +1154,28 @@ bool fill_output_entries(std::vector<output_index>& out_indices,
if (append)
{
tx_source_entry::output_entry oe = AUTO_VAL_INIT(oe);
const txout_to_key& otk = boost::get<txout_to_key>(oi.out);
if (use_ref_by_id) // <-- HINT: this could be replaced by 'true' to enforce using ref_by_id across all the tests if needed
txout_ref_v out_ref_v{};
if (use_ref_by_id)
{
ref_by_id rbi = AUTO_VAL_INIT(rbi);
rbi.n = oi.out_no;
rbi.tx_id = get_transaction_hash(*oi.p_tx);
oe = tx_source_entry::output_entry(rbi, otk.key);
out_ref_v = rbi;
}
else
{
oe = tx_source_entry::output_entry(oi.idx, otk.key);
out_ref_v = oi.idx;
}
output_entries.push_back(oe);
VARIANT_SWITCH_BEGIN(oi.out_v)
VARIANT_CASE_CONST(tx_out_bare, ob)
VARIANT_SWITCH_BEGIN(ob.target)
VARIANT_CASE_CONST(txout_to_key, otk)
output_entries.emplace_back(out_ref_v, otk.key);
VARIANT_SWITCH_END()
VARIANT_CASE_CONST(tx_out_zarcanum, ozc)
output_entries.emplace_back(out_ref_v, ozc.stealth_address, ozc.concealing_point, ozc.amount_commitment);
VARIANT_SWITCH_END()
}
}
@ -1141,110 +1194,117 @@ bool fill_tx_sources(std::vector<currency::tx_source_entry>& sources, const std:
const currency::block& blk_head, const currency::account_keys& from, uint64_t amount, size_t nmix, const std::vector<currency::tx_source_entry>& sources_to_avoid,
bool check_for_spends, bool check_for_unlocktime, bool use_ref_by_id, uint64_t* p_sources_amount_found /* = nullptr */)
{
map_output_idx_t outs;
map_output_t outs_mine;
map_output_idx_t outs;
map_output_t outs_mine;
std::vector<currency::block> blockchain;
map_hash2tx_t mtx;
if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)))
return false;
std::vector<currency::block> blockchain;
map_hash2tx_t mtx;
if (!find_block_chain(events, blockchain, mtx, get_block_hash(blk_head)))
return false;
if (!init_output_indices(outs, outs_mine, blockchain, mtx, from))
return false;
if (!init_output_indices(outs, outs_mine, blockchain, mtx, from))
return false;
if(check_for_spends)
if(check_for_spends)
{
if (!init_spent_output_indices(outs, outs_mine, blockchain, mtx, from))
return false;
}
// mark some outputs as spent to avoid their using
for (const auto& s : sources_to_avoid)
{
for (const auto& s_outputs_el : s.outputs) // avoid all outputs, including fake mix-ins
{
if (!init_spent_output_indices(outs, outs_mine, blockchain, mtx, from))
return false;
}
// mark some outputs as spent to avoid their using
for (const auto& s : sources_to_avoid)
{
for (const auto& s_outputs_el : s.outputs) // avoid all outputs, including fake mix-ins
txout_ref_v sout = s_outputs_el.out_reference;
if (sout.type().hash_code() == typeid(uint64_t).hash_code()) // output by global index
{
txout_ref_v sout = s_outputs_el.out_reference;
if (sout.type().hash_code() == typeid(uint64_t).hash_code()) // output by global index
uint64_t gindex = boost::get<uint64_t>(sout);
auto& outs_by_amount = outs[s.amount];
if (gindex >= outs_by_amount.size())
return false;
outs_by_amount[gindex].spent = true;
}
else if (sout.type().hash_code() == typeid(ref_by_id).hash_code()) // output by ref_by_id
{
ref_by_id out_ref_by_id = boost::get<ref_by_id>(sout);
const auto it = mtx.find(out_ref_by_id.tx_id);
if (it == mtx.end())
return false;
const transaction* p_tx = it->second;
for (auto& e : outs[s.amount]) // linear search by transaction among all outputs with such amount
{
uint64_t gindex = boost::get<uint64_t>(sout);
auto& outs_by_amount = outs[s.amount];
if (gindex >= outs_by_amount.size())
return false;
outs_by_amount[gindex].spent = true;
}
else if (sout.type().hash_code() == typeid(ref_by_id).hash_code()) // output by ref_by_id
{
ref_by_id out_ref_by_id = boost::get<ref_by_id>(sout);
const auto it = mtx.find(out_ref_by_id.tx_id);
if (it == mtx.end())
return false;
const transaction* p_tx = it->second;
for (auto& e : outs[s.amount]) // linear search by transaction among all outputs with such amount
if (e.p_tx == p_tx)
{
if (e.p_tx == p_tx)
{
e.spent = true;
p_tx = nullptr; // means 'found'
break;
}
e.spent = true;
p_tx = nullptr; // means 'found'
break;
}
if (p_tx != nullptr)
return false; // output, referring by ref_by_id was not found
}
else
{
return false; // unknown output type
}
if (p_tx != nullptr)
return false; // output, referring by ref_by_id was not found
}
else
{
return false; // unknown output type
}
}
}
// Iterate in reverse is more efficiency
uint64_t sources_amount = 0;
bool sources_found = false;
BOOST_REVERSE_FOREACH(const map_output_t::value_type o, outs_mine)
uint64_t head_block_ts = get_actual_timestamp(blk_head);
uint64_t next_block_height = blockchain.size();
// Iterate in reverse is more efficiency
uint64_t sources_amount = 0;
bool sources_found = false;
BOOST_REVERSE_FOREACH(const map_output_t::value_type o, outs_mine)
{
for (size_t i = 0; i < o.second.size() && !sources_found; ++i)
{
for (size_t i = 0; i < o.second.size() && !sources_found; ++i)
size_t sender_out = o.second[i];
const output_index& oi = outs[o.first][sender_out];
if (oi.spent)
continue;
if (check_for_unlocktime)
{
uint64_t unlock_time = currency::get_tx_max_unlock_time(*oi.p_tx);
if (unlock_time < CURRENCY_MAX_BLOCK_NUMBER)
{
size_t sender_out = o.second[i];
const output_index& oi = outs[o.first][sender_out];
if (oi.spent)
continue;
if (check_for_unlocktime)
{
if (currency::get_tx_max_unlock_time(*oi.p_tx) < CURRENCY_MAX_BLOCK_NUMBER)
{
//interpret as block index
if (currency::get_tx_max_unlock_time(*oi.p_tx) > blockchain.size())
continue;
}
else
{
//interpret as time
}
}
currency::tx_source_entry ts = AUTO_VAL_INIT(ts);
ts.amount = oi.amount;
ts.real_output_in_tx_index = oi.out_no;
ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // incoming tx public key
if (!fill_output_entries(outs[o.first], sender_out, nmix, ts.real_output, ts.outputs, use_ref_by_id))
continue;
sources.push_back(ts);
sources_amount += ts.amount;
sources_found = amount <= sources_amount;
//interpret as block index
if (unlock_time > next_block_height)
continue;
}
else
{
//interpret as time
if (unlock_time > head_block_ts + DIFFICULTY_TOTAL_TARGET)
continue;
}
}
if (sources_found)
break;
currency::tx_source_entry ts = AUTO_VAL_INIT(ts);
ts.amount = oi.amount;
ts.real_out_amount_blinding_mask = oi.blinding_mask;
ts.real_output_in_tx_index = oi.out_no;
ts.real_out_tx_key = get_tx_pub_key_from_extra(*oi.p_tx); // source tx public key
if (!fill_output_entries(outs[o.first], sender_out, nmix, check_for_unlocktime, use_ref_by_id, next_block_height, head_block_ts, ts.real_output, ts.outputs))
continue;
sources.push_back(ts);
sources_amount += ts.amount;
sources_found = amount <= sources_amount;
}
if (p_sources_amount_found != nullptr)
*p_sources_amount_found = sources_amount;
if (sources_found)
break;
}
return sources_found;
if (p_sources_amount_found != nullptr)
*p_sources_amount_found = sources_amount;
return sources_found;
}
bool fill_tx_sources_and_destinations(const std::vector<test_event_entry>& events, const block& blk_head,
@ -1264,9 +1324,9 @@ bool fill_tx_sources_and_destinations(const std::vector<test_event_entry>& event
uint64_t source_amount_found = 0;
bool r = fill_tx_sources(sources, events, blk_head, from, amount + fee, nmix, std::vector<currency::tx_source_entry>(), check_for_spends, check_for_unlocktime, use_ref_by_id, &source_amount_found);
CHECK_AND_ASSERT_MES(r, false, "couldn't fill transaction sources: " << ENDL <<
CHECK_AND_ASSERT_MES(r, false, "couldn't fill transaction sources (nmix = " << nmix << "): " << ENDL <<
" required: " << print_money(amount + fee) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * (amount + fee) / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE" << ENDL <<
" unspent coins: " << print_money(source_amount_found) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * source_amount_found / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE" << ENDL <<
" found coins: " << print_money(source_amount_found) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * source_amount_found / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE" << ENDL <<
" lack of coins: " << print_money(amount + fee - source_amount_found) << " = " << std::fixed << std::setprecision(1) << ceil(1.0 * (amount + fee - source_amount_found) / TESTS_DEFAULT_FEE) << " x TESTS_DEFAULT_FEE"
);
@ -1301,6 +1361,18 @@ bool fill_tx_sources_and_destinations(const std::vector<test_event_entry>& event
case tests_digits_split_strategy:
tools::detail::digit_split_strategy(dsts, change_dst, tgs.dust_threshold, destinations, dust, tgs.tx_max_out_amount);
break;
case tests_random_split_strategy:
{
size_t outs_count = cache_back > 0 ? 2 : 1;
if (outs_count < tgs.rss_min_number_of_outputs)
{
// decompose both target and cache back amounts
// TODO: support tgs.tx_max_out_amount
decompose_amount_randomly(amount, [&](uint64_t a){ destinations.emplace_back(a, to.back()); }, tgs.rss_min_number_of_outputs, tgs.rss_num_digits_to_keep);
decompose_amount_randomly(cache_back, [&](uint64_t a){ destinations.emplace_back(a, from.account_address); }, tgs.rss_min_number_of_outputs, tgs.rss_num_digits_to_keep);
}
}
break;
default:
CHECK_AND_ASSERT_MES(false, false, "Invalid split strategy set in gentime settings");
}
@ -1436,7 +1508,7 @@ bool construct_tx_to_key(const currency::hard_forks_descriptor& hf,
std::vector<tx_destination_entry> destinations;
if (!fill_tx_sources_and_destinations(events, blk_head, from.get_keys(), to.get_public_address(), amount, fee, nmix, sources, destinations, check_for_spends, check_for_unlocktime))
return false;
uint64_t tx_version = currency::get_tx_version(get_block_height(blk_head), hf);
uint64_t tx_version = currency::get_tx_version(get_block_height(blk_head) + 1, hf); // assuming the tx will be in the next block (blk_head + 1)
return construct_tx(from.get_keys(), sources, destinations, extr, att, tx, tx_version, sk, 0, mix_attr);
}
@ -1506,33 +1578,36 @@ bool construct_tx_with_many_outputs(const currency::hard_forks_descriptor& hf, s
return construct_tx(keys_from, sources, destinations, empty_attachment, tx, tx_version, 0);
}
uint64_t get_balance(const currency::account_keys& addr, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, bool dbg_log) {
uint64_t res = 0;
std::map<uint64_t, std::vector<output_index> > outs;
std::map<uint64_t, std::vector<size_t> > outs_mine;
uint64_t get_balance(const currency::account_keys& addr, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, bool dbg_log)
{
uint64_t res = 0;
std::map<uint64_t, std::vector<output_index> > outs;
std::map<uint64_t, std::vector<size_t> > outs_mine;
map_hash2tx_t confirmed_txs;
get_confirmed_txs(blockchain, mtx, confirmed_txs);
map_hash2tx_t confirmed_txs;
get_confirmed_txs(blockchain, mtx, confirmed_txs);
if (!init_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
return false;
if (!init_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
return false;
if (!init_spent_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
return false;
if (!init_spent_output_indices(outs, outs_mine, blockchain, confirmed_txs, addr))
return false;
BOOST_FOREACH (const map_output_t::value_type &o, outs_mine) {
for (size_t i = 0; i < o.second.size(); ++i) {
if (outs[o.first][o.second[i]].spent)
continue;
for (const map_output_t::value_type &o : outs_mine)
{
for (size_t i = 0; i < o.second.size(); ++i)
{
if (outs[o.first][o.second[i]].spent)
continue;
output_index& oiv = outs[o.first][o.second[i]];
res += oiv.amount;
if (dbg_log)
LOG_PRINT_L0(oiv.toString());
}
output_index& oiv = outs[o.first][o.second[i]];
res += oiv.amount;
if (dbg_log)
LOG_PRINT_L0(oiv.to_string());
}
}
return res;
return res;
}
uint64_t get_balance(const currency::account_base& addr, const std::vector<currency::block>& blockchain, const map_hash2tx_t& mtx, bool dbg_log)
@ -1562,9 +1637,14 @@ void get_confirmed_txs(const std::vector<currency::block>& blockchain, const map
bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<currency::block>& blockchain, map_hash2tx_t& mtx, const crypto::hash& head)
{
size_t invalid_tx_index = UINT64_MAX;
size_t invalid_block_index = UINT64_MAX;
std::unordered_map<crypto::hash, const block*> block_index;
for(size_t i = 0, sz = events.size(); i < sz; ++i)
{
if (invalid_tx_index == i || invalid_block_index == i)
continue;
const test_event_entry& ev = events[i];
if (typeid(currency::block) == ev.type())
{
@ -1584,6 +1664,14 @@ bool find_block_chain(const std::vector<test_event_entry>& events, std::vector<c
const transaction& tx = boost::get<transaction>(ev);
mtx[get_transaction_hash(tx)] = &tx;
}
else if (test_chain_unit_enchanced::is_event_mark_invalid_block(ev))
{
invalid_block_index = i + 1;
}
else if (test_chain_unit_enchanced::is_event_mark_invalid_tx(ev))
{
invalid_tx_index = i + 1;
}
}
bool b_success = false;
@ -1626,7 +1714,7 @@ bool check_balance_via_wallet(const tools::wallet2& w, const char* account_name,
uint64_t total, unlocked, awaiting_in, awaiting_out, mined;
balance_via_wallet(w, &total, &unlocked, &awaiting_in, &awaiting_out, &mined);
LOG_PRINT_CYAN("Balance for wallet " << account_name << ":" << ENDL <<
LOG_PRINT_CYAN("Balance for wallet " << account_name << " @ height " << w.get_top_block_height() << ":" << ENDL <<
"unlocked: " << print_money(unlocked) << ENDL <<
"awaiting in: " << print_money(awaiting_in) << ENDL <<
"awaiting out: " << print_money(awaiting_out) << ENDL <<
@ -1925,7 +2013,7 @@ bool generate_pos_block_with_given_coinstake(test_generator& generator, const st
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake_tx.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner.get_public_address());
@ -1992,16 +2080,36 @@ bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, c
for (size_t i = 0; i < ptce->tx.vin.size(); ++i)
{
auto& input = ptce->tx.vin[i];
if (input.type() == typeid(txin_to_key))
{
auto& intk = boost::get<txin_to_key>(input);
CHECK_AND_ASSERT_MES(intk.key_offsets.size() == mixin + 1, false, "for input #" << i << " mixin count is " << intk.key_offsets.size() - 1 << ", expected is " << mixin);
}
const std::vector<currency::txout_ref_v>& key_offsets = get_key_offsets_from_txin_v(input);
CHECK_AND_ASSERT_MES(key_offsets.size() == mixin + 1, false, "for input #" << i << " mixin count is " << key_offsets.size() - 1 << ", expected is " << mixin);
}
return true;
}
// randomly shuffles tx_source_entry, restores the correct real_output afterwards
bool shuffle_source_entry(tx_source_entry& se)
{
if (se.outputs.size() < 2)
return true;
tx_source_entry::output_entry real_out_entry = se.outputs[se.real_output]; // store the real one
std::shuffle(se.outputs.begin(), se.outputs.end(), crypto::uniform_random_bit_generator{}); // shuffle
auto it = std::find(se.outputs.begin(), se.outputs.end(), real_out_entry); // where is the real one now?
CHECK_AND_ASSERT_MES(it != se.outputs.end(), false, "cannot find the real one output entry");
se.real_output = it - se.outputs.begin(); // restore the real output index
return true;
}
// randomly shuffles std::vector<tx_source_entry>, restores the correct real_output afterwards
bool shuffle_source_entries(std::vector<tx_source_entry>& sources)
{
for(auto& se : sources)
if (!shuffle_source_entry(se))
return false;
return true;
}
//------------------------------------------------------------------------------
void test_chain_unit_base::register_callback(const std::string& cb_name, verify_callback cb)
@ -2033,6 +2141,11 @@ bool test_chain_unit_base::verify(const std::string& cb_name, currency::core& c,
return cb_it->second(c, ev_index, events);
}
void test_chain_unit_base::on_test_generator_created(test_generator& gen) const
{
gen.set_hardforks(m_hardforks);
}
//------------------------------------------------------------------------------
test_chain_unit_enchanced::test_chain_unit_enchanced()
@ -2055,6 +2168,7 @@ test_chain_unit_enchanced::test_chain_unit_enchanced()
REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, remove_stuck_txs);
REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_offers_count);
REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_hardfork_active);
REGISTER_CALLBACK_METHOD(test_chain_unit_enchanced, check_hardfork_inactive);
}
bool test_chain_unit_enchanced::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
@ -2191,12 +2305,51 @@ bool test_chain_unit_enchanced::check_hardfork_active(currency::core& c, size_t
const std::string& params = boost::get<callback_entry>(events[ev_index]).callback_params;
CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(params, hardfork_id_to_check), false, "hex_to_pod failed, params = " << params);
uint64_t top_block_height = c.get_top_block_height();
if (!c.get_blockchain_storage().get_core_runtime_config().is_hardfork_active_for_height(hardfork_id_to_check, top_block_height))
if (!c.get_blockchain_storage().is_hardfork_active(hardfork_id_to_check))
{
LOG_ERROR("Hardfork #" << hardfork_id_to_check << " is not active yet (top block height is " << top_block_height << ")");
LOG_ERROR("Hardfork #" << hardfork_id_to_check << " is not active yet (top block height is " << c.get_top_block_height() << ")");
return false;
}
return true;
}
bool test_chain_unit_enchanced::check_hardfork_inactive(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
size_t hardfork_id_to_check = 0;
const std::string& params = boost::get<callback_entry>(events[ev_index]).callback_params;
CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(params, hardfork_id_to_check), false, "hex_to_pod failed, params = " << params);
if (c.get_blockchain_storage().is_hardfork_active(hardfork_id_to_check))
{
LOG_ERROR("Hardfork #" << hardfork_id_to_check << " is active, which is not expected (top block height is " << c.get_top_block_height() << ")");
return false;
}
return true;
}
/*static*/ bool test_chain_unit_enchanced::is_event_mark_invalid_block(const test_event_entry& ev, bool use_global_gentime_settings /* = true */)
{
if (use_global_gentime_settings && !test_generator::get_test_gentime_settings().ignore_invalid_blocks)
return false;
if (typeid(callback_entry) != ev.type())
return false;
const callback_entry& ce = boost::get<callback_entry>(ev);
return ce.callback_name == "mark_invalid_block";
}
/*static*/ bool test_chain_unit_enchanced::is_event_mark_invalid_tx(const test_event_entry& ev, bool use_global_gentime_settings /* = true */)
{
if (use_global_gentime_settings && !test_generator::get_test_gentime_settings().ignore_invalid_txs)
return false;
if (typeid(callback_entry) != ev.type())
return false;
const callback_entry& ce = boost::get<callback_entry>(ev);
return ce.callback_name == "mark_invalid_tx";
}

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2022 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
@ -212,17 +212,21 @@ VARIANT_TAG(binary_archive, event_core_time, 0xd1);
typedef boost::variant<currency::block, currency::transaction, currency::account_base, callback_entry, serialized_block, serialized_transaction, event_visitor_settings, event_special_block, event_core_time> test_event_entry;
typedef std::unordered_map<crypto::hash, const currency::transaction*> map_hash2tx_t;
enum test_tx_split_strategy { tests_void_split_strategy, tests_null_split_strategy, tests_digits_split_strategy };
enum test_tx_split_strategy { tests_void_split_strategy, tests_null_split_strategy, tests_digits_split_strategy, tests_random_split_strategy };
struct test_gentime_settings
{
test_gentime_settings(test_tx_split_strategy split_strategy, size_t miner_tx_max_outs, uint64_t tx_max_out_amount, uint64_t dust_threshold)
: split_strategy(split_strategy), miner_tx_max_outs(miner_tx_max_outs), tx_max_out_amount(tx_max_out_amount), dust_threshold(dust_threshold) {}
test_tx_split_strategy split_strategy;
size_t miner_tx_max_outs;
uint64_t tx_max_out_amount;
uint64_t dust_threshold;
test_tx_split_strategy split_strategy = tests_digits_split_strategy;
size_t miner_tx_max_outs = CURRENCY_MINER_TX_MAX_OUTS;
uint64_t tx_max_out_amount = WALLET_MAX_ALLOWED_OUTPUT_AMOUNT;
uint64_t dust_threshold = DEFAULT_DUST_THRESHOLD;
size_t rss_min_number_of_outputs = CURRENCY_TX_MIN_ALLOWED_OUTS; // for random split strategy: min (target) number of tx outputs, one output will be split into this many parts
size_t rss_num_digits_to_keep = CURRENCY_TX_OUTS_RND_SPLIT_DIGITS_TO_KEEP; // for random split strategy: number of digits to keep
bool ignore_invalid_blocks = true; // gen-time blockchain building: don't take into account blocks marked as invalid ("mark_invalid_block")
bool ignore_invalid_txs = true; // gen-time blockchain building: don't take into account txs marked as invalid ("mark_invalid_txs")
};
class test_generator;
class test_chain_unit_base
{
public:
@ -236,6 +240,8 @@ public:
void set_core_proxy(std::shared_ptr<tools::i_core_proxy>) { /* do nothing */ }
uint64_t get_tx_version_from_events(const std::vector<test_event_entry> &events) const;
void on_test_generator_created(test_generator& generator) const; // tests can override this for special initialization
private:
callbacks_map m_callbacks;
@ -313,8 +319,10 @@ public:
bool remove_stuck_txs(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool check_offers_count(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool check_hardfork_active(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool check_hardfork_inactive(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
static bool is_event_mark_invalid_block(const test_event_entry& ev, bool use_global_gentime_settings = true);
static bool is_event_mark_invalid_tx(const test_event_entry& ev, bool use_global_gentime_settings = true);
protected:
struct params_top_block
@ -434,14 +442,13 @@ public:
bool init_test_wallet(const currency::account_base& account, const crypto::hash& genesis_hash, std::shared_ptr<tools::wallet2> &result);
bool refresh_test_wallet(const std::vector<test_event_entry>& events, tools::wallet2* w, const crypto::hash& top_block_hash, size_t expected_blocks_to_be_fetched = std::numeric_limits<size_t>::max());
bool sign_block(currency::block& b,
currency::pos_entry& pe,
tools::wallet2& w,
const tools::wallet2::mining_context& mining_context,
const blockchain_vector& blocks,
const outputs_index& oi);
bool sign_block(const tools::wallet2::mining_context& mining_context,
const currency::pos_entry& pe,
const tools::wallet2& w,
const crypto::scalar_t& blinding_masks_sum,
currency::block& b);
bool get_output_details_by_global_index(const test_generator::blockchain_vector& blck_chain,
/*bool get_output_details_by_global_index(const test_generator::blockchain_vector& blck_chain,
const test_generator::outputs_index& indexes,
uint64_t amount,
uint64_t global_index,
@ -449,7 +456,7 @@ public:
const currency::transaction* tx,
uint64_t& tx_out_index,
crypto::public_key& tx_pub_key,
crypto::public_key& output_key);
crypto::public_key& output_key);*/
@ -538,9 +545,16 @@ private:
std::unordered_map<crypto::hash, block_info> m_blocks_info;
static test_gentime_settings m_test_gentime_settings;
static test_gentime_settings m_test_gentime_settings_default;
static const test_gentime_settings m_test_gentime_settings_default;
}; // class class test_generator
struct test_gentime_settings_restorer
{
test_gentime_settings_restorer() : m_settings(test_generator::get_test_gentime_settings()) {}
~test_gentime_settings_restorer() { test_generator::set_test_gentime_settings(m_settings); }
test_gentime_settings m_settings;
};
extern const crypto::signature invalid_signature; // invalid non-null signature for test purpose
static const std::vector<currency::extra_v> empty_extra;
static const std::vector<currency::attachment_v> empty_attachment;
@ -680,6 +694,9 @@ bool check_ring_signature_at_gen_time(const std::vector<test_event_entry>& event
bool check_mixin_value_for_each_input(size_t mixin, const crypto::hash& tx_id, currency::core& c);
bool shuffle_source_entry(currency::tx_source_entry& se);
bool shuffle_source_entries(std::vector<currency::tx_source_entry>& sources);
//--------------------------------------------------------------------------
template<class t_test_class>
auto do_check_tx_verification_context(const currency::tx_verification_context& tvc, bool tx_added, size_t event_index, const currency::transaction& tx, t_test_class& validator, int)
@ -790,7 +807,7 @@ bool construct_broken_tx(const currency::account_keys& sender_account_keys, cons
BOOST_FOREACH(const currency::tx_source_entry::output_entry& out_entry, src_entr.outputs)
input_to_key.key_offsets.push_back(out_entry.out_reference);
input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets);
input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets); // TODO @#@#
tx.vin.push_back(input_to_key);
}
@ -1006,6 +1023,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
#define MAKE_GENESIS_BLOCK(VEC_EVENTS, BLK_NAME, MINER_ACC, TS) \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_GENESIS_BLOCK(" << #BLK_NAME << ")"); \
test_generator generator; \
this->on_test_generator_created(generator); \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_genesis_block(BLK_NAME, MINER_ACC, TS); \
VEC_EVENTS.push_back(BLK_NAME)
@ -1019,19 +1037,19 @@ void append_vector_by_another_vector(U& dst, const V& src)
#define MAKE_NEXT_BLOCK_TIMESTAMP_ADJUSTMENT(ADJ, VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TIMESTAMP_ADJUSTMENT(" << #BLK_NAME << ")"); \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(ADJ, VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC); \
VEC_EVENTS.push_back(BLK_NAME)
#define MAKE_NEXT_POS_BLOCK(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, MINERS_ACC_LIST) \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_POS_BLOCK(" << #BLK_NAME << ")"); \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, std::list<currency::transaction>(), MINERS_ACC_LIST); \
VEC_EVENTS.push_back(BLK_NAME)
#define MAKE_NEXT_POS_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, MINERS_ACC_LIST, TX_1) \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_POS_BLOCK_TX1(" << #BLK_NAME << ")"); \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
{ \
std::list<currency::transaction>tx_list; \
@ -1041,7 +1059,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
VEC_EVENTS.push_back(BLK_NAME)
#define MAKE_NEXT_BLOCK_NO_ADD(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_NO_ADD(" << #BLK_NAME << ")"); \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC); \
VEC_EVENTS.push_back(event_special_block(BLK_NAME, event_special_block::flag_skip))
@ -1051,7 +1069,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
VEC_EVENTS.push_back(BLK_NAME)
#define MAKE_NEXT_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TX1) \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TX1(" << #BLK_NAME << ")"); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TX1(" << #BLK_NAME << ", " << #TX1 << ")"); \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
{ \
std::list<currency::transaction> tx_list; \
@ -1062,7 +1080,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
#define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST) \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "MAKE_NEXT_BLOCK_TX_LIST(" << #BLK_NAME << ", " << #TXLIST << ")"); \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \
VEC_EVENTS.push_back(BLK_NAME)
@ -1095,7 +1113,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
#define MAKE_TX_MIX_ATTR_EXTRA(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, NMIX, HEAD, MIX_ATTR, EXTRA, CHECK_SPENDS) \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "transaction " << #TX_NAME); \
currency::transaction TX_NAME; \
{ \
bool txr = construct_tx_to_key(generator.get_hardforks(), VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, generator.last_tx_generated_secret_key, MIX_ATTR, EXTRA, std::vector<currency::attachment_v>(), CHECK_SPENDS); \
@ -1104,7 +1122,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
VEC_EVENTS.push_back(TX_NAME)
#define MAKE_TX_FEE_MIX_ATTR_EXTRA(VEC_EVENTS, TX_NAME, FROM, TO, AMOUNT, FEE, NMIX, HEAD, MIX_ATTR, EXTRA, CHECK_SPENDS) \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "transaction " << #TX_NAME); \
currency::transaction TX_NAME; \
{ \
bool txr = construct_tx_to_key(generator.get_hardforks(), VEC_EVENTS, TX_NAME, HEAD, FROM, TO, AMOUNT, FEE, NMIX, generator.last_tx_generated_secret_key, MIX_ATTR, EXTRA, std::vector<currency::attachment_v>(), CHECK_SPENDS); \
@ -1129,7 +1147,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
#define MAKE_TX_MIX_LIST_EXTRA_MIX_ATTR(VEC_EVENTS, SET_NAME, FROM, TO, AMOUNT, NMIX, HEAD, MIX_ATTR, EXTRA, ATTACH) \
{ \
PRINT_EVENT_N(VEC_EVENTS); \
PRINT_EVENT_N_TEXT(VEC_EVENTS, "transaction list " << #SET_NAME); \
currency::transaction t; \
bool r = construct_tx_to_key(generator.get_hardforks(), VEC_EVENTS, t, HEAD, FROM, TO, AMOUNT, TESTS_DEFAULT_FEE, NMIX, generator.last_tx_generated_secret_key, MIX_ATTR, EXTRA, ATTACH); \
if (!r) { LOG_PRINT_YELLOW("ERROR in tx @ EVENT #" << VEC_EVENTS.size(), LOG_LEVEL_0); } \
@ -1185,10 +1203,14 @@ void append_vector_by_another_vector(U& dst, const V& src)
// Adjust gentime and playtime "time" at once
#define ADJUST_TEST_CORE_TIME(desired_time) \
PRINT_EVENT_N(events); \
PRINT_EVENT_N_TEXT(events, "ADJUST_TEST_CORE_TIME(" << desired_time << ")"); \
test_core_time::adjust(desired_time); \
events.push_back(event_core_time(desired_time))
#define ADD_CUSTOM_EVENT_CODE(VEC_EVENTS, CODE) PRINT_EVENT_N_TEXT(VEC_EVENTS, #CODE); CODE
#define ADD_CUSTOM_EVENT(VEC_EVENTS, EVENT_OBJ) PRINT_EVENT_N_TEXT(VEC_EVENTS, #EVENT_OBJ); VEC_EVENTS.push_back(EVENT_OBJ)
// --- gentime wallet helpers -----------------------------------------------------------------------
#define CREATE_TEST_WALLET(WLT_VAR, ACCOUNT, GENESIS_BLOCK) \

View file

@ -87,7 +87,7 @@ inline bool mine_next_pow_block_in_playtime_with_given_txs(const currency::accou
{
CRITICAL_REGION_LOCAL(s_locker);
loc_helper::txs_accessor() = &txs;
r = c.get_blockchain_storage().create_block_template(b, miner_addr, miner_addr, diff, height_from_template, extra, false, pe, loc_helper::fill_block_template_func);
r = c.get_blockchain_storage().create_block_template(miner_addr, miner_addr, extra, false, pe, loc_helper::fill_block_template_func, b, diff, height_from_template);
}
CHECK_AND_ASSERT_MES(r, false, "get_block_template failed");

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2022 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
@ -106,8 +106,9 @@ bool generate_and_play(const char* const genclass_name)
{
std::vector<test_event_entry> events;
bool generated = false;
bool result = true;
std::cout << ENDL << concolor::bright_white << "#TEST# " << genclass_name << concolor::normal << ENDL << ENDL;
bool result = false;
std::cout << ENDL << concolor::bright_white << "#TEST# >>>> " << genclass_name << " <<<<" << ENDL << ENDL;
LOG_PRINT2("get_object_blobsize.log", "#TEST# " << genclass_name, LOG_LEVEL_3);
if (!clean_data_directory())
@ -118,29 +119,36 @@ bool generate_and_play(const char* const genclass_name)
genclass g;
try
{
generated = g.generate(events);;
generated = g.generate(events);
if (generated)
{
std::cout << concolor::bright_white << std::string(100, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< start replaying events" << std::endl <<
std::string(100, '=') << concolor::normal << std::endl;
result = do_replay_events(events, g);
}
}
catch (const std::exception& ex)
{
LOG_ERROR(genclass_name << " generation failed: what=" << ex.what());
LOG_ERROR("got an exception during " << genclass_name << (generated ? " replaying: " : " generation: ") << ex.what());
}
catch (...)
{
LOG_ERROR(genclass_name << " generation failed: generic exception");
LOG_ERROR("got an unknown exception during " << genclass_name << (generated ? " replaying" : " generation"));
}
std::cout << concolor::bright_white << std::string(100, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< start replaying events" << std::endl <<
std::string(100, '=') << concolor::normal << std::endl;
if (generated && do_replay_events(events, g))
if (result)
{
std::cout << concolor::green << "#TEST# Succeeded " << genclass_name << concolor::normal << std::endl;
std::cout << concolor::green << std::string(100, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< Succeeded" << std::endl <<
std::string(100, '=') << concolor::normal << std::endl;
}
else
{
std::cout << concolor::magenta << "#TEST# Failed " << genclass_name << concolor::normal << std::endl;
LOG_PRINT_RED_L0("#TEST# Failed " << genclass_name);
std::cout << concolor::red << std::string(100, '=') << std::endl <<
"#TEST# >>>> " << genclass_name << " <<<< FAILED" << std::endl <<
std::string(100, '=') << concolor::normal << std::endl;
result = false;
}
std::cout << std::endl;
@ -667,6 +675,7 @@ int main(int argc, char* argv[])
po::options_description desc_options("Allowed options");
command_line::add_arg(desc_options, command_line::arg_help);
command_line::add_arg(desc_options, command_line::arg_log_level);
command_line::add_arg(desc_options, arg_test_data_path);
command_line::add_arg(desc_options, arg_generate_test_data);
command_line::add_arg(desc_options, arg_play_test_data);
@ -697,6 +706,16 @@ int main(int argc, char* argv[])
return 0;
}
if (command_line::has_arg(g_vm, command_line::arg_log_level))
{
int new_log_level = command_line::get_arg(g_vm, command_line::arg_log_level);
if (new_log_level >= LOG_LEVEL_MIN && new_log_level <= LOG_LEVEL_MAX && log_space::get_set_log_detalisation_level(false) != new_log_level)
{
log_space::get_set_log_detalisation_level(true, new_log_level);
LOG_PRINT_L0("LOG_LEVEL set to " << new_log_level);
}
}
if (command_line::has_arg(g_vm, arg_stop_on_fail))
{
stop_on_first_fail = command_line::get_arg(g_vm, arg_stop_on_fail);
@ -761,8 +780,6 @@ int main(int argc, char* argv[])
MARK_TEST_AS_POSTPONED(before_hard_fork_1_cumulative_difficulty);
MARK_TEST_AS_POSTPONED(inthe_middle_hard_fork_1_cumulative_difficulty);
MARK_TEST_AS_POSTPONED(zarcanum_basic_test);
#undef MARK_TEST_AS_POSTPONED
@ -1063,6 +1080,8 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(multiassets_basic_test);
GENERATE_AND_PLAY(zarcanum_test_n_inputs_validation);
GENERATE_AND_PLAY(zarcanum_gen_time_balance);
GENERATE_AND_PLAY(zarcanum_txs_with_big_shuffled_decoy_set_shuffled);
// GENERATE_AND_PLAY(gen_block_reward);
// END OF TESTS */

View file

@ -559,7 +559,7 @@ bool gen_checkpoints_pos_validation_on_altchain::generate(std::vector<test_event
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), miner_acc.get_public_address());
@ -588,7 +588,7 @@ bool gen_checkpoints_pos_validation_on_altchain::generate(std::vector<test_event
//crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), miner_acc.get_public_address());
@ -654,7 +654,7 @@ bool gen_no_attchments_in_coinbase::init_config_set_cp(currency::core& c, size_t
crc.pos_minimum_heigh = 1;
c.get_blockchain_storage().set_core_runtime_config(crc);
m_checkpoints.add_checkpoint(12, "8cce274f3ade893fa407a5215b90c595d2ecb036e76ad934b3410da4e8bc7c2c");
m_checkpoints.add_checkpoint(12, "ac57db2582acdd076f92aa8dfcb88d216f60e35b805c16b6256ca26e023bfc3c");
c.set_checkpoints(currency::checkpoints(m_checkpoints));
return true;

View file

@ -92,7 +92,7 @@ bool emission_test::c1(currency::core& c, size_t ev_index, const std::vector<tes
crypto::hash prev_id = get_block_hash(b);
crypto::hash stake_tx_hash = stake_tx_outs.front().first;
size_t stake_output_idx = stake_tx_outs.front().second;
auto tce_ptr = c.get_blockchain_storage().get_tx_chain_entry(stake_tx_hash);
auto tce_ptr = bcs.get_tx_chain_entry(stake_tx_hash);
CHECK_AND_ASSERT_MES(tce_ptr, false, "");
CHECK_AND_ASSERT_MES(stake_output_idx < tce_ptr->m_global_output_indexes.size(), false, "");
@ -105,11 +105,11 @@ bool emission_test::c1(currency::core& c, size_t ev_index, const std::vector<tes
generate_key_image_helper(m_miner_acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
difficulty = c.get_blockchain_storage().get_next_diff_conditional(true);
difficulty = bcs.get_next_diff_conditional(true);
//size_t median_size = 0; // little hack: we're using small blocks (only coinbase tx), so we're in CURRENCY_BLOCK_GRANTED_FULL_REWARD_ZONE - don't need to calc median size
pb.clear();
pb.step1_init_header(get_block_height(b) + 1, prev_id);
pb.step1_init_header(bcs.get_core_runtime_config().hard_forks, get_block_height(b) + 1, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, difficulty, prev_id, null_hash, timestamp);
pb.step4_generate_coinbase_tx(0, already_generated_coins, m_miner_acc.get_public_address());

View file

@ -397,7 +397,7 @@ bool hard_fork_1_checkpoint_basic_test::generate(std::vector<test_event_entry>&
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
@ -573,7 +573,7 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector<test_event_entry>& e
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address());
@ -612,7 +612,7 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector<test_event_entry>& e
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
@ -660,7 +660,7 @@ bool hard_fork_1_pos_and_locked_coins::generate(std::vector<test_event_entry>& e
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
@ -757,7 +757,7 @@ bool hard_fork_1_pos_locked_height_vs_time::generate(std::vector<test_event_entr
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
@ -802,7 +802,7 @@ bool hard_fork_1_pos_locked_height_vs_time::generate(std::vector<test_event_entr
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
@ -846,7 +846,7 @@ bool hard_fork_1_pos_locked_height_vs_time::generate(std::vector<test_event_entr
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.m_block.major_version = HF1_BLOCK_MAJOR_VERSION;
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);

View file

@ -60,7 +60,6 @@ bool hard_fork_2_tx_payer_in_wallet::generate(std::vector<test_event_entry>& eve
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // Bob has auditable address
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
@ -307,7 +306,6 @@ bool hard_fork_2_tx_receiver_in_wallet::generate(std::vector<test_event_entry>&
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // Bob has auditable address
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
@ -448,7 +446,6 @@ bool hard_fork_2_tx_extra_alias_entry_in_wallet::generate(std::vector<test_event
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // auditable address
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
@ -640,7 +637,6 @@ bool hard_fork_2_auditable_addresses_basics::generate(std::vector<test_event_ent
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // Bob has auditable address
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
@ -740,13 +736,16 @@ hard_fork_2_no_new_structures_before_hf::hard_fork_2_no_new_structures_before_hf
bool hard_fork_2_no_new_structures_before_hf::generate(std::vector<test_event_entry>& events) const
{
test_gentime_settings tgs = test_generator::get_test_gentime_settings();
tgs.ignore_invalid_txs = false; // this test pushes originally invalid tx_0, tx_1 and tx_2 which are good after HF2, so we'd like to avoid mess with the sources among txs
test_generator::set_test_gentime_settings(tgs);
bool r = false;
m_accounts.resize(TOTAL_ACCS_COUNT);
account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate();
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate();
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
@ -830,9 +829,9 @@ bool hard_fork_2_no_new_structures_before_hf::generate(std::vector<test_event_en
MAKE_NEXT_BLOCK(events, blk_6, blk_5, miner_acc);
MAKE_NEXT_BLOCK(events, blk_7, blk_6, miner_acc);
events.push_back(tx_0);
events.push_back(tx_1);
events.push_back(tx_2);
ADD_CUSTOM_EVENT(events, tx_0);
ADD_CUSTOM_EVENT(events, tx_1);
ADD_CUSTOM_EVENT(events, tx_2);
// tx_0 with tx_payer should be accepted after HF2
@ -884,7 +883,6 @@ bool hard_fork_2_awo_wallets_basic_test<before_hf_2>::generate(std::vector<test_
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // Bob has auditable address
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
@ -1158,7 +1156,6 @@ bool hard_fork_2_alias_update_using_old_tx<before_hf_2>::generate(std::vector<te
alice_acc.set_createtime(ts);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
events.push_back(event_core_time(ts));
@ -1276,7 +1273,6 @@ bool hard_fork_2_incorrect_alias_update<before_hf_2>::generate(std::vector<test_
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(true); // Bob has auditable address
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
set_hard_fork_heights_to_generator(generator);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);

View file

@ -1194,7 +1194,7 @@ bool multisig_and_unlock_time::generate(std::vector<test_event_entry>& events) c
// noramal input -> multisig output with unlock time
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
r = fill_tx_sources_and_destinations(events, blk_0r, miner_acc.get_keys(), ms_addr_list, amount, TESTS_DEFAULT_FEE, 1, sources, destinations, true, true, 1);
r = fill_tx_sources_and_destinations(events, blk_0r, miner_acc.get_keys(), ms_addr_list, amount, TESTS_DEFAULT_FEE, 1 /*nmix*/, sources, destinations, true, true, 1 /* minimum sigs */);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources_and_destinations failed");
uint64_t unlock_time = blk_0r.timestamp + DIFFICULTY_TOTAL_TARGET * 3 + CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS;
@ -1251,10 +1251,17 @@ bool multisig_and_unlock_time::generate(std::vector<test_event_entry>& events) c
ADJUST_TEST_CORE_TIME(unlock_time_2 - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS - 1);
DO_CALLBACK(events, "mark_invalid_tx");
MAKE_TX(events, tx_3, alice_acc, bob_acc, amount - TESTS_DEFAULT_FEE * 2, blk_2);
// instead of MAKE_TX use manual construction to set check_for_unlocktime = false, old: MAKE_TX(events, tx_3, alice_acc, bob_acc, amount - TESTS_DEFAULT_FEE * 2, blk_2);
r = fill_tx_sources_and_destinations(events, blk_2, alice_acc, bob_acc, amount - TESTS_DEFAULT_FEE * 2, TESTS_DEFAULT_FEE, 0 /*nmix*/, sources, destinations, true, false /* check_for_unlocktime */);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources_and_destinations failed");
transaction tx_3{};
r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_3, get_tx_version_from_events(events), 0);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
ADD_CUSTOM_EVENT(events, tx_3);
ADJUST_TEST_CORE_TIME(unlock_time_2 - CURRENCY_LOCKED_TX_ALLOWED_DELTA_SECONDS + 1);
events.push_back(tx_3);
ADD_CUSTOM_EVENT(events, tx_3);
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_acc, tx_3);
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(BOB_ACC_IDX, amount - TESTS_DEFAULT_FEE * 2));
@ -1362,11 +1369,11 @@ bool multisig_and_coinbase::generate(std::vector<test_event_entry>& events) cons
keypair tx_key = keypair::generate();
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), miner_acc.get_public_address(),
blobdata(), CURRENCY_MINER_TX_MAX_OUTS, extra_alias_entry(), tx_key);
blobdata(), CURRENCY_MINER_TX_MAX_OUTS, &tx_key);
// The builder creates PoS miner tx with normal outputs.
// Replace all miner_tx outputs with one multisig output and re-sign it.

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2022 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -9,23 +9,17 @@
using namespace epee;
using namespace currency;
pos_block_builder::pos_block_builder()
{
clear();
}
void pos_block_builder::clear()
{
m_block = AUTO_VAL_INIT(m_block);
m_stake_kernel = AUTO_VAL_INIT(m_stake_kernel);
m_step = 0;
*this = pos_block_builder{};
}
void pos_block_builder::step1_init_header(size_t block_height, crypto::hash& prev_block_hash)
void pos_block_builder::step1_init_header(const hard_forks_descriptor& hardforks, size_t block_height, crypto::hash& prev_block_hash)
{
CHECK_AND_ASSERT_THROW_MES(m_step == 0, "pos_block_builder: incorrect step sequence");
m_block.minor_version = CURRENT_BLOCK_MINOR_VERSION;
m_block.major_version = BLOCK_MAJOR_VERSION_INITIAL;
m_block.major_version = hardforks.get_block_major_version_by_height(block_height);
m_block.timestamp = 0; // to be set at step 3
m_block.prev_id = prev_block_hash;
m_block.flags = CURRENCY_BLOCK_FLAG_POS_BLOCK;
@ -33,9 +27,12 @@ void pos_block_builder::step1_init_header(size_t block_height, crypto::hash& pre
m_height = block_height;
m_context.zarcanum = hardforks.is_hardfork_active_for_height(ZANO_HARDFORK_04_ZARCANUM, m_height);
m_step = 1;
}
void pos_block_builder::step2_set_txs(const std::vector<currency::transaction>& txs)
{
CHECK_AND_ASSERT_THROW_MES(m_step == 1, "pos_block_builder: incorrect step sequence");
@ -57,6 +54,7 @@ void pos_block_builder::step2_set_txs(const std::vector<currency::transaction>&
m_step = 2;
}
void pos_block_builder::step3_build_stake_kernel(
uint64_t stake_output_amount,
size_t stake_output_gindex,
@ -68,78 +66,119 @@ void pos_block_builder::step3_build_stake_kernel(
uint64_t timestamp_window,
uint64_t timestamp_step)
{
CHECK_AND_ASSERT_THROW_MES(m_step == 2, "pos_block_builder: incorrect step sequence");
m_pos_stake_amount = stake_output_amount;
m_pos_stake_output_gindex = stake_output_gindex;
step3a(difficulty, last_pow_block_hash, last_pos_block_kernel_hash);
m_stake_kernel.kimage = stake_output_key_image;
m_stake_kernel.block_timestamp = m_block.timestamp;
m_stake_kernel.stake_modifier.last_pow_id = last_pow_block_hash;
m_stake_kernel.stake_modifier.last_pos_kernel_id = last_pos_block_kernel_hash;
crypto::public_key stake_source_tx_pub_key {};
uint64_t stake_out_in_tx_index = UINT64_MAX;
crypto::scalar_t stake_out_blinding_mask {};
crypto::secret_key view_secret {};
step3b(stake_output_amount, stake_output_key_image, stake_source_tx_pub_key, stake_out_in_tx_index, stake_out_blinding_mask, view_secret, stake_output_gindex,
timestamp_lower_bound, timestamp_window, timestamp_step);
}
void pos_block_builder::step3a(
currency::wide_difficulty_type difficulty,
const crypto::hash& last_pow_block_hash,
const crypto::hash& last_pos_block_kernel_hash
)
{
CHECK_AND_ASSERT_THROW_MES(m_step == 2, "pos_block_builder: incorrect step sequence");
stake_modifier_type sm{};
sm.last_pow_id = last_pow_block_hash;
sm.last_pos_kernel_id = last_pos_block_kernel_hash;
if (last_pos_block_kernel_hash == null_hash)
{
bool r = string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, m_stake_kernel.stake_modifier.last_pos_kernel_id);
bool r = string_tools::parse_tpod_from_hex_string(POS_STARTER_KERNEL_HASH, sm.last_pos_kernel_id);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to parse POS_STARTER_KERNEL_HASH");
}
wide_difficulty_type stake_difficulty = difficulty / stake_output_amount;
m_context.init(difficulty, sm, m_context.zarcanum);
m_step = 31;
}
void pos_block_builder::step3b(
uint64_t stake_output_amount,
const crypto::key_image& stake_output_key_image,
const crypto::public_key& stake_source_tx_pub_key, // zarcanum only
uint64_t stake_out_in_tx_index, // zarcanum only
const crypto::scalar_t& stake_out_blinding_mask, // zarcanum only
const crypto::secret_key& view_secret, // zarcanum only
size_t stake_output_gindex,
uint64_t timestamp_lower_bound,
uint64_t timestamp_window,
uint64_t timestamp_step)
{
CHECK_AND_ASSERT_THROW_MES(m_step == 31, "pos_block_builder: incorrect step sequence");
m_pos_stake_output_gindex = stake_output_gindex;
m_context.prepare_entry(stake_output_amount, stake_output_key_image, stake_source_tx_pub_key, stake_out_in_tx_index, stake_out_blinding_mask, view_secret);
// align timestamp_lower_bound up to timestamp_step boundary if needed
if (timestamp_lower_bound % timestamp_step != 0)
timestamp_lower_bound = timestamp_lower_bound - (timestamp_lower_bound % timestamp_step) + timestamp_step;
bool sk_found = false;
for (uint64_t ts = timestamp_lower_bound; !sk_found && ts < timestamp_lower_bound + timestamp_window; ts += timestamp_step)
{
m_stake_kernel.block_timestamp = ts;
crypto::hash sk_hash = crypto::cn_fast_hash(&m_stake_kernel, sizeof(m_stake_kernel));
if (check_hash(sk_hash, stake_difficulty))
{
if (m_context.do_iteration(ts))
sk_found = true;
}
}
if (!sk_found)
ASSERT_MES_AND_THROW("Could't build stake kernel");
// update block header with found timestamp
m_block.timestamp = m_stake_kernel.block_timestamp;
m_block.timestamp = m_context.sk.block_timestamp;
m_step = 3;
}
void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
const account_public_address &reward_and_stake_receiver_address,
const blobdata& extra_nonce,
size_t max_outs,
const extra_alias_entry& alias,
keypair tx_one_time_key)
const keypair* tx_one_time_key_to_use)
{
step4_generate_coinbase_tx(median_size, already_generated_coins, reward_and_stake_receiver_address, reward_and_stake_receiver_address, extra_nonce, max_outs, alias, tx_one_time_key);
step4_generate_coinbase_tx(median_size, already_generated_coins, reward_and_stake_receiver_address, reward_and_stake_receiver_address, extra_nonce, max_outs, tx_one_time_key_to_use);
}
void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
const account_public_address &reward_receiver_address,
const account_public_address &stakeholder_address,
const blobdata& extra_nonce,
size_t max_outs,
const extra_alias_entry& alias,
keypair tx_one_time_key)
const keypair* tx_one_time_key_to_use)
{
CHECK_AND_ASSERT_THROW_MES(m_step == 3, "pos_block_builder: incorrect step sequence");
uint64_t tx_version = m_context.zarcanum ? TRANSACTION_VERSION_POST_HF4 : TRANSACTION_VERSION_PRE_HF4;
pos_entry pe{};
pe.stake_unlock_time = 0; // TODO
pe.amount = m_context.stake_amount;
// generate miner tx using incorrect current_block_size only for size estimation
size_t estimated_block_size = m_txs_total_size;
bool r = construct_homemade_pos_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, m_pos_stake_amount, m_stake_kernel.kimage,
m_pos_stake_output_gindex, reward_receiver_address, stakeholder_address, m_block.miner_tx, extra_nonce, max_outs, tx_one_time_key);
CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed");
bool r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee,
reward_receiver_address, stakeholder_address, m_block.miner_tx, tx_version, extra_nonce, max_outs, true, pe, &m_blinding_masks_sum, tx_one_time_key_to_use);
CHECK_AND_ASSERT_THROW_MES(r, "construct_miner_tx failed");
estimated_block_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx);
size_t cumulative_size = 0;
for (size_t try_count = 0; try_count != 10; ++try_count)
{
r = construct_homemade_pos_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee, m_pos_stake_amount, m_stake_kernel.kimage,
m_pos_stake_output_gindex, reward_receiver_address, stakeholder_address, m_block.miner_tx, extra_nonce, max_outs, tx_one_time_key);
r = construct_miner_tx(m_height, median_size, already_generated_coins, estimated_block_size, m_total_fee,
reward_receiver_address, stakeholder_address, m_block.miner_tx, tx_version, extra_nonce, max_outs, true, pe, &m_blinding_masks_sum, tx_one_time_key_to_use);
CHECK_AND_ASSERT_THROW_MES(r, "construct_homemade_pos_miner_tx failed");
cumulative_size = m_txs_total_size + get_object_blobsize(m_block.miner_tx);
@ -159,138 +198,94 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
m_step = 4;
}
void pos_block_builder::step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, const currency::account_base& stakeholder_account)
// supports Zarcanum and mixins
void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const currency::account_keys& stakeholder_keys)
{
bool r = false;
CHECK_AND_ASSERT_THROW_MES(m_step == 4, "pos_block_builder: incorrect step sequence");
crypto::key_derivation pos_coin_derivation = AUTO_VAL_INIT(pos_coin_derivation);
bool r = crypto::generate_key_derivation(stake_tx_pub_key, stakeholder_account.get_keys().view_secret_key, pos_coin_derivation); // derivation(tx_pub; view_sec)
// calculate stake_out_derivation and secret_x (derived ephemeral secret key)
crypto::key_derivation stake_out_derivation = AUTO_VAL_INIT(stake_out_derivation);
r = crypto::generate_key_derivation(se.real_out_tx_key, stakeholder_keys.view_secret_key, stake_out_derivation); // d = 8 * v * R
CHECK_AND_ASSERT_THROW_MES(r, "generate_key_derivation failed");
crypto::secret_key secret_x = AUTO_VAL_INIT(secret_x);
crypto::derive_secret_key(stake_out_derivation, se.real_output_in_tx_index, stakeholder_keys.spend_secret_key, secret_x); // x = Hs(8 * v * R, i) + s
crypto::secret_key derived_secret_ephemeral_key = AUTO_VAL_INIT(derived_secret_ephemeral_key);
crypto::derive_secret_key(pos_coin_derivation, stake_tx_out_index, stakeholder_account.get_keys().spend_secret_key, derived_secret_ephemeral_key); // derivation.derive(spend_sec, out_idx) => input ephemeral secret key
if (m_context.zarcanum)
{
// Zarcanum
zarcanum_sig& sig = boost::get<zarcanum_sig>(m_block.miner_tx.signatures[0]);
txin_zc_input& stake_input = boost::get<txin_zc_input>(m_block.miner_tx.vin[1]);
// sign block actually in coinbase transaction
crypto::hash block_hash = currency::get_block_hash(m_block);
std::vector<const crypto::public_key*> keys_ptrs(1, &stake_tx_out_pub_key);
crypto::generate_ring_signature(block_hash, m_stake_kernel.kimage, keys_ptrs, derived_secret_ephemeral_key, 0, &boost::get<currency::NLSAG_sig>(m_block.miner_tx.signatures[0]).s[0]);
stake_input.k_image = m_context.sk.kimage;
std::vector<crypto::CLSAG_GGXG_input_ref_t> ring;
for(const auto& el : se.outputs)
{
stake_input.key_offsets.push_back(el.out_reference);
ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point);
}
r = absolute_sorted_output_offsets_to_relative_in_place(stake_input.key_offsets);
CHECK_AND_ASSERT_THROW_MES(r, "absolute_sorted_output_offsets_to_relative_in_place failed");
crypto::hash tx_hash_for_sig = get_transaction_hash(m_block.miner_tx); // TODO @#@# change to block hash after the corresponding test is made
uint8_t err = 0;
r = crypto::zarcanum_generate_proof(tx_hash_for_sig, m_context.kernel_hash, ring, m_context.last_pow_block_id_hashed, m_context.sk.kimage,
secret_x, m_context.secret_q, se.real_output, m_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_blinding_mask,
static_cast<crypto::zarcanum_proof&>(sig), &err);
CHECK_AND_ASSERT_THROW_MES(r, "zarcanum_generate_proof failed, err: " << (int)err);
}
else
{
// old PoS with non-hidden amounts
NLSAG_sig& sig = boost::get<NLSAG_sig>(m_block.miner_tx.signatures[0]);
txin_to_key& stake_input = boost::get<txin_to_key>(m_block.miner_tx.vin[1]);
stake_input.k_image = m_context.sk.kimage;
stake_input.amount = m_context.stake_amount;
stake_input.key_offsets.push_back(m_pos_stake_output_gindex);
crypto::hash block_hash = currency::get_block_hash(m_block);
std::vector<const crypto::public_key*> keys_ptrs(1, &se.outputs.front().stealth_address);
sig.s.resize(1);
crypto::generate_ring_signature(block_hash, m_context.sk.kimage, keys_ptrs, secret_x, 0, sig.s.data());
}
m_step = 5;
}
bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
size_t current_block_size,
uint64_t fee,
uint64_t pos_stake_amount,
crypto::key_image pos_stake_keyimage,
size_t pos_stake_gindex,
const account_public_address &reward_receiving_address,
const account_public_address &stakeholder_address,
transaction& tx,
const blobdata& extra_nonce /*= blobdata()*/,
size_t max_outs /*= CURRENCY_MINER_TX_MAX_OUTS*/,
keypair tx_one_time_key /*= keypair::generate()*/)
void pos_block_builder::step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key,
const currency::account_base& stakeholder_account)
{
boost::value_initialized<transaction> new_tx;
tx = new_tx;
CHECK_AND_ASSERT_THROW_MES(!m_context.zarcanum, "for zarcanum use another overloading");
tx.version = TRANSACTION_VERSION_PRE_HF4;
set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
tx_source_entry se{};
// calculate block reward
uint64_t block_reward;
bool r = get_block_reward(true, median_size, current_block_size, already_generated_coins, block_reward, height);
CHECK_AND_ASSERT_MES(r, false, "Block is too big");
block_reward += fee;
se.real_out_tx_key = stake_tx_pub_key;
se.real_output_in_tx_index = stake_tx_out_index;
se.outputs.emplace_back(m_pos_stake_output_gindex, stake_tx_out_pub_key);
// decompose reward into outputs and populate tx.vout
std::vector<size_t> out_amounts;
decompose_amount_into_digits(block_reward, DEFAULT_DUST_THRESHOLD,
[&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); },
[&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); });
CHECK_AND_ASSERT_MES(2 <= max_outs, false, "max_out must be greather than 1");
while (out_amounts.size() + 1 > max_outs)
{
out_amounts[out_amounts.size() - 2] += out_amounts.back();
out_amounts.resize(out_amounts.size() - 1);
}
// reward
bool burn_money = reward_receiving_address.spend_public_key == null_pkey && reward_receiving_address.view_public_key == null_pkey; // if true, burn reward, so no one on Earth can spend them
for (size_t output_index = 0; output_index < out_amounts.size(); ++output_index)
{
txout_to_key tk;
tk.key = null_pkey; // null means burn money
tk.mix_attr = 0;
if (!burn_money)
{
r = currency::derive_public_key_from_target_address(reward_receiving_address, tx_one_time_key.sec, output_index, tk.key); // derivation(view_pub; tx_sec).derive(output_index, spend_pub) => output pub key
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
}
tx_out_bare out;
out.amount = out_amounts[output_index];
out.target = tk;
tx.vout.push_back(out);
}
// stake
burn_money = stakeholder_address.spend_public_key == null_pkey && stakeholder_address.view_public_key == null_pkey; // if true, burn stake
{
txout_to_key tk;
tk.key = null_pkey; // null means burn money
tk.mix_attr = 0;
if (!burn_money)
{
r = currency::derive_public_key_from_target_address(stakeholder_address, tx_one_time_key.sec, tx.vout.size(), tk.key);
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
}
tx_out_bare out;
out.amount = pos_stake_amount;
out.target = tk;
tx.vout.push_back(out);
}
// take care about extra
add_tx_pub_key_to_extra(tx, tx_one_time_key.pub);
if (extra_nonce.size())
if (!add_tx_extra_userdata(tx, extra_nonce))
return false;
// populate ins with 1) money-generating and 2) PoS
txin_gen in;
in.height = height;
tx.vin.push_back(in);
txin_to_key posin;
posin.amount = pos_stake_amount;
posin.key_offsets.push_back(pos_stake_gindex);
posin.k_image = pos_stake_keyimage;
tx.vin.push_back(posin);
//reserve place for ring signature
tx.signatures.resize(1);
boost::get<currency::NLSAG_sig>(tx.signatures[0]).s.resize(posin.key_offsets.size());
return true;
step5_sign(se, stakeholder_account.get_keys());
}
bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc,
std::function<bool(currency::block&)> before_sign_cb, currency::block& output)
{
blockchain_storage& bcs = c.get_blockchain_storage();
// these values (median and diff) are correct only for the next main chain block, it's incorrect for altblocks, especially for old altblocks
// but for now we assume they will work fine
uint64_t block_size_median = c.get_blockchain_storage().get_current_comulative_blocksize_limit() / 2;
currency::wide_difficulty_type difficulty = c.get_blockchain_storage().get_next_diff_conditional(true);
uint64_t block_size_median = bcs.get_current_comulative_blocksize_limit() / 2;
currency::wide_difficulty_type difficulty = bcs.get_next_diff_conditional(true);
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
block_extended_info bei = AUTO_VAL_INIT(bei);
bool r = c.get_blockchain_storage().get_block_extended_info_by_hash(prev_id, bei);
bool r = bcs.get_block_extended_info_by_hash(prev_id, bei);
CHECK_AND_ASSERT_MES(r, false, "get_block_extended_info_by_hash failed for hash = " << prev_id);
@ -305,7 +300,7 @@ bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(bcs.get_core_runtime_config().hard_forks, height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, difficulty, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(block_size_median, bei.already_generated_coins, acc.get_public_address());

View file

@ -1,16 +1,20 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2022 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#pragma once
namespace currency
{
struct hard_forks_descriptor;
}
struct pos_block_builder
{
pos_block_builder();
pos_block_builder() = default;
void clear();
void step1_init_header(size_t block_height, crypto::hash& prev_block_hash);
void step1_init_header(const currency::hard_forks_descriptor& hardforks, size_t block_height, crypto::hash& prev_block_hash);
void step2_set_txs(const std::vector<currency::transaction>& txs);
@ -23,6 +27,26 @@ struct pos_block_builder
uint64_t timestamp_lower_bound,
uint64_t timestamp_window = POS_SCAN_WINDOW,
uint64_t timestamp_step = POS_SCAN_STEP);
void step3a(
currency::wide_difficulty_type difficulty,
const crypto::hash& last_pow_block_hash,
const crypto::hash& last_pos_block_kernel_hash
);
void step3b(
uint64_t stake_output_amount,
const crypto::key_image& stake_output_key_image,
const crypto::public_key& stake_source_tx_pub_key,
uint64_t stake_out_in_tx_index,
const crypto::scalar_t& stake_out_blinding_mask,
const crypto::secret_key& view_secret,
size_t stake_output_gindex,
uint64_t timestamp_lower_bound,
uint64_t timestamp_window,
uint64_t timestamp_step);
void step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
@ -30,30 +54,38 @@ struct pos_block_builder
const currency::account_public_address &stakeholder_address,
const currency::blobdata& extra_nonce = currency::blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
const currency::extra_alias_entry& alias = currency::extra_alias_entry(),
currency::keypair tx_one_time_key = currency::keypair::generate());
const currency::keypair* tx_one_time_key_to_use = nullptr
);
void step4_generate_coinbase_tx(size_t median_size,
const boost::multiprecision::uint128_t& already_generated_coins,
const currency::account_public_address &reward_and_stake_receiver_address,
const currency::blobdata& extra_nonce = currency::blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
const currency::extra_alias_entry& alias = currency::extra_alias_entry(),
currency::keypair tx_one_time_key = currency::keypair::generate());
const currency::keypair* tx_one_time_key_to_use = nullptr
);
void step5_sign(const currency::tx_source_entry& se, const currency::account_keys& stakeholder_keys);
void step5_sign(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const crypto::public_key& stake_tx_out_pub_key, const currency::account_base& stakeholder_account);
currency::block m_block;
size_t m_step;
size_t m_txs_total_size;
uint64_t m_total_fee;
currency::stake_kernel m_stake_kernel;
size_t m_height;
size_t m_pos_stake_output_gindex;
uint64_t m_pos_stake_amount;
//void step5_sign_zarcanum(const crypto::public_key& stake_tx_pub_key, size_t stake_tx_out_index, const currency::account_base& stakeholder_account);
currency::block m_block {};
size_t m_step = 0;
size_t m_txs_total_size = 0;
uint64_t m_total_fee = 0;
//currency::stake_kernel m_stake_kernel {};
size_t m_height = 0;
size_t m_pos_stake_output_gindex = 0;
//uint64_t m_pos_stake_amount = 0;
crypto::scalar_t m_blinding_masks_sum {}; // bliding masks of zc outputs of miner tx
currency::pos_mining_context m_context {};
};
bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
/* bool construct_homemade_pos_miner_tx(bool zarcanum, size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
size_t current_block_size,
uint64_t fee,
uint64_t pos_stake_amount,
@ -64,7 +96,7 @@ bool construct_homemade_pos_miner_tx(size_t height, size_t median_size, const bo
currency::transaction& tx,
const currency::blobdata& extra_nonce = currency::blobdata(),
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
currency::keypair tx_one_time_key = currency::keypair::generate());
currency::keypair tx_one_time_key = currency::keypair::generate()); */
bool mine_next_pos_block_in_playtime_sign_cb(currency::core& c, const currency::block& prev_block, const currency::block& coinstake_scr_block, const currency::account_base& acc,
std::function<bool(currency::block&)> before_sign_cb, currency::block& output);

View file

@ -79,7 +79,7 @@ bool gen_pos_coinstake_already_spent::generate(std::vector<test_event_entry>& ev
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), alice.get_public_address());
@ -130,7 +130,7 @@ bool gen_pos_incorrect_timestamp::generate(std::vector<test_event_entry>& events
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
// use incorrect timestamp_step
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp - 1, POS_SCAN_WINDOW, POS_SCAN_STEP - 1);
@ -148,7 +148,7 @@ bool gen_pos_incorrect_timestamp::generate(std::vector<test_event_entry>& events
// Now try PoS timestamp window boundaries.
pb.clear();
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
// move timestamp to the future
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, ts + CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT + 1, POS_SCAN_WINDOW, POS_SCAN_STEP);
@ -162,7 +162,7 @@ bool gen_pos_incorrect_timestamp::generate(std::vector<test_event_entry>& events
// lower limit
pb.clear();
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
// move timestamp to the future
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, genesis_ts - POS_SCAN_WINDOW, POS_SCAN_WINDOW, POS_SCAN_STEP);
@ -247,17 +247,17 @@ bool gen_pos_extra_nonce::generate(std::vector<test_event_entry>& events) const
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp);
// use biggest possible extra nonce (255 bytes) + largest alias
currency::blobdata extra_none(255, 'x');
currency::extra_alias_entry alias = AUTO_VAL_INIT(alias);
alias.m_alias = std::string(255, 'a');
alias.m_address = miner.get_keys().account_address;
alias.m_text_comment = std::string(255, 'y');
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), alice.get_public_address(), extra_none, CURRENCY_MINER_TX_MAX_OUTS, alias);
currency::blobdata extra_nonce(255, 'x');
//currency::extra_alias_entry alias = AUTO_VAL_INIT(alias); // TODO: this alias entry was ignored for a long time, now I commented it out, make sure it's okay -- sowle
//alias.m_alias = std::string(255, 'a');
//alias.m_address = miner.get_keys().account_address;
//alias.m_text_comment = std::string(255, 'y');
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), alice.get_public_address(), extra_nonce, CURRENCY_MINER_TX_MAX_OUTS);
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, miner);
block blk_1 = pb.m_block;
@ -301,7 +301,7 @@ bool gen_pos_min_allowed_height::generate(std::vector<test_event_entry>& events)
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>(1, tx_1));
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), alice.get_public_address());
@ -353,7 +353,7 @@ bool gen_pos_invalid_coinbase::generate(std::vector<test_event_entry>& events) c
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(boost::get<currency::tx_out_bare>(stake.vout[stake_output_idx]).target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, prev_block.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), alice_acc.get_public_address());

View file

@ -37,7 +37,7 @@ struct tx_builder
for(const currency::tx_source_entry::output_entry& out_entry : src_entr.outputs)
input_to_key.key_offsets.push_back(out_entry.out_reference);
input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets);
input_to_key.key_offsets = currency::absolute_output_offsets_to_relative(input_to_key.key_offsets); // TODO @#@#
m_tx.vin.push_back(input_to_key);
}
}

View file

@ -32,9 +32,16 @@ bool wallet_test_core_proxy::update_blockchain(const std::vector<test_event_entr
for (auto b : m_blocks)
std::for_each(b->b.tx_hashes.begin(), b->b.tx_hashes.end(), [&confirmed_txs](const crypto::hash& h) { confirmed_txs.insert(h); });
for (auto e : events)
size_t invalid_tx_index = UINT64_MAX;
for (size_t i = 0; i < events.size(); ++i)
{
if (e.type() != typeid(currency::transaction))
const test_event_entry& e = events[i];
if (test_chain_unit_enchanced::is_event_mark_invalid_tx(e))
{
invalid_tx_index = i + 1;
continue;
}
if (e.type() != typeid(currency::transaction) || i == invalid_tx_index)
continue;
const currency::transaction& tx = boost::get<currency::transaction>(e);

View file

@ -49,7 +49,7 @@ bool wallet_test::check_balance_via_build_wallets(currency::core& c, size_t ev_i
r = generator.build_wallets(get_block_hash(*top_block), accounts, w, c.get_blockchain_storage().get_core_runtime_config());
CHECK_AND_ASSERT_MES(r && w.size() == 1 && w[0].wallet != 0, false, "check_balance: failed to build wallets");
if (!check_balance_via_wallet(*w[0].wallet, epee::string_tools::num_to_string_fast(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[0].wallet, 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))
return false;
return true;
@ -67,12 +67,25 @@ 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(), epee::string_tools::num_to_string_fast(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, pcb.mined_balance, pcb.unlocked_balance, pcb.awaiting_in, pcb.awaiting_out))
return false;
return true;
}
std::string wallet_test::get_test_account_name_by_id(size_t acc_id)
{
switch(acc_id)
{
case MINER_ACC_IDX: return "miner";
case ALICE_ACC_IDX: return "Alice";
case BOB_ACC_IDX: return "Bob";
case CAROL_ACC_IDX: return "Carol";
case DAN_ACC_IDX: return "Dan";
default: return "unknown";
}
}
std::shared_ptr<tools::wallet2> wallet_test::init_playtime_test_wallet(const std::vector<test_event_entry>& events, currency::core& c, const account_base& acc) const
{
CHECK_AND_ASSERT_THROW_MES(events.size() > 0 && events[0].type() == typeid(currency::block), "Invalid events queue, can't find genesis block at the beginning");

View file

@ -17,6 +17,8 @@ struct wallet_test : virtual public test_chain_unit_enchanced
bool check_balance_via_build_wallets(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
bool check_balance(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
static std::string get_test_account_name_by_id(size_t acc_id);
protected:
struct params_check_balance

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2022 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -9,6 +9,7 @@
#include "random_helper.h"
#include "tx_builder.h"
#include "pos_block_builder.h"
#define AMOUNT_TO_TRANSFER_ZARCANUM_BASIC (TESTS_DEFAULT_FEE*10)
@ -17,6 +18,7 @@
using namespace currency;
//------------------------------------------------------------------------------
zarcanum_basic_test::zarcanum_basic_test()
{
REGISTER_CALLBACK_METHOD(zarcanum_basic_test, configure_core);
@ -30,13 +32,15 @@ zarcanum_basic_test::zarcanum_basic_test()
bool zarcanum_basic_test::generate(std::vector<test_event_entry>& events) const
{
m_accounts.resize(MINER_ACC_IDX+1);
account_base& miner_acc = m_accounts[MINER_ACC_IDX];
miner_acc.generate();
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);
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts);
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts);
//account_base& carol_acc = m_accounts[CAROL_ACC_IDX]; carol_acc.generate(); carol_acc.set_createtime(ts);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks
set_hard_fork_heights_to_generator(generator);
//TODO: Need to make sure REWIND_BLOCKS_N and other coretests codebase are capable of following hardfork4 rules
//in this test hardfork4 moment moved to runtime section
REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
@ -50,58 +54,46 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect
{
bool r = false;
std::shared_ptr<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, ALICE_ACC_IDX);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, BOB_ACC_IDX);
account_base alice_acc;
alice_acc.generate();
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, alice_acc);
//pass over hardfork
// check passing over the hardfork
CHECK_AND_ASSERT_MES(!c.get_blockchain_storage().is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM), false, "ZANO_HARDFORK_04_ZARCANUM is active");
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 2);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
CHECK_AND_ASSERT_MES(c.get_blockchain_storage().is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM), false, "ZANO_HARDFORK_04_ZARCANUM is not active");
miner_wlt->refresh();
alice_wlt->refresh();
CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool");
//create transfer from pre-zarcanum inputs to post-zarcanum inputs
uint64_t transfer_amount = AMOUNT_TO_TRANSFER_ZARCANUM_BASIC + TESTS_DEFAULT_FEE;
miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address());
LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0);
miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address());
LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0);
miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address());
LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0);
miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address());
LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << transfer_amount, LOG_LEVEL_0);
const size_t batches_to_Alice_count = 4;
for(size_t i = 0; i < batches_to_Alice_count; ++i)
{
miner_wlt->transfer(transfer_amount, alice_wlt->get_account().get_public_address());
LOG_PRINT_MAGENTA("Legacy-2-zarcanum transaction sent to Alice: " << print_money_brief(transfer_amount), LOG_LEVEL_0);
}
CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 4, false, "Incorrect txs count in the pool");
CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == batches_to_Alice_count, false, "Incorrect txs count in the pool");
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
//miner_wlt->refresh();
CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 0, false, "Incorrect txs count in the pool");
alice_wlt->refresh();
//uint64_t unlocked = 0;
//uint64_t balance = alice_wlt->balance(unlocked);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", transfer_amount * 4, UINT64_MAX, transfer_amount * 4), false, "");
account_base bob_acc;
bob_acc.generate();
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, bob_acc);
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*alice_wlt, "Alice", transfer_amount * batches_to_Alice_count, UINT64_MAX, transfer_amount * batches_to_Alice_count), false, "");
//create transfer from post-zarcanum inputs to post-zarcanum inputs
uint64_t transfer_amount2 = AMOUNT_TO_TRANSFER_ZARCANUM_BASIC;
alice_wlt->transfer(transfer_amount2, bob_acc.get_public_address());
LOG_PRINT_MAGENTA("Zarcanum-2-zarcanum transaction sent from Alice to Bob " << transfer_amount2, LOG_LEVEL_0);
alice_wlt->transfer(transfer_amount2, m_accounts[BOB_ACC_IDX].get_public_address());
LOG_PRINT_MAGENTA("Zarcanum-2-zarcanum transaction sent from Alice to Bob " << print_money_brief(transfer_amount2), LOG_LEVEL_0);
CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 1, false, "Incorrect txs count in the pool");
@ -110,8 +102,6 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
bob_wlt->refresh();
//balance = bob_wlt->balance(unlocked);
//CHECK_AND_ASSERT_MES(unlocked == transfer_amount2, false, "wrong amount");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", transfer_amount2, UINT64_MAX, transfer_amount2), false, "");
account_base staker_benefeciary_acc;
@ -122,27 +112,36 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect
miner_benefeciary_acc.generate();
std::shared_ptr<tools::wallet2> miner_benefeciary_acc_wlt = init_playtime_test_wallet(events, c, miner_benefeciary_acc);
alice_wlt->refresh();
size_t pos_entries_count = 0;
//do staking
for(size_t i = 0; i != CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4; i++)
for(size_t i = 0; i < batches_to_Alice_count - 1; i++)
{
alice_wlt->refresh();
r = mine_next_pos_block_in_playtime_with_wallet(*alice_wlt.get(), staker_benefeciary_acc_wlt->get_account().get_public_address(), pos_entries_count);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pos_block_in_playtime_with_wallet failed");
CHECK_AND_ASSERT_MES(r, false, "mine_next_pos_block_in_playtime_with_wallet failed, pos_entries_count = " << pos_entries_count);
r = mine_next_pow_block_in_playtime(miner_benefeciary_acc.get_public_address(), c);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_block_in_playtime failed");
}
// make sure all mined coins in staker_benefeciary_acc_wlt and miner_benefeciary_acc_wlt are now spendable
r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
CHECK_AND_ASSERT_MES(r, false, "mine_next_pow_blocks_in_playtime failed");
//attempt to spend staked and mined coinbase outs
staker_benefeciary_acc_wlt->refresh();
miner_benefeciary_acc_wlt->refresh();
staker_benefeciary_acc_wlt->transfer(transfer_amount2, bob_acc.get_public_address());
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, "");
staker_benefeciary_acc_wlt->transfer(transfer_amount2, bob_wlt->get_account().get_public_address());
LOG_PRINT_MAGENTA("Zarcanum(pos-coinbase)-2-zarcanum transaction sent from Staker to Bob " << print_money_brief(transfer_amount2), LOG_LEVEL_0);
miner_benefeciary_acc_wlt->transfer(transfer_amount2, bob_acc.get_public_address());
miner_benefeciary_acc_wlt->transfer(transfer_amount2, bob_wlt->get_account().get_public_address());
LOG_PRINT_MAGENTA("Zarcanum(pow-coinbase)-2-zarcanum transaction sent from Staker to Bob " << print_money_brief(transfer_amount2), LOG_LEVEL_0);
CHECK_AND_FORCE_ASSERT_MES(c.get_pool_transactions_count() == 2, false, "Incorrect txs count in the pool");
@ -154,13 +153,14 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect
bob_wlt->refresh();
//balance = bob_wlt->balance(unlocked);
//CHECK_AND_ASSERT_MES(unlocked == transfer_amount2*3, false, "wrong amount");
CHECK_AND_ASSERT_MES(check_balance_via_wallet(*bob_wlt, "Bob", transfer_amount2*3, UINT64_MAX, transfer_amount2*3), false, "");
//try to make pre-zarcanum block after hardfork 4
currency::core_runtime_config rc = alice_wlt->get_core_runtime_config();
rc.hard_forks.set_hardfork_height(4, ZANO_HARDFORK_04_AFTER_HEIGHT);
rc.hard_forks.set_hardfork_height(4, ZANO_HARDFORK_04_AFTER_HEIGHT); // <-- TODO: this won't help to build pre-hardfork block,
// because blocktemplate is created by the core.
// (wallet2::build_minted_block will fail instead)
// We need to use pos block builder or smth -- sowle
alice_wlt->set_core_runtime_config(rc);
r = mine_next_pos_block_in_playtime_with_wallet(*alice_wlt.get(), staker_benefeciary_acc_wlt->get_account().get_public_address(), pos_entries_count);
CHECK_AND_ASSERT_MES(!r, false, "Pre-zarcanum block accepted in post-zarcanum era");
@ -169,7 +169,7 @@ bool zarcanum_basic_test::c1(currency::core& c, size_t ev_index, const std::vect
return true;
}
//------------------------------------------------------------------------------
zarcanum_test_n_inputs_validation::zarcanum_test_n_inputs_validation()
{
@ -226,4 +226,305 @@ bool zarcanum_test_n_inputs_validation::generate(std::vector<test_event_entry>&
REWIND_BLOCKS_N(events, blk_16, blk_14, miner_account, 2);
return true;
}
}
//------------------------------------------------------------------------------
zarcanum_gen_time_balance::zarcanum_gen_time_balance()
{
m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 1);
}
bool zarcanum_gen_time_balance::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure post HF4 transactions with ZC inputs and outputs are handled properly by chaingen gen-time routines
// (including balance check)
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);
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts);
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
//
// tx_0: alice_amount from miner to Alice
uint64_t alice_amount = MK_TEST_COINS(99);
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_0r, miner_acc, alice_acc, alice_amount, TESTS_DEFAULT_FEE, 0, sources, destinations), false, "");
std::vector<extra_v> extra;
transaction tx_0 = AUTO_VAL_INIT(tx_0);
crypto::secret_key tx_sec_key;
r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_0, get_tx_version_from_events(events), tx_sec_key, 0);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
ADD_CUSTOM_EVENT(events, tx_0);
// add tx_0 to the block
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
// rewind and check unlocked balance
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, alice_amount, alice_amount, 0, 0, 0));
// do a gen-time balance check
CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_1r, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, alice_amount);
//
// tx_1: bob_amount, Alice -> Bob
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);
// 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));
// ... 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);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(bob_wlt, bob_amount);
// try to construct tx with only one output (that is wrong for HF4)
test_gentime_settings_restorer tgsr;
test_gentime_settings tgs = test_generator::get_test_gentime_settings();
tgs.split_strategy = tests_void_split_strategy; // amount won't be splitted by test_genertor::construct_tx()
test_generator::set_test_gentime_settings(tgs);
DO_CALLBACK(events, "mark_invalid_tx");
// transfer all Bob's coins -- they should be put in one out
MAKE_TX(events, tx_2_bad, bob_acc, alice_acc, bob_amount - TESTS_DEFAULT_FEE, blk_2);
CHECK_AND_ASSERT_MES(tx_2_bad.vout.size() == 1, false, "tx_2_bad.vout.size() = " << tx_2_bad.vout.size());
DO_CALLBACK(events, "mark_invalid_block");
MAKE_NEXT_BLOCK_TX1(events, blk_3_bad, blk_2, miner_acc, tx_2_bad);
// now change split strategy and make a tx with more outputs
tgs.split_strategy = tests_random_split_strategy;
test_generator::set_test_gentime_settings(tgs);
size_t nmix = 7;
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);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_3, 2);
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);
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(BOB_ACC_IDX, 0, 0, 0, 0, 0));
return true;
}
//------------------------------------------------------------------------------
zarcanum_pos_block_math::zarcanum_pos_block_math()
{
m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 0);
}
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
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc);
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
//generator.get_tx_out_gindex
// try to make a PoS block with locked stake after the hardfork
block blk_1_pos;
{
const block& prev_block = blk_1r;
const transaction& stake_tx = blk_1.miner_tx;
const account_base& stake_acc = miner_acc;
size_t stake_out_in_tx_index = 0;
size_t stake_output_gindex = 0;
crypto::hash prev_id = get_block_hash(prev_block);
size_t height = get_block_height(prev_block) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake_tx);
pos_block_builder pb;
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3a(diff, prev_id, null_hash);
crypto::key_derivation derivation {};
crypto::scalar_t stake_out_blinding_mask {};
uint64_t stake_output_amount = 0;
crypto::public_key stealth_address {};
crypto::secret_key secret_x {};
crypto::key_image stake_output_key_image {};
r = generate_key_derivation(stake_tx_pub_key, stake_acc.get_keys().view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed");
r = is_out_to_acc(stake_acc.get_public_address(), boost::get<currency::tx_out_zarcanum>(stake_tx.vout[stake_out_in_tx_index]), derivation, stake_out_in_tx_index, stake_output_amount, stake_out_blinding_mask);
CHECK_AND_ASSERT_MES(r, false, "is_out_to_acc failed");
r = crypto::derive_public_key(derivation, stake_out_in_tx_index, stake_acc.get_public_address().spend_public_key, stealth_address);
CHECK_AND_ASSERT_MES(r, false, "derive_public_key failed");
crypto::derive_secret_key(derivation, stake_out_in_tx_index, stake_acc.get_keys().spend_secret_key, secret_x);
crypto::generate_key_image(stealth_address, secret_x, stake_output_key_image);
pb.step3b(stake_output_amount, stake_output_key_image, stake_tx_pub_key, stake_out_in_tx_index, stake_out_blinding_mask, stake_acc.get_keys().view_secret_key,
stake_output_gindex, prev_block.timestamp, POS_SCAN_WINDOW, POS_SCAN_STEP);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(prev_block), stake_acc.get_public_address());
pb.step5_sign(stake_tx_pub_key, stake_out_in_tx_index, stake_tx_pub_key, stake_acc);
blk_1_pos = pb.m_block;
}
ADD_CUSTOM_EVENT(events, blk_1_pos);
return true;
}
//------------------------------------------------------------------------------
zarcanum_txs_with_big_shuffled_decoy_set_shuffled::zarcanum_txs_with_big_shuffled_decoy_set_shuffled()
{
m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 24);
}
bool zarcanum_txs_with_big_shuffled_decoy_set_shuffled::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make few txs with a big decoy set, each time shuffling the sources (decoy set) before constructing a tx => to make sure real_output can be any.
// Then do the same after HF4.
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);
account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts);
account_base& bob_acc = m_accounts[BOB_ACC_IDX]; bob_acc.generate(); bob_acc.set_createtime(ts);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts);
DO_CALLBACK(events, "configure_core"); // necessary to set m_hardforks
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
uint64_t alice_amount = CURRENCY_BLOCK_REWARD;
MAKE_TX(events, tx_0_a, miner_acc, alice_acc, alice_amount, blk_0r);
MAKE_TX(events, tx_0_b, miner_acc, alice_acc, alice_amount, blk_0r);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_1, blk_0r, miner_acc, std::list<transaction>({ tx_0_a, tx_0_b }));
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// do a gen-time balance check
CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0);
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_1r, 2 * CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 1);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, 2 * alice_amount);
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 2 * alice_amount, 2 * alice_amount, 0, 0, 0));
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
// tx_1: Alice -> miner with big decoy set
size_t nmix = 10;
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_1r, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, "");
// randomly source entries (real_output will be changed accordingly)
CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed");
transaction tx_1_a{};
r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_1_a, get_tx_version_from_events(events), 0 /* unlock time */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
ADD_CUSTOM_EVENT(events, tx_1_a);
MAKE_NEXT_BLOCK_TX1(events, blk_2, blk_1r, miner_acc, tx_1_a);
// tx_2 (the same as tx_1): Alice -> miner, big decoy set, all coins are back now
sources.clear();
destinations.clear();
nmix = 10;
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_2, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, "");
CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed");
transaction tx_1_b{};
r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_1_b, get_tx_version_from_events(events), 0 /* unlock time */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
ADD_CUSTOM_EVENT(events, tx_1_b);
MAKE_NEXT_BLOCK_TX1(events, blk_3, blk_2, miner_acc, tx_1_b);
// make sure Alice has no coins left
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 0, 0, 0, 0, 0));
//
// now do the same after HF4
//
// make sure the hardfork goes well
DO_CALLBACK_PARAMS(events, "check_hardfork_inactive", static_cast<size_t>(ZANO_HARDFORK_04_ZARCANUM));
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_acc);
DO_CALLBACK_PARAMS(events, "check_hardfork_active", static_cast<size_t>(ZANO_HARDFORK_04_ZARCANUM));
// tx_3_a, tx_3_b: miner -> Alice, alice_amount x 2
MAKE_TX(events, tx_2_a, miner_acc, alice_acc, alice_amount, blk_4);
MAKE_TX(events, tx_2_b, miner_acc, alice_acc, alice_amount, blk_4);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_5, blk_4, miner_acc, std::list<transaction>({ tx_2_a, tx_2_b }));
REWIND_BLOCKS_N_WITH_TIME(events, blk_5r, blk_5, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
// do a gen-time balance check
REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_5r, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 4);
CHECK_TEST_WALLET_BALANCE_AT_GEN_TIME(alice_wlt, 2 * alice_amount);
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 2 * alice_amount, 2 * alice_amount, 0, 0, 0));
// tx_3_a: Alice -> miner, alice_amount - TESTS_DEFAULT_FEE, big decoy set
sources.clear();
destinations.clear();
nmix = 10;
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_5r, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, "");
CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed");
transaction tx_3_a{};
r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_3_a, get_tx_version_from_events(events), 0 /* unlock time */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
ADD_CUSTOM_EVENT(events, tx_3_a);
MAKE_NEXT_BLOCK_TX1(events, blk_6, blk_5r, miner_acc, tx_3_a);
// tx_3_b: Alice -> miner, alice_amount - TESTS_DEFAULT_FEE, big decoy set
sources.clear();
destinations.clear();
nmix = 10;
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_6, alice_acc, miner_acc, alice_amount - TESTS_DEFAULT_FEE, TESTS_DEFAULT_FEE, nmix, sources, destinations), false, "");
CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "shuffle_source_entries failed");
transaction tx_3_b{};
r = construct_tx(alice_acc.get_keys(), sources, destinations, empty_attachment, tx_3_b, get_tx_version_from_events(events), 0 /* unlock time */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
ADD_CUSTOM_EVENT(events, tx_3_b);
MAKE_NEXT_BLOCK_TX1(events, blk_7, blk_6, miner_acc, tx_3_b);
// make sure Alice has no coins left
DO_CALLBACK_PARAMS(events, "check_balance", params_check_balance(ALICE_ACC_IDX, 0, 0, 0, 0, 0));
return true;
}

View file

@ -1,8 +1,7 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2022 Zano Project
// Copyright (c) 2014-2018 The Louisdor 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 "chaingen.h"
#include "wallet_tests_basic.h"
@ -21,4 +20,22 @@ struct zarcanum_test_n_inputs_validation : public wallet_test
bool generate(std::vector<test_event_entry>& events) const;
};
struct zarcanum_gen_time_balance : public wallet_test
{
zarcanum_gen_time_balance();
bool generate(std::vector<test_event_entry>& events) const;
};
struct zarcanum_pos_block_math : public test_chain_unit_enchanced
{
zarcanum_pos_block_math();
bool generate(std::vector<test_event_entry>& events) const;
};
struct zarcanum_txs_with_big_shuffled_decoy_set_shuffled : public wallet_test
{
zarcanum_txs_with_big_shuffled_decoy_set_shuffled();
bool generate(std::vector<test_event_entry>& events) const;
};

View file

@ -109,7 +109,7 @@ bool generate_events(currency::core& c, cct_events_t& events, const cct_wallets_
wide_difficulty_type diff = 0;
if (prev_block.height != 0)
test_core_time::adjust(prev_block.bl.timestamp + DIFFICULTY_POW_TARGET);
r = bcs.create_block_template(b, miner_addr, diff, height, ex_nonce);
r = bcs.create_block_template(miner_addr, ex_nonce, b, diff, height);
CHECK_AND_ASSERT_MES(r, false, "create_block_template failed");
}
else

View file

@ -288,5 +288,98 @@ TEST(bppe, two)
ASSERT_TRUE(r);
return true;
}
TEST(bppe, power_128)
{
std::vector<bppe_signature> signatures_vector;
signatures_vector.reserve(200);
std::vector<std::vector<point_t>> commitments_vector;
commitments_vector.reserve(200);
std::vector<bppe_sig_commit_ref_t> sigs;
uint8_t err = 0;
bool r = false;
auto gen_rp_for_vec = [&](const scalar_vec_t& values)
{
signatures_vector.resize(signatures_vector.size() + 1);
bppe_signature &bppe_sig = signatures_vector.back();
commitments_vector.resize(commitments_vector.size() + 1);
std::vector<point_t>& commitments = commitments_vector.back();
scalar_vec_t masks, masks2;
for(auto& el: values)
{
masks.emplace_back(scalar_t::random());
masks2.emplace_back(scalar_t::random());
}
r = bppe_gen<bpp_crypto_trait_zano<128>>(values, masks, masks2, bppe_sig, commitments, &err);
ASSERT_TRUE(r);
sigs.emplace_back(bppe_sig, commitments);
return true;
};
auto gen_rp_for_value = [&](const scalar_t& v) { return gen_rp_for_vec(scalar_vec_t{ v }); };
const scalar_t s_128_max = scalar_t(UINT64_MAX, UINT64_MAX, 0, 0);
LOG_PRINT_L0("1");
ASSERT_TRUE(gen_rp_for_value(s_128_max));
ASSERT_TRUE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
LOG_PRINT_L0("2");
ASSERT_TRUE(gen_rp_for_value(scalar_t(crypto::rand<uint64_t>(), crypto::rand<uint64_t>(), 0, 0)));
ASSERT_TRUE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
LOG_PRINT_L0("3");
ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 1, 0)));
ASSERT_FALSE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
LOG_PRINT_L0("4");
ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, crypto::rand<uint64_t>(), 0)));
ASSERT_FALSE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
LOG_PRINT_L0("5");
ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 0, crypto::rand<uint64_t>())));
ASSERT_FALSE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
LOG_PRINT_L0("6");
ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 0, UINT64_MAX)));
ASSERT_FALSE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
LOG_PRINT_L0("7");
ASSERT_TRUE(gen_rp_for_vec(scalar_vec_t{s_128_max, s_128_max, s_128_max, s_128_max}));
LOG_PRINT_L0("simple generated");
ASSERT_TRUE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
LOG_PRINT_L0("simple verified");
for(size_t i = 0; i < 16; ++i)
{
LOG_PRINT_L0(" #" << i << " simple generated");
scalar_vec_t vec;
for(size_t j = 0, n = crypto::rand<size_t>() % 4 + 1; j < n; ++j)
vec.emplace_back(scalar_t(crypto::rand<uint64_t>(), crypto::rand<uint64_t>(), 0, 0));
ASSERT_TRUE(gen_rp_for_vec(vec));
}
LOG_PRINT_L0("verification started");
ASSERT_TRUE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
LOG_PRINT_L0("verification finished" << ENDL);
LOG_PRINT_L0("8");
ASSERT_TRUE(gen_rp_for_value(s_128_max));
ASSERT_TRUE(gen_rp_for_value(scalar_t(0, 0, 0, UINT64_MAX)));
ASSERT_TRUE(gen_rp_for_value(s_128_max));
ASSERT_FALSE(bppe_verify<bpp_crypto_trait_zano<128>>(sigs, &err));
signatures_vector.clear(), commitments_vector.clear(), sigs.clear();
return true;
}

View file

@ -84,9 +84,10 @@ TEST(tx_signatures_packing, 1)
{
// empty NLSAG
// v(0) + (1 + v(0) + 0 * 2 * 32) = 3
// v(1) + (1 + v(0) + 0 * 2 * 32) = 3
sigs.clear();
sigs.emplace_back(std::move(NLSAG_sig()));
ASSERT_EQ(3, t_serializable_object_to_blob(sigs).size());
ASSERT_EQ(3, get_object_blobsize(sigs));
}
@ -96,14 +97,28 @@ TEST(tx_signatures_packing, 1)
sigs.clear();
for(size_t i = 0; i < 128; ++i)
sigs.emplace_back(std::move(NLSAG_sig()));
ASSERT_EQ(258, t_serializable_object_to_blob(sigs).size());
ASSERT_EQ(258, get_object_blobsize(sigs));
}
{
// 128 10-ring NLSAGs
// v(128) + 128 * (1 + v(10) + 10 * 2 * 32) = 82178
sigs.clear();
NLSAG_sig nlsag = AUTO_VAL_INIT(nlsag);
nlsag.s.resize(10);
for(size_t i = 0; i < 128; ++i)
sigs.push_back(nlsag);
ASSERT_EQ(82178, t_serializable_object_to_blob(sigs).size());
ASSERT_EQ(82178, get_object_blobsize(sigs));
}
{
// empty ZC_sig
// v(0) + (1 + 32 + 32 + (1 + 10*32) + 32) = 99
// v(1) + (1 + 32 + 32 + (1 + 10*32) + 32) = 99
sigs.clear();
sigs.emplace_back(std::move(ZC_sig()));
ASSERT_EQ(99, t_serializable_object_to_blob(sigs).size());
ASSERT_EQ(99, get_object_blobsize(sigs));
}
@ -113,6 +128,7 @@ TEST(tx_signatures_packing, 1)
sigs.clear();
for(size_t i = 0; i < 128; ++i)
sigs.emplace_back(std::move(ZC_sig()));
ASSERT_EQ(12546, t_serializable_object_to_blob(sigs).size());
ASSERT_EQ(12546, get_object_blobsize(sigs));
}
@ -124,6 +140,7 @@ TEST(tx_signatures_packing, 1)
sigs.clear();
for(size_t i = 0; i < 128; ++i)
sigs.emplace_back(zc);
ASSERT_EQ(53506, t_serializable_object_to_blob(sigs).size());
ASSERT_EQ(53506, get_object_blobsize(sigs));
}
}