1
0
Fork 0
forked from lthn/blockchain

assets surjection proof: work in progress

This commit is contained in:
sowle 2023-03-22 23:28:01 +01:00
parent 1a53806642
commit c1d6d13e7b
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
7 changed files with 186 additions and 28 deletions

View file

@ -20,6 +20,7 @@
#include "crypto/range_proofs.h"
#include "crypto/clsag.h"
#include "crypto/zarcanum.h"
#include "crypto/one_out_of_many_proofs.h"
#include "boost_serialization_maps.h"
#include "serialization/keyvalue_enable_POD_serialize_as_string.h"
//

View file

@ -0,0 +1,48 @@
// Copyright (c) 2023 Zano Project
// Copyright (c) 2023 sowle (val@zano.org, crypto.sowle@gmail.com)
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include "one_out_of_many_proofs.h"
#include "../currency_core/crypto_config.h"
#include "epee/include/misc_log_ex.h"
//DISABLE_GCC_AND_CLANG_WARNING(unused-function)
#if 0
# define DBG_VAL_PRINT(x) std::cout << std::setw(30) << std::left << #x ": " << x << std::endl
# define DBG_PRINT(x) std::cout << x << std::endl
#else
# define DBG_VAL_PRINT(x) (void(0))
# define DBG_PRINT(x) (void(0))
#endif
namespace crypto
{
#define CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(cond, err_code) \
if (!(cond)) { LOG_PRINT_RED("generate_BGE_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 generate_BGE_proof(const std::vector<point_t>& ring, const scalar_t& secret, const size_t secret_index, BGE_proof& result, uint8_t* p_err /* = nullptr */)
{
DBG_PRINT(" - - - generate_BGE_proof - - -");
size_t N = ring.size();
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(N > 0, 0);
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(secret_index < N, 1);
#ifndef NDEBUG
CHECK_AND_FAIL_WITH_ERROR_IF_FALSE(ring[secret_index] == secret * crypto::c_point_X, 2);
#endif
return true;
}
bool verify_BGE_proof(const std::vector<const public_key*>& ring, BGE_proof& result, uint8_t* p_err /* = nullptr */)
{
return false;
}
} // namespace crypto

View file

@ -0,0 +1,39 @@
// Copyright (c) 2023 Zano Project
// Copyright (c) 2023 sowle (val@zano.org, crypto.sowle@gmail.com)
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#pragma once
#include "crypto-sugar.h"
namespace crypto
{
//
// BGE stands for Bootle, Groth, Esgin
//
// This is a proof-of-concept implementation of a log-size one-out-of-many proof based on ideas and approaches by Bootle et al, Groth et al and Esgin et al
//
// https://eprint.iacr.org/2014/764
// https://eprint.iacr.org/2015/643
// https://eprint.iacr.org/2019/1287
//
// Disclaimer: shouldn't be used in production code until the security proofs and the code are peer-reviewed.
//
struct BGE_proof
{
point_t A;
point_t B;
std::vector<point_t> Pk;
scalar_vec_t f;
scalar_t y;
scalar_t z;
};
bool generate_BGE_proof(const std::vector<point_t>& ring, const scalar_t& secret, const size_t secret_index, BGE_proof& result, uint8_t* p_err = nullptr);
bool verify_BGE_proof(const std::vector<const public_key*>& ring, BGE_proof& result, uint8_t* p_err = nullptr);
} // namespace crypto

View file

@ -434,14 +434,12 @@ namespace currency
struct zc_asset_surjection_proof
{
int stub = 0; // TODO: one-out-of-many Groth-Bootle-Esgin proof here, adapted version of membership Groth-Kohlweis proof with optimisations from Bootle et. al. and Esgin et. al.
std::vector<crypto::BGE_proof> bge_proofs; // one per output, non-aggregated version of Groth-Bootle-Esgin yet, need to be upgraded later -- sowle
BEGIN_SERIALIZE_OBJECT()
FIELD(stub)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(stub)
END_BOOST_SERIALIZATION()
};

View file

@ -71,9 +71,59 @@ namespace currency
}*/
//--------------------------------------------------------------------------------
bool generate_asset_surjection_proof(const crypto::hash& context_hash, zc_asset_surjection_proof& result)
bool generate_asset_surjection_proof(const crypto::hash& context_hash, outputs_generation_context& ogc, zc_asset_surjection_proof& result)
{
// TODO: @#@# membership proof here
bool r = false;
size_t outs_count = ogc.blinded_asset_ids.size();
CHECK_AND_ASSERT_MES(outs_count > 0, false, "");
CHECK_AND_ASSERT_MES(outs_count == ogc.asset_id_blinding_masks.size(), false, "");
size_t zc_ins_count = ogc.pseudo_outs_blinded_asset_ids.size();
if (zc_ins_count == 0)
{
// if there's no ZC inputs all the outputs must be native coins with explicit asset ids
for(size_t j = 0; j < ogc.blinded_asset_ids.size(); ++j)
{
CHECK_AND_ASSERT_MES(ogc.blinded_asset_ids[j] == currency::native_coin_asset_id_pt, false, "");
CHECK_AND_ASSERT_MES(ogc.asset_id_blinding_masks[j] == 0, false, "");
}
return true;
}
// okay, have some ZC inputs
CHECK_AND_ASSERT_MES(zc_ins_count == ogc.pseudo_outs_plus_real_out_blinding_masks.size(), false, "");
CHECK_AND_ASSERT_MES(zc_ins_count == ogc.real_zc_ins_asset_ids.size(), false, "");
// ins
//ogc.pseudo_outs_blinded_asset_ids; // T^p_i = T_real + r'_i * X
//ogc.pseudo_outs_plus_real_out_blinding_masks; // r_pi + r'_j
// outs
//ogc.blinded_asset_ids; // T'_j = H_j + s_j * X
//ogc.asset_id_blinding_masks; // s_j
for(size_t j = 0; j < outs_count; ++j)
{
const crypto::public_key H = ogc.asset_ids[j].to_public_key();
const crypto::point_t& T = ogc.blinded_asset_ids[j];
std::vector<crypto::point_t> ring;
ring.reserve(zc_ins_count);
size_t secret_index = SIZE_MAX;
for(size_t i = 0; i < zc_ins_count; ++i)
{
ring.emplace_back(ogc.pseudo_outs_blinded_asset_ids[i] - T);
if (secret_index == SIZE_MAX && ogc.real_zc_ins_asset_ids[i].to_public_key() == H)
secret_index = i;
}
CHECK_AND_ASSERT_MES(secret_index != SIZE_MAX, false, "");
crypto::scalar_t secret = ogc.pseudo_outs_plus_real_out_blinding_masks[j] - ogc.asset_id_blinding_masks[j];
result.bge_proofs.emplace_back(crypto::BGE_proof{});
r = crypto::generate_BGE_proof(ring, secret, secret_index, result.bge_proofs.back());
CHECK_AND_ASSERT_MES(r, false, "");
}
return true;
}
//--------------------------------------------------------------------------------
@ -81,7 +131,7 @@ namespace currency
const std::vector<tx_out_v>& vouts, zc_outs_range_proof& result)
{
size_t outs_count = outs_gen_context.amounts.size();
CHECK_AND_ASSERT_MES(outs_gen_context.check_sizes(outs_count), false, "");
// TODO @#@# reconsider this check CHECK_AND_ASSERT_MES(outs_gen_context.check_sizes(outs_count), false, "");
CHECK_AND_ASSERT_MES(out_index_start + outs_count == vouts.size(), false, "");
// prepare data for aggregation proof
@ -322,6 +372,8 @@ namespace currency
//we always add extra_padding with 2 bytes length to make possible for get_block_template to adjust cumulative size
tx.extra.push_back(extra_padding());
size_t zc_ins_count = 0;
// input #0: txin_gen
txin_gen in;
in.height = height;
@ -335,6 +387,7 @@ namespace currency
// just placeholders, they will be filled in wallet2::prepare_and_sign_pos_block()
tx.vin.emplace_back(std::move(txin_zc_input()));
tx.signatures.emplace_back(std::move(zarcanum_sig()));
++zc_ins_count;
}
else
{
@ -346,7 +399,7 @@ namespace currency
}
// fill outputs
outputs_generation_context outs_gen_context(destinations.size()); // auxiliary data for each output
outputs_generation_context outs_gen_context(zc_ins_count, destinations.size()); // auxiliary data for each output
uint64_t output_index = 0;
for (auto& d : destinations)
{
@ -1711,7 +1764,7 @@ namespace currency
};
//--------------------------------------------------------------------------------
bool generate_ZC_sig(const crypto::hash& tx_hash_for_signature, size_t input_index, const tx_source_entry& se, const input_generation_context_data& in_context,
const account_keys& sender_account_keys, const uint64_t tx_flags, outputs_generation_context& outs_gen_context, transaction& tx, bool last_output)
const account_keys& sender_account_keys, const uint64_t tx_flags, outputs_generation_context& ogc, transaction& tx, bool last_output)
{
bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey;
CHECK_AND_ASSERT_MES(se.is_zc(), false, "sources contains a non-zc input");
@ -1728,6 +1781,7 @@ namespace currency
crypto::point_t asset_id_pt(se.asset_id);
crypto::point_t source_blinded_asset_id = asset_id_pt + se.real_out_asset_id_blinding_mask * crypto::c_point_X; // T_i = H_i + r_i * X
CHECK_AND_ASSERT_MES(crypto::point_t(in_context.outputs[in_context.real_out_index].blinded_asset_id).modify_mul8() == source_blinded_asset_id, false, "real output blinded asset id check failed");
ogc.real_zc_ins_asset_ids.emplace_back(asset_id_pt);
#ifndef NDEBUG
{
@ -1741,21 +1795,23 @@ namespace currency
if ((last_output && (tx_flags & TX_FLAG_SIGNATURE_MODE_SEPARATE) == 0) || se.separately_signed_tx_complete)
{
// either normal tx or the last signature of consolidated tx -- in both cases we need to calculate non-random blinding mask for pseudo output commitment
pseudo_out_amount_blinding_mask = outs_gen_context.amount_blinding_masks_sum - outs_gen_context.pseudo_out_amount_blinding_masks_sum - outs_gen_context.ao_amount_blinding_mask; // A_1 - A^p_0 = (f_1 - f'_1) * G => f'_{i-1} = sum{y_j} - sum{f'_i}
pseudo_out_amount_blinding_mask = ogc.amount_blinding_masks_sum - ogc.pseudo_out_amount_blinding_masks_sum - ogc.ao_amount_blinding_mask; // A_1 - A^p_0 = (f_1 - f'_1) * G => f'_{i-1} = sum{y_j} - sum{f'_i}
}
else
{
pseudo_out_amount_blinding_mask.make_random();
outs_gen_context.pseudo_out_amount_blinding_masks_sum += pseudo_out_amount_blinding_mask;
ogc.pseudo_out_amount_blinding_masks_sum += pseudo_out_amount_blinding_mask;
}
crypto::point_t pseudo_out_blinded_asset_id = source_blinded_asset_id + pseudo_out_asset_id_blinding_mask * crypto::c_point_X; // T^p_i = T_i + r'_i * X
sig.pseudo_out_blinded_asset_id = (crypto::c_scalar_1div8 * pseudo_out_blinded_asset_id).to_public_key();
outs_gen_context.real_in_asset_id_blinding_mask_x_amount_sum += se.real_out_asset_id_blinding_mask * se.amount; // += r_i * a_i
ogc.real_in_asset_id_blinding_mask_x_amount_sum += se.real_out_asset_id_blinding_mask * se.amount; // += r_i * a_i
ogc.pseudo_outs_blinded_asset_ids.emplace_back(pseudo_out_blinded_asset_id);
ogc.pseudo_outs_plus_real_out_blinding_masks.emplace_back(pseudo_out_asset_id_blinding_mask + se.real_out_asset_id_blinding_mask);
crypto::point_t pseudo_out_amount_commitment = se.amount * source_blinded_asset_id + pseudo_out_amount_blinding_mask * crypto::c_point_G; // A^p_i = a_i * T_i + f'_i * G
sig.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
outs_gen_context.pseudo_out_amount_commitments_sum += pseudo_out_amount_commitment;
ogc.pseudo_out_amount_commitments_sum += pseudo_out_amount_commitment;
//LOG_PRINT_CYAN("SBAID " << ": " << asset_id_pt << " + " << se.real_out_asset_id_blinding_mask << " x X", LOG_LEVEL_0);
//LOG_PRINT_CYAN(" == " << source_blinded_asset_id, LOG_LEVEL_0);
@ -1953,7 +2009,7 @@ namespace currency
size_t current_index = 0;
inputs_mapping.resize(sources.size());
size_t input_starter_index = tx.vin.size();
bool has_zc_inputs = false;
size_t zc_inputs_count = 0;
bool all_inputs_are_obviously_native_coins = true;
for (const tx_source_entry& src_entr : sources)
{
@ -2035,7 +2091,7 @@ namespace currency
//potentially this approach might help to support htlc and multisig without making to complicated code
if (src_entr.is_zc())
{
has_zc_inputs = true;
++zc_inputs_count;
txin_zc_input zc_in = AUTO_VAL_INIT(zc_in);
zc_in.k_image = img;
zc_in.key_offsets = std::move(key_offsets);
@ -2075,7 +2131,7 @@ namespace currency
//
std::vector<tx_destination_entry> shuffled_dsts(destinations);
size_t outputs_to_be_constructed = shuffled_dsts.size();
outputs_generation_context outs_gen_context(outputs_to_be_constructed); // auxiliary data for each output
outputs_generation_context outs_gen_context(zc_inputs_count, outputs_to_be_constructed); // auxiliary data for each output
// ASSET oprations handling
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
@ -2244,7 +2300,7 @@ namespace currency
{
// asset surjection proof
currency::zc_asset_surjection_proof asp{};
bool r = generate_asset_surjection_proof(tx_prefix_hash, asp);
bool r = generate_asset_surjection_proof(tx_prefix_hash, outs_gen_context, asp);
CHECK_AND_ASSERT_MES(r, false, "generete_asset_surjection_proof failed");
tx.proofs.emplace_back(std::move(asp));

View file

@ -209,8 +209,9 @@ namespace currency
{
outputs_generation_context() = default;
outputs_generation_context(size_t outs_count)
: asset_ids(outs_count)
outputs_generation_context(size_t zc_ins_count, size_t outs_count)
: /*pseudo_outs_blinded_asset_ids(zc_ins_count)
, */asset_ids(outs_count)
, blinded_asset_ids(outs_count)
, amount_commitments(outs_count)
, asset_id_blinding_masks(outs_count)
@ -218,15 +219,17 @@ namespace currency
, amount_blinding_masks(outs_count)
{}
bool check_sizes(size_t outs_count) const
// TODO @#@# reconsider this check -- sowle
bool check_sizes(size_t zc_ins_count, size_t outs_count) const
{
return
asset_ids.size() == outs_count &&
blinded_asset_ids.size() == outs_count &&
amount_commitments.size() == outs_count &&
asset_id_blinding_masks.size() == outs_count &&
amounts.size() == outs_count &&
amount_blinding_masks.size() == outs_count;
pseudo_outs_blinded_asset_ids.size() == zc_ins_count &&
asset_ids.size() == outs_count &&
blinded_asset_ids.size() == outs_count &&
amount_commitments.size() == outs_count &&
asset_id_blinding_masks.size() == outs_count &&
amounts.size() == outs_count &&
amount_blinding_masks.size() == outs_count;
}
// per output data
@ -237,6 +240,11 @@ namespace currency
crypto::scalar_vec_t amounts; // generate_zc_outs_range_proof
crypto::scalar_vec_t amount_blinding_masks; // generate_zc_outs_range_proof
// per zc input data
std::vector<crypto::point_t> pseudo_outs_blinded_asset_ids;
crypto::scalar_vec_t pseudo_outs_plus_real_out_blinding_masks; // r_pi + r'_j
std::vector<crypto::point_t> real_zc_ins_asset_ids; // H_i
// common data: inputs
crypto::point_t pseudo_out_amount_commitments_sum = crypto::c_point_0; // generate_tx_balance_proof generate_ZC_sig
crypto::scalar_t pseudo_out_amount_blinding_masks_sum = 0; // generate_ZC_sig
@ -253,7 +261,7 @@ namespace currency
crypto::point_t ao_amount_commitment = crypto::c_point_0;
crypto::scalar_t ao_amount_blinding_mask {}; // generate_tx_balance_proof generate_ZC_sig
// consider redesign
// consider redesign, some data may possibly be excluded from kv serialization -- sowle
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(asset_ids);
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blinded_asset_ids);

View file

@ -3997,13 +3997,21 @@ bool wallet2::prepare_and_sign_pos_block(const mining_context& cxt, currency::bl
crypto::hash miner_tx_id = get_transaction_hash(b.miner_tx);
// proofs for miner_tx
// asset surjection proof
currency::zc_asset_surjection_proof asp{};
//r = generate_asset_surjection_proof(miner_tx_id, miner_tx_ogc, asp);
//CHECK_AND_ASSERT_MES(r, false, "generete_asset_surjection_proof failed");
b.miner_tx.proofs.emplace_back(std::move(asp));
// range proofs
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);
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;
// balance proof
uint64_t block_reward = COIN; // TODO @#@# move it somewhere -- sowle
currency::zc_balance_proof balance_proof{};
r = generate_tx_balance_proof(b.miner_tx, miner_tx_id, miner_tx_ogc, block_reward, balance_proof);
WLT_CHECK_AND_ASSERT_MES(r, false, "generate_tx_balance_proof failed");