Merge branch 'zarcanum' into multiassets

This commit is contained in:
cryptozoidberg 2022-11-18 21:55:39 +01:00
commit b6fd8314ca
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
16 changed files with 443 additions and 208 deletions

View file

@ -27,7 +27,7 @@ namespace crypto
#define DBG_PRINT(x) (void(0)) //*/ std::cout << x << ENDL
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("bpp_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \
if (!(cond)) { LOG_PRINT_RED("bpp_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
@ -355,7 +355,7 @@ namespace crypto
bool bpp_verify(const std::vector<bpp_sig_commit_ref_t>& sigs, uint8_t* p_err = nullptr)
{
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("bpp_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \
if (!(cond)) { LOG_PRINT_RED("bpp_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
DBG_PRINT(ENDL << " . . . . bpp_verify() . . . . ");

View file

@ -28,7 +28,7 @@ namespace crypto
#define DBG_PRINT(x) (void(0)) // std::cout << x << ENDL
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("bppe_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \
if (!(cond)) { LOG_PRINT_RED("bppe_gen: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
@ -366,7 +366,7 @@ namespace crypto
bool bppe_verify(const std::vector<bppe_sig_commit_ref_t>& sigs, uint8_t* p_err = nullptr)
{
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("bppe_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << err_code, LOG_LEVEL_3); \
if (!(cond)) { LOG_PRINT_RED("bppe_verify: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)err_code, LOG_LEVEL_3); \
if (p_err) { *p_err = err_code; } return false; }
DBG_PRINT(ENDL << " . . . . bppe_verify() . . . . ");

View file

@ -14,6 +14,10 @@ namespace crypto
const scalar_t c_zarcanum_z_coeff_s = { 0, 1, 0, 0 }; // c_scalar_2p64
const mp::uint256_t c_zarcanum_z_coeff_mp = c_zarcanum_z_coeff_s.as_boost_mp_type<mp::uint256_t>();
#define DBG_VAL_PRINT(x) (void(0)) // std::cout << #x ": " << x << std::endl
#define DBG_PRINT(x) (void(0)) // std::cout << x << std::endl
mp::uint256_t zarcanum_precalculate_l_div_z_D(const mp::uint128_t& pos_difficulty)
{
//LOG_PRINT_GREEN_L0(ENDL << "floor( l / (z * D) ) = " << c_scalar_L.as_boost_mp_type<mp::uint256_t>() / (c_zarcanum_z_coeff_mp * pos_difficulty));
@ -43,7 +47,7 @@ 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 (!(cond)) { LOG_PRINT_RED("zarcanum_generate_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)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<CLSAG_GGXG_input_ref_t>& ring,
@ -51,6 +55,7 @@ namespace crypto
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 */)
{
DBG_PRINT("zarcanum_generate_proof");
const scalar_t a = stake_amount;
const scalar_t h = scalar_t(kernel_hash);
const scalar_t f_plus_q = stake_blinding_mask + secret_q;
@ -84,6 +89,9 @@ namespace crypto
point_t F = h * C_prime - dz * C + E + last_pow_block_id_hashed * h * c_point_H;
DBG_VAL_PRINT(h); DBG_VAL_PRINT(last_pow_block_id_hashed); DBG_VAL_PRINT(dz);
DBG_VAL_PRINT(C); DBG_VAL_PRINT(C_prime); DBG_VAL_PRINT(E); DBG_VAL_PRINT(F);
scalar_t r0 = scalar_t::random();
scalar_t r1 = scalar_t::random();
scalar_t r2 = scalar_t::random();
@ -92,9 +100,9 @@ namespace crypto
point_t R_01 = r0 * c_point_X + r1 * c_point_H_plus_G;
point_t R_23 = r2 * c_point_X + r3 * c_point_H_minus_G;
point_t R_4 = r4 * c_point_G;
point_t R_4 = r4 * c_point_X;
hash_helper_t::hs_t hash_calc(3);
hash_helper_t::hs_t hash_calc(7);
hash_calc.add_32_chars(CRYPTO_HDS_ZARCANUM_PROOF_HASH);
hash_calc.add_point(R_01);
hash_calc.add_point(R_23);
@ -160,26 +168,65 @@ namespace crypto
#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 (!(cond)) { LOG_PRINT_RED("zarcanum_verify_proof: \"" << #cond << "\" is false at " << LOCATION_SS << ENDL << "error code = " << (int)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 mp::uint128_t& pos_difficulty,
const zarcanum_proof& sig, uint8_t* p_err /* = nullptr */)
{
DBG_PRINT("zarcanum_verify_proof");
bool r = false;
// TODO @#@#
// make sure 0 < d <= l / floor(z * D)
const mp::uint256_t l_div_z_D_mp = crypto::zarcanum_precalculate_l_div_z_D(pos_difficulty);
const scalar_t l_div_z_D(l_div_z_D_mp);
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(!sig.d.is_zero() && sig.d < l_div_z_D, 2);
const scalar_t dz = sig.d * c_zarcanum_z_coeff_s;
std::vector<point_t> E_for_range_proof = { point_t(sig.E) };
// calculate h
const scalar_t h = scalar_t(kernel_hash);
// calculate F
point_t C_prime = point_t(sig.C_prime);
C_prime.modify_mul8();
point_t C = point_t(sig.C);
C.modify_mul8();
point_t E = point_t(sig.E);
E.modify_mul8();
point_t F = h * C_prime - dz * C + E + last_pow_block_id_hashed * h * c_point_H;
DBG_VAL_PRINT(h); DBG_VAL_PRINT(last_pow_block_id_hashed); DBG_VAL_PRINT(dz);
DBG_VAL_PRINT(C); DBG_VAL_PRINT(C_prime); DBG_VAL_PRINT(E); DBG_VAL_PRINT(F);
// check three proofs with a shared Fiat-Shamir challenge c
point_t C_plus_C_prime = C + C_prime;
point_t C_minus_C_prime = C - C_prime;
hash_helper_t::hs_t hash_calc(7);
hash_calc.add_32_chars(CRYPTO_HDS_ZARCANUM_PROOF_HASH);
hash_calc.add_point(sig.y0 * c_point_X + sig.y1 * c_point_H_plus_G - sig.c * C_plus_C_prime); // y_0 * X + y1 (H + G) - c (C + C')
hash_calc.add_point(sig.y2 * c_point_X + sig.y3 * c_point_H_minus_G - sig.c * C_minus_C_prime); // y_2 * X + y3 (H - G) - c (C - C')
hash_calc.add_point(sig.y4 * c_point_X - sig.c * F); // y_4 * X - c * F
hash_calc.add_point(C_plus_C_prime);
hash_calc.add_point(C_minus_C_prime);
hash_calc.add_point(F);
scalar_t c_prime = hash_calc.calc_hash();
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(sig.c == c_prime, 3);
// check extended range proof for E
std::vector<point_t> E_for_range_proof = { point_t(sig.E) }; // consider changing to 8*sig.E to avoid additional conversion
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 extended CLSAG-GGXG ring signature
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;
}
#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
} // namespace crypto

View file

@ -52,6 +52,7 @@ namespace crypto
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 mp::uint128_t& pos_difficulty,
const zarcanum_proof& sig, uint8_t* p_err = nullptr);
} // namespace crypto

View file

@ -2562,10 +2562,10 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU
VARIANT_CASE_CONST(tx_out_zarcanum, toz)
{
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oen.amount_commitment = toz.amount_commitment;
oen.concealing_point = toz.concealing_point;
oen.global_amount_index = g_index;
oen.stealth_address = toz.stealth_address;
oen.amount_commitment = toz.amount_commitment;
oen.concealing_point = toz.concealing_point;
}
VARIANT_SWITCH_END();
@ -5428,7 +5428,8 @@ bool blockchain_storage::validate_pos_block(const block& b,
// build kernel and calculate hash
stake_kernel sk = AUTO_VAL_INIT(sk);
stake_modifier_type sm = AUTO_VAL_INIT(sm);
bool r = build_stake_modifier(sm, alt_chain, split_height);
uint64_t last_pow_block_height = 0;
bool r = build_stake_modifier(sm, alt_chain, split_height, nullptr, &last_pow_block_height);
CHECK_AND_ASSERT_MES(r, false, "failed to build_stake_modifier");
r = build_kernel(stake_key_image, sk, sm, b.timestamp);
CHECK_AND_ASSERT_MES(r, false, "failed to build kernel_stake");
@ -5442,7 +5443,6 @@ bool blockchain_storage::validate_pos_block(const block& b,
CHECK_AND_ASSERT_MES(b.miner_tx.signatures.size() == 1, false, "incorrect number of stake input signatures: " << b.miner_tx.signatures.size());
CHECK_AND_ASSERT_MES(b.miner_tx.signatures[0].type() == typeid(zarcanum_sig), false, "incorrect sig 0 type: " << b.miner_tx.signatures[0].type().name());
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;
@ -5452,6 +5452,9 @@ bool blockchain_storage::validate_pos_block(const block& b,
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.");
// make sure that all referring inputs are either older then, or the same age as, the most resent PoW block.
CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_block_height, false, "stake input refs' max related block height is " << max_related_block_height << " while last PoW block height is " << last_pow_block_height);
// build a ring of references
vector<crypto::CLSAG_GGXG_input_ref_t> ring;
ring.reserve(scan_contex.zc_outs.size());
@ -5461,10 +5464,8 @@ bool blockchain_storage::validate_pos_block(const block& b,
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
r = crypto::zarcanum_verify_proof(id, kernel_hash, ring, last_pow_block_id_hashed, stake_input.k_image, basic_diff, sig, &err);
CHECK_AND_ASSERT_MES(r, false, "zarcanum_verify_proof failed with code " << (int)err);
return true;
}
else
@ -6098,10 +6099,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
int64_t ts_diff = actual_ts - m_core_runtime_config.get_core_time();
powpos_str_entry << "PoS:\t" << proof_hash << ", stake amount: ";
if (pos_coinstake_amount != UINT64_MAX)
{
powpos_str_entry << print_money_brief(pos_coinstake_amount);
powpos_str_entry << ", final_difficulty: " << this_coin_diff;
}
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
@ -6452,7 +6455,9 @@ bool blockchain_storage::build_kernel(const block& bl, stake_kernel& kernel, uin
return build_kernel(txin.k_image, kernel, stake_modifier, bl.timestamp);
}
//------------------------------------------------------------------
bool blockchain_storage::build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain, uint64_t split_height, crypto::hash *p_last_block_hash /* = nullptr */) const
bool blockchain_storage::build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain, uint64_t split_height,
crypto::hash* p_last_block_hash /* = nullptr */,
uint64_t* p_last_pow_block_height /* = nullptr */ ) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
sm = stake_modifier_type();
@ -6474,6 +6479,9 @@ bool blockchain_storage::build_stake_modifier(stake_modifier_type& sm, const alt
if (p_last_block_hash != nullptr)
*p_last_block_hash = get_block_hash(m_db_blocks.back()->bl);
if (p_last_pow_block_height != nullptr)
*p_last_pow_block_height = pbei_last_pow->height;
return true;
}
//------------------------------------------------------------------
@ -6688,22 +6696,24 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
const txin_v& input_v = input_tx.vin[input_index];
const txin_to_key& input_to_key = get_to_key_input_from_txin_v(input_v);
const crypto::key_image& input_key_image = get_key_image_from_txin_v(input_v);
const std::vector<txout_ref_v>& input_key_offsets = get_key_offsets_from_txin_v(input_v);
const uint64_t input_amount = get_amount_from_variant(input_v);
// check case b1: key_image spent status in main chain, should be either non-spent or has spent height >= split_height
auto p = m_db_spent_keys.get(input_to_key.k_image);
CHECK_AND_ASSERT_MES(p == nullptr || *p >= split_height, false, "key image " << input_to_key.k_image << " has been already spent in main chain at height " << *p << ", split height: " << split_height);
auto p = m_db_spent_keys.get(input_key_image);
CHECK_AND_ASSERT_MES(p == nullptr || *p >= split_height, false, "key image " << input_key_image << " has been already spent in main chain at height " << *p << ", split height: " << split_height);
TIME_MEASURE_START(ki_lookup_time);
//check key_image in altchain
//check among this alt block already collected key images first
if (collected_keyimages.find(input_to_key.k_image) != collected_keyimages.end())
if (collected_keyimages.find(input_key_image) != collected_keyimages.end())
{
// cases b2, b3
LOG_ERROR("key image " << input_to_key.k_image << " already spent in this alt block");
LOG_ERROR("key image " << input_key_image << " already spent in this alt block");
return false;
}
auto ki_it = m_altblocks_keyimages.find(input_to_key.k_image);
auto ki_it = m_altblocks_keyimages.find(input_key_image);
if (ki_it != m_altblocks_keyimages.end())
{
//have some entry for this key image. Check if this key image belongs to this alt chain
@ -6713,18 +6723,18 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
if (alt_chain_block_ids.find(h) != alt_chain_block_ids.end())
{
// cases b2, b3
LOG_ERROR("key image " << input_to_key.k_image << " already spent in altchain");
LOG_ERROR("key image " << input_key_image << " already spent in altchain");
return false;
}
}
}
//update altchain with key image
collected_keyimages.insert(input_to_key.k_image);
collected_keyimages.insert(input_key_image);
TIME_MEASURE_FINISH(ki_lookup_time);
ki_lookuptime = ki_lookup_time;
std::vector<txout_ref_v> abs_key_offsets = relative_output_offsets_to_absolute(input_to_key.key_offsets);
CHECK_AND_ASSERT_MES(abs_key_offsets.size() > 0 && abs_key_offsets.size() == input_to_key.key_offsets.size(), false, "internal error: abs_key_offsets.size()==" << abs_key_offsets.size() << ", input_to_key.key_offsets.size()==" << input_to_key.key_offsets.size());
std::vector<txout_ref_v> abs_key_offsets = relative_output_offsets_to_absolute(input_key_offsets);
CHECK_AND_ASSERT_MES(abs_key_offsets.size() > 0 && abs_key_offsets.size() == input_key_offsets.size(), false, "internal error: abs_key_offsets.size()==" << abs_key_offsets.size() << ", input_key_offsets.size()==" << input_key_offsets.size());
// eventually we should found all public keys for all outputs this input refers to, for checking ring signature
std::vector<crypto::public_key> pub_keys(abs_key_offsets.size(), null_pkey);
@ -6739,7 +6749,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
if (!alt_chain.empty())
{
auto abg_it = alt_chain.back()->second.gindex_lookup_table.find(input_to_key.amount);
auto abg_it = alt_chain.back()->second.gindex_lookup_table.find(input_amount);
if (abg_it != alt_chain.back()->second.gindex_lookup_table.end())
{
amount_touched_altchain = true;
@ -6749,13 +6759,13 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
else
{
//quite easy,
global_outs_for_amount = m_db_outputs.get_item_size(input_to_key.amount);
global_outs_for_amount = m_db_outputs.get_item_size(input_amount);
}
}
else
{
//quite easy,
global_outs_for_amount = m_db_outputs.get_item_size(input_to_key.amount);
global_outs_for_amount = m_db_outputs.get_item_size(input_amount);
}
CHECK_AND_ASSERT_MES(pub_keys.size() == abs_key_offsets.size(), false, "pub_keys.size()==" << pub_keys.size() << " != abs_key_offsets.size()==" << abs_key_offsets.size()); // just a little bit of paranoia
@ -6773,7 +6783,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
{
uint64_t offset_gindex = boost::get<uint64_t>(off);
CHECK_AND_ASSERT_MES(amount_touched_altchain || (offset_gindex < global_outs_for_amount), false,
"invalid global output index " << offset_gindex << " for amount=" << input_to_key.amount <<
"invalid global output index " << offset_gindex << " for amount=" << input_amount <<
", max is " << global_outs_for_amount <<
", referred to by offset #" << pk_n <<
", amount_touched_altchain = " << amount_touched_altchain);
@ -6782,7 +6792,7 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
bool found_the_key = false;
for (auto alt_it = alt_chain.rbegin(); alt_it != alt_chain.rend(); alt_it++)
{
auto it_aag = (*alt_it)->second.gindex_lookup_table.find(input_to_key.amount);
auto it_aag = (*alt_it)->second.gindex_lookup_table.find(input_amount);
if (it_aag == (*alt_it)->second.gindex_lookup_table.end())
{
CHECK_AND_ASSERT_MES(alt_it != alt_chain.rbegin(), false, "internal error: was marked as amount_touched_altchain but unable to find on first entry");
@ -6796,8 +6806,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
//TODO: At the moment we ignore check of mix_attr against mixing to simplify alt chain check, but in future consider it for stronger validation
uint64_t local_offset = offset_gindex - it_aag->second;
auto& alt_keys = (*alt_it)->second.outputs_pub_keys;
CHECK_AND_ASSERT_MES(local_offset < alt_keys[input_to_key.amount].size(), false, "Internal error: local_offset=" << local_offset << " while alt_keys[" << input_to_key.amount << " ].size()=" << alt_keys.size());
const output_key_or_htlc_v& out_in_alt = alt_keys[input_to_key.amount][local_offset];
CHECK_AND_ASSERT_MES(local_offset < alt_keys[input_amount].size(), false, "Internal error: local_offset=" << local_offset << " while alt_keys[" << input_amount << " ].size()=" << alt_keys.size());
const output_key_or_htlc_v& out_in_alt = alt_keys[input_amount][local_offset];
/*
here we do validation against compatibility of input and output type
@ -6834,8 +6844,8 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
continue;
//otherwise lookup in main chain index
}
auto p = m_db_outputs.get_subitem(input_to_key.amount, offset_gindex);
CHECK_AND_ASSERT_MES(p != nullptr, false, "global output was not found, amount: " << input_to_key.amount << ", gindex: " << offset_gindex << ", referred to by offset #" << pk_n);
auto p = m_db_outputs.get_subitem(input_amount, offset_gindex);
CHECK_AND_ASSERT_MES(p != nullptr, false, "global output was not found, amount: " << input_amount << ", gindex: " << offset_gindex << ", referred to by offset #" << pk_n);
tx_id = p->tx_id;
out_n = p->out_no;
}
@ -6957,10 +6967,21 @@ bool blockchain_storage::validate_alt_block_input(const transaction& input_tx,
VARIANT_SWITCH_END();
}
// @#@# TODO review the following checks!
// do input checks (attachment_info, ring signature and extra signature, etc.)
r = check_input_signature(input_tx, input_index, input_to_key, input_tx_hash, pub_key_pointers);
CHECK_AND_ASSERT_MES(r, false, "to_key input validation failed");
VARIANT_SWITCH_BEGIN(input_v);
VARIANT_CASE_CONST(txin_to_key, input_to_key)
r = check_input_signature(input_tx, input_index, input_to_key, input_tx_hash, pub_key_pointers);
CHECK_AND_ASSERT_MES(r, false, "to_key input validation failed");
VARIANT_CASE_CONST(txin_zc_input, input_zc);
uint64_t max_related_block_height = 0;
r = check_tx_input(input_tx, input_index, input_zc, input_tx_hash, max_related_block_height);
CHECK_AND_ASSERT_MES(r, false, "check_tx_input failed");
VARIANT_CASE_OTHER()
LOG_ERROR("unexpected input type: " << input_v.type().name());
return false;
VARIANT_SWITCH_END();
// TODO: consider checking input_tx for valid extra attachment info as it's checked in check_tx_inputs()

View file

@ -346,7 +346,7 @@ namespace currency
stake_kernel& kernel,
const stake_modifier_type& stake_modifier,
uint64_t timestamp) const;
bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash *p_last_block_hash = nullptr) const;
bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash* p_last_block_hash = nullptr, uint64_t* p_last_pow_block_height = nullptr) const;
bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t source_max_unlock_time)const;
bool validate_pos_block(const block& b, const crypto::hash& id, bool for_altchain)const;
@ -660,7 +660,7 @@ namespace currency
//bool resync_spent_tx_flags();
bool prune_ring_signatures_and_attachments_if_need();
bool prune_ring_signatures_and_attachments(uint64_t height, uint64_t& transactions_pruned, uint64_t& signatures_pruned, uint64_t& attachments_pruned);
// bool build_stake_modifier_for_alt(const alt_chain_type& alt_chain, stake_modifier_type& sm);
template<class visitor_t>
bool enum_blockchain(visitor_t& v, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0) const;
bool update_spent_tx_flags_for_input(uint64_t amount, const txout_ref_v& o, bool spent);

View file

@ -87,7 +87,7 @@ namespace currency
result.outputs_count = outs_count;
uint8_t err = 0;
bool r = crypto::bpp_gen<>(amounts, blinding_masks, commitments_1div8, result.bpp, &err);
CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << err);
CHECK_AND_ASSERT_MES(r, false, "bpp_gen failed with error " << (int)err);
return true;
}

View file

@ -342,7 +342,7 @@ namespace currency
{
std::list<uint64_t> amounts;
uint64_t decoys_count; // how many decoy outputs needed (per amount)
uint64_t height_upper_limit; // all the decoy outputs must be either older than, or the same age as this height
uint64_t height_upper_limit; // if nonzero, all the decoy outputs must be either older than, or the same age as this height
bool use_forced_mix_outs;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amounts)
@ -359,6 +359,9 @@ namespace currency
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)
{}
out_entry(uint64_t global_amount_index, const crypto::public_key& stealth_address)
: global_amount_index(global_amount_index), stealth_address(stealth_address), amount_commitment{}, concealing_point{}
{}
uint64_t global_amount_index;
crypto::public_key stealth_address;
crypto::public_key concealing_point;

View file

@ -3774,12 +3774,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
// generate sring signature
sig.s.resize(1);
crypto::generate_ring_signature(block_hash,
stake_input.k_image,
keys_ptrs,
secret_x,
0,
&sig.s[0]);
crypto::generate_ring_signature(block_hash, stake_input.k_image, keys_ptrs, secret_x, 0, sig.s.data());
WLT_LOG_L4("GENERATED RING SIGNATURE for PoS block coinbase: block_id " << block_hash
<< "txin.k_image" << stake_input.k_image
@ -3877,7 +3872,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
}
#endif
crypto::hash tx_hash_for_sig = get_transaction_hash(b.miner_tx); // TODO @#@# change to block hash after the corresponding test is made
crypto::hash tx_hash_for_sig = get_block_hash(b);
uint8_t err = 0;
r = crypto::zarcanum_generate_proof(tx_hash_for_sig, cxt.kernel_hash, ring, cxt.last_pow_block_id_hashed, cxt.sk.kimage,

View file

@ -154,22 +154,20 @@ void test_generator::add_block(const currency::block& blk,
uint64_t block_reward;
get_block_reward(is_pos_block(blk), misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, currency::get_block_height(blk));
crypto::hash block_hash = get_block_hash(blk);
m_blocks_info[block_hash] = block_info(blk, already_generated_coins + block_reward, block_size, cum_diff, tx_list, ks_hash);
std::stringstream ss_tx_hashes;
for (auto& h : blk.tx_hashes)
{
ss_tx_hashes << " [tx]: " << h << ENDL;
}
LOG_PRINT_MAGENTA("ADDED_BLOCK[" << block_hash << "][" << (is_pos_block(blk)? "PoS":"PoW") <<"][" << get_block_height(blk) << "][cumul_diff:" << cum_diff << "]" << ENDL << ss_tx_hashes.str(), LOG_LEVEL_0);
add_block_info(block_info(blk, already_generated_coins + block_reward, block_size, cum_diff, tx_list, ks_hash));
}
void test_generator::add_block_info(const block_info& bi)
{
m_blocks_info[get_block_hash(bi.b)] = bi;
crypto::hash block_hash = get_block_hash(bi.b);
m_blocks_info[block_hash] = bi;
std::stringstream ss_tx_hashes;
for (auto& h : bi.b.tx_hashes)
{
ss_tx_hashes << " [tx]: " << h << ENDL;
}
LOG_PRINT_MAGENTA("ADDED_BLOCK[" << block_hash << "][" << (is_pos_block(bi.b)? "PoS":"PoW") <<"][" << get_block_height(bi.b) << "][cumul_diff:" << bi.cumul_difficulty << "]" << ENDL << ss_tx_hashes.str(), LOG_LEVEL_0);
}
bool test_generator::add_block_info(const currency::block& b, const std::list<currency::transaction>& tx_list)
@ -179,7 +177,8 @@ bool test_generator::add_block_info(const currency::block& b, const std::list<cu
txs_total_size += get_object_blobsize(tx);
uint64_t mined_money = get_reward_from_miner_tx(b.miner_tx);
crypto::hash sk_hash = null_hash;
if (is_pos_block(b))
bool pos = is_pos_block(b);
if (pos)
{
stake_kernel sk = AUTO_VAL_INIT(sk);
std::vector<const block_info*> chain;
@ -193,15 +192,29 @@ bool test_generator::add_block_info(const currency::block& b, const std::list<cu
}
uint64_t pow_idx = get_last_block_of_type(false, chain);
sk.stake_modifier.last_pow_id = get_block_hash(chain[pow_idx]->b);
sk.kimage = boost::get<txin_to_key>(b.miner_tx.vin[1]).k_image;
sk.kimage = get_key_image_from_txin_v(b.miner_tx.vin[1]);
sk.block_timestamp = b.timestamp;
sk_hash = crypto::cn_fast_hash(&sk, sizeof(sk));
}
add_block_info(block_info(b, get_already_generated_coins(b.prev_id) + mined_money,
txs_total_size + get_object_blobsize(b.miner_tx), get_cumul_difficulty_for_next_block(b.prev_id), tx_list, sk_hash));
txs_total_size + get_object_blobsize(b.miner_tx), get_cumul_difficulty_for_next_block(b.prev_id, !pos), tx_list, sk_hash));
return true;
}
bool test_generator::remove_block_info(const crypto::hash& block_id)
{
if (m_blocks_info.erase(block_id) == 1)
{
LOG_PRINT_MAGENTA("REMOVED BLOCK[" << block_id << "]", LOG_LEVEL_0);
return true;
}
return false;
}
bool test_generator::remove_block_info(const currency::block& blk)
{
return remove_block_info(get_block_hash(blk));
}
bool test_generator::construct_block(currency::block& blk,
@ -259,10 +272,9 @@ bool test_generator::construct_block(currency::block& blk,
build_outputs_indext_for_chain(blocks, oi, txs_outs);
//build wallets
build_wallets(blocks, coin_stake_sources, txs_outs, wallets);
build_wallets(blocks, coin_stake_sources, txs_outs, oi, wallets);
r = find_kernel(coin_stake_sources,
blocks,
oi,
wallets,
pe,
won_walled_index,
@ -361,6 +373,7 @@ bool test_generator::sign_block(const tools::wallet2::mining_context& mining_con
bool test_generator::build_wallets(const blockchain_vector& blockchain,
const std::list<currency::account_base>& accs,
const tx_global_indexes& txs_outs,
const outputs_index& oi,
wallets_vector& wallets,
const core_runtime_config& cc)
{
@ -369,10 +382,12 @@ bool test_generator::build_wallets(const blockchain_vector& blockchain,
const tx_global_indexes& m_txs_outs;
const blockchain_vector& m_blockchain;
const core_runtime_config& m_core_runtime_config;
const outputs_index& m_outputs_index;
stub_core_proxy(const blockchain_vector& blockchain, const tx_global_indexes& txs_outs, const core_runtime_config& crc)
stub_core_proxy(const blockchain_vector& blockchain, const tx_global_indexes& txs_outs, const outputs_index& oi, const core_runtime_config& crc)
: m_blockchain(blockchain)
, m_txs_outs(txs_outs)
, m_outputs_index(oi)
, m_core_runtime_config(crc)
{}
@ -412,9 +427,68 @@ bool test_generator::build_wallets(const blockchain_vector& blockchain,
return true;
}
};
bool call_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS(const currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& rqt, currency::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& rsp) override
{
for (uint64_t amount : rqt.amounts)
{
rsp.outs.resize(rsp.outs.size() + 1);
auto& rsp_entry = rsp.outs.back();
rsp_entry.amount = amount;
auto it = m_outputs_index.find(amount);
if (it == m_outputs_index.end())
continue;
std::shared_ptr<tools::i_core_proxy> tmp_proxy(new stub_core_proxy(blockchain, txs_outs, cc));
std::vector<size_t> random_mapping(it->second.size());
for(size_t i = 0; i < random_mapping.size(); ++i)
random_mapping[i] = i;
std::shuffle(random_mapping.begin(), random_mapping.end(), crypto::uniform_random_bit_generator());
for (size_t gindex : random_mapping)
{
const out_index_info& oii = it->second[gindex];
if (rqt.height_upper_limit != 0 && oii.block_height > rqt.height_upper_limit)
continue;
const transaction& tx = oii.in_block_tx_index == 0 ? m_blockchain[oii.block_height]->b.miner_tx : m_blockchain[oii.block_height]->m_transactions[oii.in_block_tx_index];
auto& out_v = tx.vout[oii.in_tx_out_index];
uint8_t mix_attr = 0;
if (!get_mix_attr_from_tx_out_v(out_v, mix_attr))
continue;
if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
continue;
if (rqt.use_forced_mix_outs && mix_attr == CURRENCY_TO_KEY_OUT_RELAXED)
continue;
if (mix_attr != CURRENCY_TO_KEY_OUT_RELAXED && mix_attr > rqt.decoys_count)
continue;
if (amount == 0 && out_v.type() == typeid(tx_out_zarcanum))
{
const tx_out_zarcanum& out_zc = boost::get<tx_out_zarcanum>(out_v);
rsp_entry.outs.emplace_back(gindex, out_zc.stealth_address, out_zc.amount_commitment, out_zc.concealing_point);
}
else if (amount != 0 && out_v.type() == typeid(tx_out_bare))
{
txout_target_v out_tv = boost::get<tx_out_bare>(out_v).target;
if (out_tv.type() != typeid(txout_to_key))
continue;
rsp_entry.outs.emplace_back(gindex, boost::get<txout_to_key>(out_tv).key);
}
if (rsp_entry.outs.size() >= rqt.decoys_count)
break;
}
if (rsp_entry.outs.size() < rqt.decoys_count)
{
rsp.status = API_RETURN_CODE_NOT_ENOUGH_OUTPUTS_FOR_MIXING;
return true;
}
}
rsp.status = API_RETURN_CODE_OK;
return true;
}
}; // struct stub_core_proxy
std::shared_ptr<tools::i_core_proxy> tmp_proxy(new stub_core_proxy(blockchain, txs_outs, oi, cc));
//build wallets
wallets.clear();
@ -471,7 +545,7 @@ bool test_generator::build_wallets(const crypto::hash& blockchain_head, const st
get_block_chain(blocks, blockchain_head, std::numeric_limits<size_t>::max());
build_outputs_indext_for_chain(blocks, oi, txs_outs);
return build_wallets(blocks, accounts, txs_outs, wallets, cc);
return build_wallets(blocks, accounts, txs_outs, oi, wallets, cc);
}
size_t test_generator::get_tx_out_gindex(const crypto::hash& blockchain_head, const crypto::hash& tx_hash, const size_t output_index) const
@ -509,7 +583,6 @@ uint64_t test_generator::get_timestamps_median(const crypto::hash& blockchain_he
bool test_generator::find_kernel(const std::list<currency::account_base>& accs,
const blockchain_vector& blck_chain,
const outputs_index& indexes,
wallets_vector& wallets,
currency::pos_entry& pe,
size_t& found_wallet_index,
@ -577,7 +650,7 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo
{
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));
index[amount].push_back(out_index_info{h, 0, out_i});
}
for (size_t tx_index = 0; tx_index != blocks[h]->m_transactions.size(); tx_index++)
@ -587,7 +660,7 @@ bool test_generator::build_outputs_indext_for_chain(const blockchain_vector& blo
{
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));
index[amount].push_back(out_index_info{h, tx_index + 1, out_i});
}
}
}
@ -652,6 +725,7 @@ bool test_generator::build_stake_modifier(stake_modifier_type& sm, const test_ge
return true;
}
currency::wide_difficulty_type test_generator::get_difficulty_for_next_block(const crypto::hash& head_id, bool pow) const
{
std::vector<const block_info*> blocks;
@ -660,6 +734,29 @@ currency::wide_difficulty_type test_generator::get_difficulty_for_next_block(con
return get_difficulty_for_next_block(blocks, pow);
}
bool test_generator::get_params_for_next_pos_block(const crypto::hash& head_id, currency::wide_difficulty_type& pos_difficulty, crypto::hash& last_pow_block_hash,
crypto::hash& last_pos_block_kernel_hash) const
{
std::vector<const block_info*> blocks;
get_block_chain(blocks, head_id, std::numeric_limits<size_t>::max());
pos_difficulty = get_difficulty_for_next_block(blocks, false);
uint64_t pos_idx = get_last_block_of_type(true, blocks);
if (pos_idx != 0)
last_pos_block_kernel_hash = blocks[pos_idx]->ks_hash;
uint64_t pow_idx = get_last_block_of_type(false, blocks);
if (pow_idx == 0)
return false;
last_pow_block_hash = get_block_hash(blocks[pow_idx]->b);
return true;
}
/* static */ currency::wide_difficulty_type test_generator::get_difficulty_for_next_block(const std::vector<const block_info*>& blocks, bool pow)
{
std::vector<uint64_t> timestamps;
@ -932,7 +1029,7 @@ bool test_generator::construct_pow_block_with_alias_info_in_coinbase(const accou
struct output_index
{
const currency::tx_out_v out_v;
uint64_t amount;
uint64_t amount; // actual amount (decoded, cannot be zero)
size_t tx_no; // index of transaction in block
size_t out_no; // index of out in transaction
size_t idx; // global index
@ -969,7 +1066,7 @@ struct output_index
}*/
};
typedef std::map<uint64_t, std::vector<size_t> > map_output_t; // amount -> [N -> global out index]
typedef std::map<uint64_t, std::vector<size_t> > map_output_t; // amount -> [N -> global out index] (for 'my' outputs, no specific order)
typedef std::map<uint64_t, std::vector<output_index> > map_output_idx_t; // amount -> [global out index -> 'output_index']
typedef std::pair<uint64_t, size_t> outloc_t;

View file

@ -266,10 +266,18 @@ public:
bool check_tx_verification_context(const currency::tx_verification_context& tvc, bool tx_added, size_t event_idx, const currency::transaction& /*tx*/)
{
if (m_invalid_tx_index == event_idx)
{
CHECK_AND_ASSERT_MES(tvc.m_verification_failed, false, ENDL << "event #" << event_idx << ": the tx passed the verification, although it had been marked as invalid" << ENDL);
return tvc.m_verification_failed;
}
if (m_unverifiable_tx_index == event_idx)
{
CHECK_AND_ASSERT_MES(tvc.m_verification_impossible, false, ENDL << "event #" << event_idx << ": the tx passed normally, although it had been marked as unverifiable" << ENDL);
return tvc.m_verification_impossible;
}
CHECK_AND_ASSERT_MES(tx_added, false, ENDL << "event #" << event_idx << ": the tx has not been added for some reason" << ENDL);
return !tvc.m_verification_failed && tx_added;
}
@ -277,10 +285,16 @@ public:
bool check_block_verification_context(const currency::block_verification_context& bvc, size_t event_idx, const currency::block& /*block*/)
{
if (m_invalid_block_index == event_idx)
{
CHECK_AND_ASSERT_MES(bvc.m_verification_failed, false, ENDL << "event #" << event_idx << ": the block passed the verification, although it had been marked as invalid" << ENDL);
return bvc.m_verification_failed;
}
if (m_orphan_block_index == event_idx)
{
CHECK_AND_ASSERT_MES(bvc.m_marked_as_orphaned, false, ENDL << "event #" << event_idx << ": the block passed normally, although it had been marked as orphaned" << ENDL);
return bvc.m_marked_as_orphaned;
}
return !bvc.m_verification_failed;
}
@ -375,9 +389,16 @@ public:
crypto::hash ks_hash;
};
// amount vec_ind, tx_index, out index in tx
typedef std::map<uint64_t, std::vector<std::tuple<size_t, size_t, size_t> > > outputs_index;
typedef std::unordered_map<crypto::hash, std::vector<uint64_t> > tx_global_indexes;
struct out_index_info
{
size_t block_height;
size_t in_block_tx_index;
size_t in_tx_out_index;
};
typedef std::map<uint64_t, std::vector<out_index_info> > outputs_index; // amount -> [gindex -> out_index_info]
typedef std::unordered_map<crypto::hash, std::vector<uint64_t> > tx_global_indexes; // tx_hash -> vector of tx's outputs global indices
typedef std::vector<const block_info*> blockchain_vector;
@ -406,6 +427,7 @@ public:
//-----------
static currency::wide_difficulty_type get_difficulty_for_next_block(const std::vector<const block_info*>& blocks, bool pow = true);
currency::wide_difficulty_type get_difficulty_for_next_block(const crypto::hash& head_id, bool pow = true) const;
bool get_params_for_next_pos_block(const crypto::hash& head_id, currency::wide_difficulty_type& pos_difficulty, crypto::hash& last_pow_block_hash, crypto::hash& last_pos_block_kernel_hash) const;
currency::wide_difficulty_type get_cumul_difficulty_for_next_block(const crypto::hash& head_id, bool pow = true) const;
void get_block_chain(std::vector<const block_info*>& blockchain, const crypto::hash& head, size_t n) const;
void get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const;
@ -421,7 +443,6 @@ public:
bool find_kernel(const std::list<currency::account_base>& accs,
const blockchain_vector& blck_chain,
const outputs_index& indexes,
wallets_vector& wallets,
currency::pos_entry& pe,
size_t& found_wallet_index,
@ -431,6 +452,7 @@ public:
bool build_wallets(const blockchain_vector& blocks,
const std::list<currency::account_base>& accs,
const tx_global_indexes& txs_outs,
const outputs_index& oi,
wallets_vector& wallets,
const currency::core_runtime_config& cc = currency::get_default_core_runtime_config());
@ -483,6 +505,9 @@ public:
bool add_block_info(const currency::block& b, const std::list<currency::transaction>& tx_list);
bool remove_block_info(const currency::block& blk);
bool remove_block_info(const crypto::hash& block_id);
bool construct_block(currency::block& blk,
uint64_t height,
const crypto::hash& prev_id,

View file

@ -1082,6 +1082,7 @@ int main(int argc, char* argv[])
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(zarcanum_pos_block_math);
// GENERATE_AND_PLAY(gen_block_reward);
// END OF TESTS */

View file

@ -13,6 +13,53 @@ using namespace epee;
using namespace crypto;
using namespace currency;
// helpers
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 = 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 = 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);
const transaction& stake = coinstake_scr_block.miner_tx;
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount =boost::get<currency::tx_out_bare>( stake.vout[stake_output_idx]).amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(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;
pos_block_builder pb;
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());
if (!before_sign_cb(pb.m_block))
return false;
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, acc);
output = pb.m_block;
return true;
}
//------------------------------------------------------------------------------
checkpoints_test::checkpoints_test()
{
REGISTER_CALLBACK_METHOD(checkpoints_test, set_checkpoint);

View file

@ -139,8 +139,6 @@ void pos_block_builder::step3b(
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,
@ -199,6 +197,7 @@ void pos_block_builder::step4_generate_coinbase_tx(size_t median_size,
}
// supports Zarcanum and mixins
// (se.outputs can be unsorted)
void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const currency::account_keys& stakeholder_keys)
{
bool r = false;
@ -219,25 +218,28 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu
stake_input.k_image = m_context.sk.kimage;
size_t prepared_real_out_index = 0;
std::vector<tx_source_entry::output_entry> prepared_outputs = prepare_outputs_entries_for_key_offsets(se.outputs, se.real_output, prepared_real_out_index);
std::vector<crypto::CLSAG_GGXG_input_ref_t> ring;
for(const auto& el : se.outputs)
for(const auto& el : prepared_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
crypto::hash tx_hash_for_sig = get_block_hash(m_block);
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,
secret_x, m_context.secret_q, prepared_real_out_index, 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
{
CHECK_AND_ASSERT_THROW_MES(se.outputs.size() == 1, "PoS blocks with NLSAG and mixing are not supported atm");
// 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]);
@ -255,7 +257,7 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu
m_step = 5;
}
// pre-Zarcanum sign function
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)
{
@ -269,47 +271,3 @@ void pos_block_builder::step5_sign(const crypto::public_key& stake_tx_pub_key, s
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 = 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 = 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);
const transaction& stake = coinstake_scr_block.miner_tx;
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount =boost::get<currency::tx_out_bare>( stake.vout[stake_output_idx]).amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(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;
pos_block_builder pb;
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());
if (!before_sign_cb(pb.m_block))
return false;
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, acc);
output = pb.m_block;
return true;
}

View file

@ -69,8 +69,6 @@ struct pos_block_builder
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);
//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;
@ -84,25 +82,3 @@ struct pos_block_builder
currency::pos_mining_context m_context {};
};
/* 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,
crypto::key_image pos_stake_keyimage,
size_t pos_stake_gindex,
const currency::account_public_address &reward_receiving_address,
const currency::account_public_address &stakeholder_address,
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()); */
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);
inline bool mine_next_pos_block_in_playtime(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)
{
return mine_next_pos_block_in_playtime_sign_cb(c, prev_block, coinstake_scr_block, acc, [](currency::block&){ return true; }, output);
}

View file

@ -14,10 +14,51 @@
#define AMOUNT_TO_TRANSFER_ZARCANUM_BASIC (TESTS_DEFAULT_FEE*10)
using namespace currency;
//------------------------------------------------------------------------------
// helpers
void invalidate_CLSAG_GGXG_sig(crypto::CLSAG_GGXG_signature& sig)
{
sig.c = 7;
}
void invalidate_bppe_sig(crypto::bppe_signature& sig)
{
sig.delta_1.make_random();
}
void invalidate_pub_key(crypto::public_key& pk)
{
pk.data[5] = 0x33;
}
bool invalidate_zarcanum_sig(size_t n, zarcanum_sig& sig)
{
switch(n)
{
case 0: break;
case 1: invalidate_pub_key(sig.C); break;
case 2: sig.c.make_random(); break;
case 3: invalidate_CLSAG_GGXG_sig(sig.clsag_ggxg); break;
case 4: invalidate_pub_key(sig.C_prime); break;
case 5: sig.d.make_random(); break;
case 6: invalidate_pub_key(sig.E); break;
case 7: invalidate_bppe_sig(sig.E_range_proof); break;
case 8: invalidate_pub_key(sig.pseudo_out_amount_commitment); break;
case 9: sig.y0.make_random(); break;
case 10: sig.y1.make_random(); break;
case 11: sig.y2.make_random(); break;
case 12: sig.y3.make_random(); break;
case 13: sig.y4.make_random(); break;
default: return false;
}
return true;
}
//------------------------------------------------------------------------------
zarcanum_basic_test::zarcanum_basic_test()
{
@ -339,6 +380,48 @@ zarcanum_pos_block_math::zarcanum_pos_block_math()
m_hardforks.set_hardfork_height(ZANO_HARDFORK_04_ZARCANUM, 0);
}
bool make_next_pos_block(test_generator& generator, std::vector<test_event_entry>& events, const block& prev_block, const account_base& stake_acc,
uint64_t amount_to_find, size_t nmix, block& result)
{
bool r = false;
std::vector<tx_source_entry> sources;
size_t height = get_block_height(prev_block) + 1;
crypto::hash prev_id = get_block_hash(prev_block);
r = fill_tx_sources(sources, events, prev_block, stake_acc.get_keys(), amount_to_find, nmix, true, true, false);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
CHECK_AND_ASSERT_MES(shuffle_source_entries(sources), false, "");
auto it = std::max_element(sources.begin(), sources.end(), [&](const tx_source_entry& lhs, const tx_source_entry& rhs){ return lhs.amount < rhs.amount; });
const tx_source_entry& se = *it;
const tx_source_entry::output_entry& oe = se.outputs[se.real_output];
crypto::key_image stake_output_key_image {};
currency::keypair ephemeral_keys {};
r = generate_key_image_helper(stake_acc.get_keys(), se.real_out_tx_key, se.real_output_in_tx_index, ephemeral_keys, stake_output_key_image);
CHECK_AND_ASSERT_MES(r, false, "generate_key_image_helper failed");
uint64_t stake_output_gindex = boost::get<uint64_t>(oe.out_reference);
currency::wide_difficulty_type pos_diff{};
crypto::hash last_pow_block_hash{}, last_pos_block_kernel_hash{};
r = generator.get_params_for_next_pos_block(prev_id, pos_diff, last_pow_block_hash, last_pos_block_kernel_hash);
CHECK_AND_ASSERT_MES(r, false, "get_params_for_next_pos_block failed");
pos_block_builder pb;
pb.step1_init_header(generator.get_hardforks(), height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3a(pos_diff, last_pow_block_hash, last_pos_block_kernel_hash);
pb.step3b(se.amount, stake_output_key_image, se.real_out_tx_key, se.real_output_in_tx_index, se.real_out_amount_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(se, stake_acc.get_keys());
result = pb.m_block;
return true;
}
bool zarcanum_pos_block_math::generate(std::vector<test_event_entry>& events) const
{
bool r = false;
@ -347,63 +430,44 @@ bool zarcanum_pos_block_math::generate(std::vector<test_event_entry>& events) co
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);
REWIND_BLOCKS_N_WITH_TIME(events, blk_1r, blk_1, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 10);
//generator.get_tx_out_gindex
// try to make a PoS block with locked stake after the hardfork
block blk_1_pos;
// blocks with an invalid zarcanum sig
for(size_t i = 1; ; ++i)
{
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;
block blk_1_pos_bad;
CHECK_AND_ASSERT_MES(make_next_pos_block(generator, events, blk_1r, miner_acc, COIN, 10, blk_1_pos_bad), false, "");
LOG_PRINT_CYAN("i = " << i, LOG_LEVEL_0);
if (!invalidate_zarcanum_sig(i, boost::get<zarcanum_sig>(blk_1_pos_bad.miner_tx.signatures[0])))
break;
generator.add_block_info(blk_1_pos_bad, std::list<transaction>{});
DO_CALLBACK(events, "mark_invalid_block");
ADD_CUSTOM_EVENT(events, blk_1_pos_bad);
}
ADD_CUSTOM_EVENT(events, blk_1_pos);
// make a normal PoS block
std::list<currency::account_base> miner_stake_sources( {miner_acc} );
MAKE_NEXT_POS_BLOCK(events, blk_2, blk_1r, miner_acc, miner_stake_sources);
// ... and a PoW block
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
// make a PoS block and than change its nonce, so its hash also changes
// this block should fail
MAKE_NEXT_POS_BLOCK(events, blk_4_bad, blk_3, miner_acc, miner_stake_sources);
generator.remove_block_info(blk_4_bad);
events.pop_back();
blk_4_bad.nonce = 0xc0ffee; // this will change block's hash
generator.add_block_info(blk_4_bad, std::list<transaction>{});
DO_CALLBACK(events, "mark_invalid_block");
ADD_CUSTOM_EVENT(events, blk_4_bad);
// finally, make a normal block
MAKE_NEXT_POS_BLOCK(events, blk_4, blk_3, miner_acc, miner_stake_sources);
return true;
}
//------------------------------------------------------------------------------
zarcanum_txs_with_big_shuffled_decoy_set_shuffled::zarcanum_txs_with_big_shuffled_decoy_set_shuffled()