diff --git a/src/currency_core/blockchain_storage.cpp b/src/currency_core/blockchain_storage.cpp index 0cbe66a9..7386f044 100644 --- a/src/currency_core/blockchain_storage.cpp +++ b/src/currency_core/blockchain_storage.cpp @@ -5419,6 +5419,31 @@ bool blockchain_storage::validate_pos_block(const block& b, if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM)) { + 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(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(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 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 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); + + r = crypto::verify_CLSAG_GGXG(miner_tx_hash, ring, sig.pseudo_out_amount_commitment, sig.C, stake_input.k_image, sig.clsag_ggxg); + CHECK_AND_ASSERT_MES(r, false, "verify_CLSAG_GGXG failed"); + return false; // not implemented yet, TODO @#@# } else diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index f6c87873..f3d84ca2 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -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; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 323aa746..f7434747 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3724,7 +3724,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 +3743,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 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 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,32 +3755,27 @@ 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 (absolute_output_offsets_to_relative) - secret_index = crypto::rand() % (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); } else @@ -3793,6 +3790,7 @@ 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