forked from lthn/blockchain
zc balance proof implemented, coinbase tx creation and validation refactored
This commit is contained in:
parent
636fb820ea
commit
605ae739b8
6 changed files with 266 additions and 114 deletions
|
|
@ -1307,8 +1307,9 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t
|
|||
|
||||
if (is_hardfork_active(ZANO_HARDFORK_04_ZARCANUM))
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.attachment.size() == 1, false, "coinbase transaction wrong attachments number(expeted 1 - rangeproofs)");
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.attachment[0].type() == typeid(zarcanum_outs_range_proof), false, "coinbase transaction wrong attachmenttype (expeted - zarcanum_outs_range_proof)");
|
||||
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(zarcanum_outs_range_proof), false, "coinbase transaction wrong attachment #0 type (expected: zarcanum_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
|
||||
{
|
||||
|
|
@ -1325,15 +1326,6 @@ bool blockchain_storage::validate_miner_transaction(const block& b,
|
|||
const boost::multiprecision::uint128_t& already_generated_coins) const
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_read_lock);
|
||||
//validate reward
|
||||
uint64_t money_in_use = get_outs_money_amount(b.miner_tx);
|
||||
|
||||
uint64_t pos_income = 0;
|
||||
if (is_pos_block(b))
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key), false, "Wrong miner tx_in");
|
||||
pos_income = boost::get<txin_to_key>(b.miner_tx.vin[1]).amount;
|
||||
}
|
||||
|
||||
std::vector<size_t> last_blocks_sizes;
|
||||
get_last_n_blocks_sizes(last_blocks_sizes, CURRENCY_REWARD_BLOCKS_WINDOW);
|
||||
|
|
@ -1343,20 +1335,10 @@ bool blockchain_storage::validate_miner_transaction(const block& b,
|
|||
LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain");
|
||||
return false;
|
||||
}
|
||||
if (base_reward + pos_income + fee < money_in_use)
|
||||
|
||||
if (!check_tx_balance(b.miner_tx, base_reward + fee))
|
||||
{
|
||||
LOG_ERROR("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + pos_income + fee) << "(" << print_money(base_reward) << "+" << print_money(pos_income) << "+" << print_money(fee)
|
||||
<< ", blocks_size_median = " << blocks_size_median
|
||||
<< ", cumulative_block_size = " << cumulative_block_size
|
||||
<< ", fee = " << fee
|
||||
<< ", already_generated_coins = " << already_generated_coins
|
||||
<< "), tx:");
|
||||
LOG_PRINT_L0(currency::obj_to_json_str(b.miner_tx));
|
||||
return false;
|
||||
}
|
||||
if (base_reward + pos_income + fee != money_in_use)
|
||||
{
|
||||
LOG_ERROR("coinbase transaction doesn't use full amount of block reward: spent: (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + pos_income + fee) << "(" << print_money(base_reward) << "+" << print_money(pos_income) << "+" << print_money(fee)
|
||||
LOG_ERROR("coinbase transaction balance check failed. Block reward is " << print_money_brief(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee)
|
||||
<< ", blocks_size_median = " << blocks_size_median
|
||||
<< ", cumulative_block_size = " << cumulative_block_size
|
||||
<< ", fee = " << fee
|
||||
|
|
@ -1365,6 +1347,7 @@ bool blockchain_storage::validate_miner_transaction(const block& b,
|
|||
LOG_PRINT_L0(currency::obj_to_json_str(b.miner_tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_PRINT_MAGENTA("Mining tx verification ok, blocks_size_median = " << blocks_size_median, LOG_LEVEL_2);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3377,15 +3360,17 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verif
|
|||
bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes)
|
||||
{
|
||||
CRITICAL_REGION_LOCAL(m_read_lock);
|
||||
size_t i = 0;
|
||||
BOOST_FOREACH(const auto& otv, tx.vout)
|
||||
size_t output_index = 0;
|
||||
for(const auto& otv : tx.vout)
|
||||
{
|
||||
VARIANT_SWITCH_BEGIN(otv);
|
||||
VARIANT_CASE_CONST(tx_out_bare, ot)
|
||||
if (ot.target.type() == typeid(txout_to_key) || ot.target.type() == typeid(txout_htlc))
|
||||
{
|
||||
m_db_outputs.push_back_item(ot.amount, global_output_entry::construct(tx_id, i));
|
||||
m_db_outputs.push_back_item(ot.amount, global_output_entry::construct(tx_id, output_index));
|
||||
global_indexes.push_back(m_db_outputs.get_item_size(ot.amount) - 1);
|
||||
|
||||
// TODO: CZ, consider removing this check
|
||||
if (ot.target.type() == typeid(txout_htlc) && !is_hardfork_active(3))
|
||||
{
|
||||
LOG_ERROR("Error: Transaction with txout_htlc before hardfork 3 (before height " << m_core_runtime_config.hard_forks.get_str_height_the_hardfork_active_after(3) << ")");
|
||||
|
|
@ -3394,18 +3379,19 @@ bool blockchain_storage::push_transaction_to_global_outs_index(const transaction
|
|||
}
|
||||
else if (ot.target.type() == typeid(txout_multisig))
|
||||
{
|
||||
|
||||
crypto::hash multisig_out_id = get_multisig_out_id(tx, i);
|
||||
crypto::hash multisig_out_id = get_multisig_out_id(tx, output_index);
|
||||
CHECK_AND_ASSERT_MES(multisig_out_id != null_hash, false, "internal error during handling get_multisig_out_id() with tx id " << tx_id);
|
||||
CHECK_AND_ASSERT_MES(!m_db_multisig_outs.find(multisig_out_id), false, "Internal error: already have multisig_out_id " << multisig_out_id << "in multisig outs index");
|
||||
m_db_multisig_outs.set(multisig_out_id, ms_output_entry::construct(tx_id, i));
|
||||
m_db_multisig_outs.set(multisig_out_id, ms_output_entry::construct(tx_id, output_index));
|
||||
global_indexes.push_back(0); // just stub to make other code easier
|
||||
}
|
||||
VARIANT_CASE_CONST(tx_out_zarcanum, toz)
|
||||
//@#@
|
||||
// TODO: CZ, consider using separate table for hidden amounts
|
||||
m_db_outputs.push_back_item(0, global_output_entry::construct(tx_id, output_index));
|
||||
global_indexes.push_back(m_db_outputs.get_item_size(0) - 1);
|
||||
VARIANT_CASE_THROW_ON_OTHER();
|
||||
VARIANT_SWITCH_END();
|
||||
++i;
|
||||
++output_index;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -5538,7 +5524,6 @@ bool get_tx_from_cache(const crypto::hash& tx_id, transactions_map& tx_cache, tr
|
|||
//------------------------------------------------------------------
|
||||
bool blockchain_storage::collect_rangeproofs_data_from_tx(std::vector<zarcanum_outs_range_proof_commit_ref_t>& agregated_proofs, const transaction& tx /*, std::vector<crypto::point_t&>& tx_outs_commitments*/)
|
||||
{
|
||||
|
||||
if (tx.version <= TRANSACTION_VERSION_PRE_HF4)
|
||||
{
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ namespace currency
|
|||
// Zarcanum-aware CLSAG signature
|
||||
struct ZC_sig
|
||||
{
|
||||
crypto::public_key pseudo_out_amount_commitment;
|
||||
crypto::public_key pseudo_out_amount_commitment; // premultiplied by 1/8
|
||||
crypto::CLSAG_GG_signature_serialized clsags_gg;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
|
|
@ -452,6 +452,20 @@ namespace currency
|
|||
BOOST_SERIALIZE(clsags_gg)
|
||||
END_BOOST_SERIALIZATION()
|
||||
};
|
||||
|
||||
struct zc_balance_proof
|
||||
{
|
||||
crypto::signature s;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
FIELD(s)
|
||||
END_SERIALIZE()
|
||||
|
||||
BEGIN_BOOST_SERIALIZATION()
|
||||
BOOST_SERIALIZE(s)
|
||||
END_BOOST_SERIALIZATION()
|
||||
};
|
||||
|
||||
//#pragma pack(pop)
|
||||
|
||||
typedef boost::variant<txin_gen, txin_to_key, txin_multisig, txin_htlc, txin_zc_input> txin_v;
|
||||
|
|
@ -721,10 +735,10 @@ namespace currency
|
|||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
typedef boost::mpl::vector23<
|
||||
typedef boost::mpl::vector24<
|
||||
tx_service_attachment, tx_comment, tx_payer_old, tx_receiver_old, tx_derivation_hint, std::string, tx_crypto_checksum, etc_tx_time, etc_tx_details_unlock_time, etc_tx_details_expiration_time,
|
||||
etc_tx_details_flags, crypto::public_key, extra_attachment_info, extra_alias_entry_old, extra_user_data, extra_padding, etc_tx_flags16_t, etc_tx_details_unlock_time2,
|
||||
tx_payer, tx_receiver, extra_alias_entry, zarcanum_tx_data_v1, zarcanum_outs_range_proof
|
||||
tx_payer, tx_receiver, extra_alias_entry, zarcanum_tx_data_v1, zarcanum_outs_range_proof, zc_balance_proof
|
||||
> all_payload_types;
|
||||
|
||||
typedef boost::make_variant_over<all_payload_types>::type payload_items_v;
|
||||
|
|
@ -1041,6 +1055,7 @@ SET_VARIANT_TAGS(currency::NLSAG_sig, 42, "NLSAG_sig");
|
|||
SET_VARIANT_TAGS(currency::ZC_sig, 43, "ZC_sig");
|
||||
SET_VARIANT_TAGS(currency::void_sig, 44, "void_sig");
|
||||
SET_VARIANT_TAGS(currency::zarcanum_outs_range_proof, 45, "zarcanum_outs_range_proof");
|
||||
SET_VARIANT_TAGS(currency::zc_balance_proof, 46, "zc_balance_proof");
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#define CURRENCY_MAX_BLOCK_NUMBER 500000000
|
||||
#define CURRENCY_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used!
|
||||
#define CURRENCY_TX_MAX_ALLOWED_OUTS 2000
|
||||
#define CURRENCY_TX_MIN_ALLOWED_OUTS 2 // effective starting HF4 Zarcanum
|
||||
#define CURRENCY_PUBLIC_ADDRESS_BASE58_PREFIX 0xc5 // addresses start with 'Zx'
|
||||
#define CURRENCY_PUBLIC_INTEG_ADDRESS_BASE58_PREFIX 0x3678 // integrated addresses start with 'iZ'
|
||||
#define CURRENCY_PUBLIC_INTEG_ADDRESS_V2_BASE58_PREFIX 0x36f8 // integrated addresses start with 'iZ' (new format)
|
||||
|
|
|
|||
|
|
@ -103,63 +103,56 @@ namespace currency
|
|||
return diff;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool construct_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,
|
||||
const account_public_address &miner_address,
|
||||
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)
|
||||
// for txs with no zc inputs (and thus no zc signatures) but with zc outputs
|
||||
bool generate_tx_balance_proof(transaction &tx, const crypto::scalar_t& outputs_blinding_masks_sum, uint64_t block_reward_for_miner_tx = 0)
|
||||
{
|
||||
uint64_t block_reward = 0;
|
||||
if (!get_block_reward(pos, median_size, current_block_size, already_generated_coins, block_reward, height))
|
||||
{
|
||||
LOG_ERROR("Block is too big");
|
||||
return false;
|
||||
}
|
||||
block_reward += fee;
|
||||
|
||||
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(tx.version > TRANSACTION_VERSION_PRE_HF4, false, "unsupported tx.version: " << tx.version);
|
||||
CHECK_AND_ASSERT_MES(count_type_in_variant_container<ZC_sig>(tx.signatures) == 0, false, "");
|
||||
|
||||
CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero");
|
||||
while (max_outs < out_amounts.size())
|
||||
uint64_t bare_inputs_sum = block_reward_for_miner_tx;
|
||||
// TODO: condider remove the followin cycle
|
||||
for(auto& vin : tx.vin)
|
||||
{
|
||||
out_amounts[out_amounts.size() - 2] += out_amounts.back();
|
||||
out_amounts.resize(out_amounts.size() - 1);
|
||||
VARIANT_SWITCH_BEGIN(vin);
|
||||
VARIANT_CASE(txin_to_key, tk)
|
||||
bare_inputs_sum += tk.amount;
|
||||
VARIANT_CASE(txin_htlc, foo);
|
||||
CHECK_AND_ASSERT_MES(false, false, "unexpected txin_htlc input");
|
||||
VARIANT_CASE(txin_multisig, ms);
|
||||
bare_inputs_sum += ms.amount;
|
||||
VARIANT_CASE(txin_zc_input, foo);
|
||||
CHECK_AND_ASSERT_MES(false, false, "unexpected txin_zc_input input");
|
||||
VARIANT_SWITCH_END();
|
||||
}
|
||||
|
||||
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
for (auto a : out_amounts)
|
||||
crypto::point_t outs_commitments_sum = crypto::c_point_0;
|
||||
for(auto& vout : tx.vout)
|
||||
{
|
||||
tx_destination_entry de = AUTO_VAL_INIT(de);
|
||||
de.addr.push_back(miner_address);
|
||||
de.amount = a;
|
||||
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
|
||||
{
|
||||
//this means that block is creating after hardfork_1 and unlock_time is needed to set for every destination separately
|
||||
de.unlock_time = height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW;
|
||||
}
|
||||
destinations.push_back(de);
|
||||
CHECK_AND_ASSERT_MES(vout.type() == typeid(tx_out_zarcanum), false, "unexpected type in outs: " << vout.type().name());
|
||||
const tx_out_zarcanum& ozc = boost::get<tx_out_zarcanum>(vout);
|
||||
outs_commitments_sum += crypto::point_t(ozc.amount_commitment); // amount_commitment premultiplied by 1/8
|
||||
}
|
||||
outs_commitments_sum.modify_mul8();
|
||||
|
||||
if (pos)
|
||||
{
|
||||
uint64_t stake_lock_time = 0;
|
||||
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
|
||||
stake_lock_time = pe.stake_unlock_time;
|
||||
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address, stake_lock_time));
|
||||
}
|
||||
|
||||
uint64_t fee = 0;
|
||||
CHECK_AND_ASSERT_MES(get_tx_fee(tx, fee), false, "unable to get tx fee");
|
||||
|
||||
return construct_miner_tx(height, median_size, already_generated_coins, current_block_size, fee, destinations, tx, tx_version, extra_nonce, max_outs, pos, pe);
|
||||
// sum(bare inputs' amounts) * H + sum(pseudo outs commitments for ZC inputs) + residual * G = sum(outputs' commitments) + fee * H
|
||||
// <=>
|
||||
// (fee - sum(bare inputs' amounts)) * H - sum(pseudo outs commitments for ZC inputs) + sum(outputs' commitments) = residual * G
|
||||
|
||||
// tx doesn't have any zc inputs --> add Schnorr proof for commitment to zero
|
||||
CHECK_AND_ASSERT_MES(count_type_in_variant_container<zc_balance_proof>(tx.attachment) == 0, false, "");
|
||||
zc_balance_proof balance_proof = AUTO_VAL_INIT(balance_proof);
|
||||
|
||||
crypto::point_t commitment_to_zero = outs_commitments_sum + (crypto::scalar_t(fee) - crypto::scalar_t(bare_inputs_sum)) * crypto::c_point_H;
|
||||
//crypto::scalar_t witness = outputs_blinding_masks_sum;
|
||||
|
||||
// TODO: consider adding more data to message
|
||||
crypto::generate_signature(null_hash, commitment_to_zero.to_public_key(), outputs_blinding_masks_sum.as_secret_key(), balance_proof.s);
|
||||
tx.attachment.push_back(balance_proof);
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool apply_unlock_time(const std::vector<tx_destination_entry>& destinations, transaction& tx)
|
||||
|
|
@ -182,11 +175,12 @@ namespace currency
|
|||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
//---------------------------------------------------------------
|
||||
bool construct_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,
|
||||
const std::vector<tx_destination_entry>& destinations,
|
||||
const account_public_address &miner_address,
|
||||
const account_public_address &stakeholder_address,
|
||||
transaction& tx,
|
||||
uint64_t tx_version,
|
||||
const blobdata& extra_nonce,
|
||||
|
|
@ -194,6 +188,71 @@ namespace currency
|
|||
bool pos,
|
||||
const pos_entry& pe)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(!pos || tx.version <= TRANSACTION_VERSION_PRE_HF4, "PoS miner tx is currently unsupported for HF4 -- sowle");
|
||||
|
||||
uint64_t block_reward = 0;
|
||||
if (!get_block_reward(pos, median_size, current_block_size, already_generated_coins, block_reward, height))
|
||||
{
|
||||
LOG_ERROR("Block is too big");
|
||||
return false;
|
||||
}
|
||||
block_reward += fee;
|
||||
|
||||
//
|
||||
// prepare destinations
|
||||
//
|
||||
// 1. split block_reward into out_amounts
|
||||
std::vector<uint64_t> out_amounts;
|
||||
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
|
||||
{
|
||||
// randomly split into CURRENCY_TX_MIN_ALLOWED_OUTS outputs
|
||||
// TODO: consider refactoring
|
||||
uint64_t amount_remaining = block_reward;
|
||||
for(size_t i = 1; i < CURRENCY_TX_MIN_ALLOWED_OUTS; ++i) // starting from 1 for one less iteration
|
||||
{
|
||||
uint64_t amount = crypto::rand<uint64_t>() % amount_remaining;
|
||||
amount_remaining -= amount;
|
||||
out_amounts.push_back(amount);
|
||||
}
|
||||
out_amounts.push_back(amount_remaining);
|
||||
// std::shuffle(out_amounts.begin(), out_amounts.end(), crypto::uniform_random_bit_generator());
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-hidden outs: split into digits
|
||||
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(1 <= max_outs, false, "max_out must be non-zero");
|
||||
while (max_outs < out_amounts.size())
|
||||
{
|
||||
out_amounts[out_amounts.size() - 2] += out_amounts.back();
|
||||
out_amounts.resize(out_amounts.size() - 1);
|
||||
}
|
||||
}
|
||||
// 2. construct destinations using out_amounts
|
||||
std::vector<tx_destination_entry> destinations;
|
||||
for (auto a : out_amounts)
|
||||
{
|
||||
tx_destination_entry de = AUTO_VAL_INIT(de);
|
||||
de.addr.push_back(miner_address);
|
||||
de.amount = a;
|
||||
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
|
||||
{
|
||||
//this means that block is creating after hardfork_1 and unlock_time is needed to set for every destination separately
|
||||
de.unlock_time = height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW;
|
||||
}
|
||||
destinations.push_back(de);
|
||||
}
|
||||
|
||||
if (pos)
|
||||
{
|
||||
uint64_t stake_lock_time = 0;
|
||||
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
|
||||
stake_lock_time = pe.stake_unlock_time;
|
||||
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address, stake_lock_time));
|
||||
}
|
||||
|
||||
CHECK_AND_ASSERT_MES(destinations.size() <= CURRENCY_TX_MAX_ALLOWED_OUTS || height == 0, false, "Too many outs (" << destinations.size() << ")! Miner tx can't be constructed.");
|
||||
tx.version = tx_version;
|
||||
tx.vin.clear();
|
||||
|
|
@ -259,17 +318,122 @@ namespace currency
|
|||
bool r = generate_zarcanum_outs_range_proof(range_proof_start_index, amounts.size(), amounts, blinding_masks, tx.vout, range_proofs);
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to generate zarcanum_outs_range_proof()");
|
||||
tx.attachment.push_back(range_proofs);
|
||||
|
||||
if (!pos)
|
||||
{
|
||||
r = generate_tx_balance_proof(tx, blinding_masks_sum, block_reward);
|
||||
CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.attachment.size())
|
||||
add_attachments_info_to_extra(tx.extra, tx.attachment);
|
||||
|
||||
if (!have_type_in_variant_container<etc_tx_details_unlock_time2>(tx.extra))
|
||||
{
|
||||
//if stake unlock time was not set, then we can use simple "whole transaction" lock scheme
|
||||
set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
bool check_tx_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx /* = 0 */)
|
||||
{
|
||||
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
|
||||
{
|
||||
size_t zc_inputs_count = 0;
|
||||
uint64_t bare_inputs_sum = additional_inputs_amount_and_fees_for_mining_tx;
|
||||
for(auto& vin : tx.vin)
|
||||
{
|
||||
VARIANT_SWITCH_BEGIN(vin);
|
||||
VARIANT_CASE_CONST(txin_to_key, tk)
|
||||
bare_inputs_sum += tk.amount;
|
||||
VARIANT_CASE_CONST(txin_htlc, htlc);
|
||||
bare_inputs_sum += htlc.amount;
|
||||
VARIANT_CASE_CONST(txin_multisig, ms);
|
||||
bare_inputs_sum += ms.amount;
|
||||
VARIANT_CASE_CONST(txin_zc_input, foo);
|
||||
++zc_inputs_count;
|
||||
VARIANT_SWITCH_END();
|
||||
}
|
||||
|
||||
crypto::point_t outs_commitments_sum = crypto::c_point_0;
|
||||
for(auto& vout : tx.vout)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(vout.type() == typeid(tx_out_zarcanum), false, "unexpected type in outs: " << vout.type().name());
|
||||
const tx_out_zarcanum& ozc = boost::get<tx_out_zarcanum>(vout);
|
||||
outs_commitments_sum += crypto::point_t(ozc.amount_commitment); // amount_commitment premultiplied by 1/8
|
||||
}
|
||||
outs_commitments_sum.modify_mul8();
|
||||
|
||||
uint64_t fee = 0;
|
||||
CHECK_AND_ASSERT_MES(get_tx_fee(tx, fee), false, "unable to get tx fee");
|
||||
|
||||
CHECK_AND_ASSERT_MES(additional_inputs_amount_and_fees_for_mining_tx == 0 || fee == 0, false, "invalid tx: fee = " << print_money_brief(fee) <<
|
||||
", additional inputs + fees = " << print_money_brief(additional_inputs_amount_and_fees_for_mining_tx));
|
||||
|
||||
size_t zc_sigs_count = 0;
|
||||
crypto::point_t sum_of_pseudo_out_amount_commitments = crypto::c_point_0;
|
||||
for(auto& sig_v : tx.signatures)
|
||||
{
|
||||
VARIANT_SWITCH_BEGIN(sig_v);
|
||||
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_SWITCH_END();
|
||||
}
|
||||
sum_of_pseudo_out_amount_commitments.modify_mul8();
|
||||
|
||||
CHECK_AND_ASSERT_MES(zc_inputs_count == zc_sigs_count, false, "zc inputs count (" << zc_inputs_count << ") and zc sigs count (" << zc_sigs_count << ") missmatch");
|
||||
|
||||
if (zc_inputs_count > 0)
|
||||
{
|
||||
// no need for additional Schnorr proof for commitment to zero
|
||||
|
||||
// sum(bare inputs' amounts) * H + sum(pseudo outs commitments for ZC inputs) = sum(outputs' commitments) + fee * H
|
||||
// <=>
|
||||
// (sum(bare inputs' amounts) - fee) * H + sum(pseudo outs commitments for ZC inputs) - sum(outputs' commitments) = 0
|
||||
crypto::point_t Z = (crypto::scalar_t(bare_inputs_sum) - crypto::scalar_t(fee)) * crypto::c_point_H + sum_of_pseudo_out_amount_commitments - outs_commitments_sum;
|
||||
CHECK_AND_ASSERT_MES(Z.is_zero(), false, "balace equation does not hold");
|
||||
}
|
||||
else
|
||||
{
|
||||
// no zc inputs -- there should be Schnorr proof for commitment to zero
|
||||
zc_balance_proof balance_proof = AUTO_VAL_INIT(balance_proof);
|
||||
bool r = get_type_in_variant_container<zc_balance_proof>(tx.attachment, balance_proof);
|
||||
CHECK_AND_ASSERT_MES(r, false, "no zc inputs are present, but at the same time there's no zc_balance_proof in attachment");
|
||||
|
||||
// (fee - sum(bare inputs' amounts)) * H + sum(outputs' commitments) = residual * G
|
||||
crypto::point_t commitment_to_zero = (crypto::scalar_t(fee) - crypto::scalar_t(bare_inputs_sum)) * crypto::c_point_H + outs_commitments_sum;
|
||||
r = crypto::check_signature(null_hash, commitment_to_zero.to_public_key(), balance_proof.s);
|
||||
CHECK_AND_ASSERT_MES(r, false, "zc_balance_proof is invalid");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// old fashioned tx with non-hidden amounts
|
||||
uint64_t bare_outputs_sum = get_outs_money_amount(tx);
|
||||
uint64_t bare_inputs_sum = get_inputs_money_amount(tx);
|
||||
|
||||
if (additional_inputs_amount_and_fees_for_mining_tx == 0)
|
||||
{
|
||||
// normal tx
|
||||
CHECK_AND_ASSERT_MES(bare_inputs_sum >= bare_outputs_sum, false, "tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum)
|
||||
<< ") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
// miner tx
|
||||
CHECK_AND_ASSERT_MES(bare_inputs_sum + additional_inputs_amount_and_fees_for_mining_tx == bare_outputs_sum, false,
|
||||
"tx balance error: sum of inputs (" << print_money_brief(bare_inputs_sum) <<
|
||||
") + additional inputs and fees (" << print_money_brief(additional_inputs_amount_and_fees_for_mining_tx) <<
|
||||
") is less than or equal to sum of outputs(" << print_money_brief(bare_outputs_sum) << ")");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool derive_ephemeral_key_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral)
|
||||
{
|
||||
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
|
||||
|
|
@ -615,7 +779,7 @@ namespace currency
|
|||
//---------------------------------------------------------------
|
||||
bool add_tx_extra_userdata(transaction& tx, const blobdata& extra_nonce)
|
||||
{
|
||||
CHECK_AND_ASSERT_MES(extra_nonce.size() <= 255, false, "extra nonce could be 255 bytes max");
|
||||
CHECK_AND_ASSERT_MES(extra_nonce.size() <= 255, false, "extra nonce size exceeded (255 bytes max)");
|
||||
extra_user_data eud = AUTO_VAL_INIT(eud);
|
||||
eud.buff = extra_nonce;
|
||||
tx.extra.push_back(eud);
|
||||
|
|
@ -2037,8 +2201,6 @@ namespace currency
|
|||
for(const auto& in : tx.vin)
|
||||
{
|
||||
uint64_t this_amount = get_amount_from_variant(in);
|
||||
if (!this_amount)
|
||||
return false;
|
||||
money += this_amount;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -2223,8 +2385,7 @@ namespace currency
|
|||
VARIANT_SWITCH_BEGIN(o);
|
||||
VARIANT_CASE_CONST(tx_out_bare, o)
|
||||
outputs_amount += o.amount;
|
||||
VARIANT_CASE_CONST(tx_out_zarcanum, o)
|
||||
//@#@
|
||||
// ignore outputs with hidden amounts
|
||||
VARIANT_SWITCH_END();
|
||||
}
|
||||
return outputs_amount;
|
||||
|
|
@ -3110,6 +3271,11 @@ namespace currency
|
|||
tv.short_view = "outputs_count = " + std::to_string(rp.outputs_count);
|
||||
return true;
|
||||
}
|
||||
bool operator()(const zc_balance_proof& bp)
|
||||
{
|
||||
tv.type = "zc_balance_proof";
|
||||
return true;
|
||||
}
|
||||
};
|
||||
//------------------------------------------------------------------
|
||||
template<class t_container>
|
||||
|
|
@ -3695,12 +3861,13 @@ namespace currency
|
|||
return true;
|
||||
|
||||
std::vector<crypto::bpp_sig_commit_ref_t> sigs;
|
||||
for(auto el : range_proofs)
|
||||
sigs.reserve(range_proofs.size());
|
||||
for(auto& el : range_proofs)
|
||||
sigs.emplace_back(el.range_proof.bpp, el.amount_commitments);
|
||||
|
||||
uint8_t err = 0;
|
||||
bool r = crypto::bpp_verify<>(sigs, &err);
|
||||
CHECK_AND_ASSERT_MES(r, false, "bpp_verify failed with error " << err);
|
||||
CHECK_AND_ASSERT_MES(r, false, "bpp_verify failed with error " << (int)err);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,6 +223,7 @@ namespace currency
|
|||
};
|
||||
|
||||
bool verify_multiple_zarcanum_outs_range_proofs(const std::vector<zarcanum_outs_range_proof_commit_ref_t>& range_proofs);
|
||||
bool check_tx_balance(const transaction& tx, uint64_t additional_inputs_amount_and_fees_for_mining_tx = 0);
|
||||
//---------------------------------------------------------------
|
||||
bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
|
||||
size_t current_block_size,
|
||||
|
|
@ -235,19 +236,6 @@ namespace currency
|
|||
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
|
||||
bool pos = false,
|
||||
const pos_entry& pe = pos_entry());
|
||||
|
||||
bool construct_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,
|
||||
const std::vector<tx_destination_entry>& destinations,
|
||||
transaction& tx,
|
||||
uint64_t tx_version,
|
||||
const blobdata& extra_nonce = blobdata(),
|
||||
size_t max_outs = CURRENCY_MINER_TX_MAX_OUTS,
|
||||
bool pos = false,
|
||||
const pos_entry& pe = pos_entry());
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -66,13 +66,9 @@ namespace currency
|
|||
return false;
|
||||
}
|
||||
|
||||
uint64_t amount_in = 0;
|
||||
get_inputs_money_amount(tx, amount_in);
|
||||
uint64_t amount_out = get_outs_money_amount(tx);
|
||||
|
||||
if (amount_in < amount_out)
|
||||
if (!check_tx_balance(tx))
|
||||
{
|
||||
LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx));
|
||||
LOG_PRINT_RED_L0("tx balance check failed, tx id= " << get_transaction_hash(tx));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue