1
0
Fork 0
forked from lthn/blockchain

Zarcanum adaptation for confidential assets (WIP)

This commit is contained in:
sowle 2023-03-20 21:25:08 +01:00
parent 4f1d01fc73
commit b5c1c5477d
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
8 changed files with 81 additions and 42 deletions

View file

@ -63,9 +63,9 @@ namespace crypto
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,
bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector<CLSAG_GGXXG_input_ref_t>& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const crypto::scalar_t& stake_out_asset_id_blinding_mask, 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");
@ -139,7 +139,7 @@ namespace crypto
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_gen<bpp_crypto_trait_Zarcanum>(values, masks, masks2, E_1div8_vec_ptr, result.E_range_proof), 10);
// = four-layers ring signature data outline =
// = five-layers ring signature data outline =
// (j in [0, ring_size-1])
// layer 0 ring
// A[j] ( = ring[j].stealth_address)
@ -153,25 +153,35 @@ namespace crypto
// layer 1 secret (with respect to G)
// stake_blinding_mask - pseudo_out_blinding_mask
//
// additional layers for Zarcanum:
// additional layer for confidential assets:
//
// layer 2 ring
// C - A[j] - Q[j]
// ring[j].blinded_asset_id - pseudo_out_blinded_asset_id
// layer 2 secret (with respect to X)
// x0
// -pseudo_out_asset_id_blinding_mask ( = -r'_i )
//
// additional layers for Zarcanum:
//
// layer 3 ring
// C - A[j] - Q[j]
// layer 3 secret (with respect to X)
// x0
//
// layer 4 ring
// Q[j]
// layer 3 secret (with respect to G)
// layer 4 secret (with respect to G)
// secret_q
point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G;
// such pseudo_out_asset_id_blinding_mask effectively makes pseudo_out_blinded_asset_id == currency::native_coin_asset_id_pt == crypto::point_H
scalar_t pseudo_out_asset_id_blinding_mask = -stake_out_asset_id_blinding_mask; // T^p_i = T_i + (-r_i) * X = H_i
point_t pseudo_out_amount_commitment = a * crypto::c_point_H + pseudo_out_blinding_mask * crypto::c_point_G; // A^p_i = a_i * H_i + f'_i * G
result.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
TRY_ENTRY()
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXG(m, ring, pseudo_out_amount_commitment, C, stake_ki,
secret_x, stake_blinding_mask - pseudo_out_blinding_mask, x0, secret_q, secret_index,
result.clsag_ggxg), 20);
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(generate_CLSAG_GGXXG(m, ring, pseudo_out_amount_commitment, crypto::c_point_H, C, stake_ki,
secret_x, stake_blinding_mask - pseudo_out_blinding_mask, -pseudo_out_asset_id_blinding_mask, x0, secret_q, secret_index,
result.clsag_ggxxg), 20);
CATCH_ENTRY2(false);
return true;
@ -179,12 +189,12 @@ namespace crypto
#undef CHECK_AND_FAIL_WITH_ERROR_IF_FALSE
#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 = " << (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,
bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector<CLSAG_GGXXG_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 */) noexcept
@ -235,8 +245,10 @@ namespace crypto
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(bppe_verify<bpp_crypto_trait_Zarcanum>(range_proofs), 10);
static public_key native_coin_asset_id = (crypto::c_scalar_1div8 * crypto::c_point_H).to_public_key(); // consider making it less ugly -- sowle
// 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);
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(verify_CLSAG_GGXXG(m, ring, sig.pseudo_out_amount_commitment, native_coin_asset_id, sig.C, stake_ki, sig.clsag_ggxxg), 1);
}
CATCH_ENTRY_CUSTOM2({if (p_err) *p_err = 100;}, false)

View file

@ -41,16 +41,16 @@ namespace crypto
bppe_signature E_range_proof;
crypto::public_key pseudo_out_amount_commitment; // premultiplied by 1/8
CLSAG_GGXG_signature clsag_ggxg;
CLSAG_GGXXG_signature clsag_ggxxg;
};
bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXG_input_ref_t>& ring,
bool zarcanum_generate_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXXG_input_ref_t>& ring,
const scalar_t& last_pow_block_id_hashed, const key_image& stake_ki,
const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
const scalar_t& secret_x, const scalar_t& secret_q, uint64_t secret_index, const crypto::scalar_t& stake_out_asset_id_blinding_mask, const scalar_t& pseudo_out_blinding_mask, uint64_t stake_amount, const scalar_t& stake_blinding_mask,
zarcanum_proof& result, uint8_t* p_err = nullptr);
bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXG_input_ref_t>& ring,
bool zarcanum_verify_proof(const hash& m, const hash& kernel_hash, const std::vector<crypto::CLSAG_GGXXG_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) noexcept;

View file

@ -376,6 +376,28 @@ namespace currency
if (ogc_ptr)
*ogc_ptr = outs_gen_context; // TODO @#@# consider refactoring (a lot of copying) -- sowle
if (tx.version > TRANSACTION_VERSION_PRE_HF4 && !pos)
{
// This is for PoW blocks only, because PoS blocks proofs are handled in wallet2::prepare_and_sign_pos_block() due to the necessity of making Zarcanum proofs first
//
// tx hash should be sealed by now
crypto::hash tx_id = get_transaction_hash(tx);
//add range proofs
currency::zc_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs);
bool r = generate_zc_outs_range_proof(tx_id, 0, outs_gen_context, tx.vout, range_proofs);
CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
tx.proofs.emplace_back(std::move(range_proofs));
currency::zc_balance_proof balance_proof{};
r = generate_tx_balance_proof(tx, tx_id, outs_gen_context, block_reward, balance_proof);
CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
tx.proofs.emplace_back(std::move(balance_proof));
}
return true;
}
//------------------------------------------------------------------

View file

@ -25,7 +25,7 @@ namespace currency
}
void pos_mining_context::prepare_entry(uint64_t stake_amount, const crypto::key_image& stake_out_ki, const crypto::public_key& stake_source_tx_pub_key, uint64_t stake_out_in_tx_index,
const crypto::scalar_t& stake_out_blinding_mask, const crypto::secret_key& view_secret)
const crypto::scalar_t& stake_out_amount_blinding_mask, const crypto::secret_key& view_secret)
{
this->stake_amount = stake_amount;
this->sk.kimage = stake_out_ki;
@ -41,7 +41,7 @@ namespace currency
// q = Hs(domain_sep, Hs(8 * v * R, i) ) * 8 * v
this->secret_q = v * 8 * crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h);
this->stake_out_blinding_mask = stake_out_blinding_mask;
this->stake_out_amount_blinding_mask = stake_out_amount_blinding_mask;
}
}
@ -62,7 +62,7 @@ namespace currency
crypto::mp::uint512_t rhs;
{
PROFILE_FUNC("check_zarcanum");
found = crypto::zarcanum_check_main_pos_inequality(this->kernel_hash, this->stake_out_blinding_mask, this->secret_q, this->last_pow_block_id_hashed, this->z_l_div_z_D, this->stake_amount, lhs, rhs);
found = crypto::zarcanum_check_main_pos_inequality(this->kernel_hash, this->stake_out_amount_blinding_mask, this->secret_q, this->last_pow_block_id_hashed, this->z_l_div_z_D, this->stake_amount, lhs, rhs);
}
if (found)
{

View file

@ -9,15 +9,15 @@ namespace currency
struct pos_mining_context
{
// Zarcanum notation:
wide_difficulty_type basic_diff; // D
// Zarcanum notation:
wide_difficulty_type basic_diff; // D
stake_kernel sk;
crypto::scalar_t last_pow_block_id_hashed; // f'
crypto::scalar_t secret_q; // q
boost::multiprecision::uint256_t z_l_div_z_D; // z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252)
crypto::hash kernel_hash; // h
crypto::scalar_t stake_out_blinding_mask; // f
uint64_t stake_amount; // a
crypto::scalar_t last_pow_block_id_hashed; // f'
crypto::scalar_t secret_q; // q
boost::multiprecision::uint256_t z_l_div_z_D; // z * floor( l / (z * D) ) (max possible value (assuming z=2^64) : z * 2^252 / (z * 1) ~= 2^252)
crypto::hash kernel_hash; // h
crypto::scalar_t stake_out_amount_blinding_mask; // f
uint64_t stake_amount; // a
bool zarcanum; // false for pre-HF4 classic PoS with explicit amounts

View file

@ -3899,7 +3899,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
const tx_out_zarcanum& stake_out = boost::get<tx_out_zarcanum>(stake_out_v);
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response decoys_resp = AUTO_VAL_INIT(decoys_resp);
std::vector<crypto::CLSAG_GGXG_input_ref_t> ring;
std::vector<crypto::CLSAG_GGXXG_input_ref_t> ring;
uint64_t secret_index = 0; // index of the real stake output
// get decoys outputs and construct miner tx
@ -3952,7 +3952,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
if (gindex == td.m_global_output_index)
secret_index = i;
++i;
ring.emplace_back(el.stealth_address, el.amount_commitment, el.concealing_point);
ring.emplace_back(el.stealth_address, el.amount_commitment, el.blinded_asset_id, el.concealing_point);
stake_input.key_offsets.push_back(el.global_amount_index);
}
r = absolute_sorted_output_offsets_to_relative_in_place(stake_input.key_offsets);
@ -3961,7 +3961,7 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
else
{
// no decoys, the ring consist of one element -- the real stake output
ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.concealing_point);
ring.emplace_back(stake_out.stealth_address, stake_out.amount_commitment, stake_out.blinded_asset_id, stake_out.concealing_point);
stake_input.key_offsets.push_back(td.m_global_output_index);
}
stake_input.k_image = pe.keyimage;
@ -3969,16 +3969,21 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
#ifndef NDEBUG
{
crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * td.m_amount * crypto::c_point_H + crypto::c_scalar_1div8 * td.m_zc_info_ptr->amount_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");
WLT_CHECK_AND_ASSERT_MES(stake_out.amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed");
WLT_CHECK_AND_ASSERT_MES(ring[secret_index].amount_commitment == stake_out.amount_commitment, false, "ring secret member doesn't match with the stake output");
WLT_CHECK_AND_ASSERT_MES(cxt.stake_amount == td.m_amount, false, "stake_amount missmatch");
//WLT_CHECK_AND_ASSERT_MES(source_amount_commitment == cxt.stake_amount * cxt.stake_out_blinded_asset_id + cxt.stake_out_amount_blinding_mask * crypto::c_point_G, false, "source_amount_commitment missmatch");
}
#endif
crypto::hash hash_for_zarcanum_sig = get_block_hash(b);
WLT_CHECK_AND_ASSERT_MES(miner_tx_ogc.pseudo_out_amount_blinding_masks_sum.is_zero(), false, "pseudo_out_amount_blinding_masks_sum is nonzero"); // it should be zero because there's only one input (stake), and thus one pseudo out
crypto::scalar_t pseudo_out_amount_blinding_mask = miner_tx_ogc.amount_blinding_masks_sum; // sum of outputs' amount blinding masks
uint8_t err = 0;
r = crypto::zarcanum_generate_proof(hash_for_zarcanum_sig, cxt.kernel_hash, ring, cxt.last_pow_block_id_hashed, cxt.sk.kimage,
secret_x, cxt.secret_q, secret_index, -miner_tx_ogc.amount_blinding_masks_sum, cxt.stake_amount, cxt.stake_out_blinding_mask,
secret_x, cxt.secret_q, secret_index, td.m_zc_info_ptr->asset_id_blinding_mask, pseudo_out_amount_blinding_mask, cxt.stake_amount, cxt.stake_out_amount_blinding_mask,
static_cast<crypto::zarcanum_proof&>(sig), &err);
WLT_CHECK_AND_ASSERT_MES(r, false, "zarcanum_generate_proof failed, err: " << (int)err);
@ -3991,14 +3996,14 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
// proofs for miner_tx
currency::zc_outs_range_proof range_proofs = AUTO_VAL_INIT(range_proofs);
r = generate_zc_outs_range_proof(miner_tx_id, 0, miner_tx_ogc, b.miner_tx.vout, range_proofs);
CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
WLT_CHECK_AND_ASSERT_MES(r, false, "Failed to generate zc_outs_range_proof()");
b.miner_tx.proofs.emplace_back(std::move(range_proofs));
uint64_t block_reward = COIN;
currency::zc_balance_proof balance_proof{};
r = generate_tx_balance_proof(b.miner_tx, miner_tx_id, miner_tx_ogc, block_reward, balance_proof);
CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
WLT_CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");
b.miner_tx.proofs.emplace_back(std::move(balance_proof));
return true;

View file

@ -221,18 +221,18 @@ void pos_block_builder::step5_sign(const currency::tx_source_entry& se, const cu
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;
std::vector<crypto::CLSAG_GGXXG_input_ref_t> ring;
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);
ring.emplace_back(el.stealth_address, el.amount_commitment, el.blinded_asset_id, el.concealing_point);
}
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, prepared_real_out_index, -m_miner_tx_ogc.amount_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_blinding_mask,
secret_x, m_context.secret_q, prepared_real_out_index, se.real_out_asset_id_blinding_mask, -m_miner_tx_ogc.amount_blinding_masks_sum, m_context.stake_amount, m_context.stake_out_amount_blinding_mask,
static_cast<crypto::zarcanum_proof&>(sig), &err);
CHECK_AND_ASSERT_THROW_MES(r, "zarcanum_generate_proof failed, err: " << (int)err);
}

View file

@ -19,7 +19,7 @@ using namespace currency;
//------------------------------------------------------------------------------
// helpers
void invalidate_CLSAG_GGXG_sig(crypto::CLSAG_GGXG_signature& sig)
void invalidate_CLSAG_GGXXG_sig(crypto::CLSAG_GGXXG_signature& sig)
{
sig.c = 7;
}
@ -41,7 +41,7 @@ bool invalidate_zarcanum_sig(size_t n, zarcanum_sig& sig)
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 3: invalidate_CLSAG_GGXXG_sig(sig.clsag_ggxxg); 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;