Merge branch 'zarcanum' into zarcanum_wallet

This commit is contained in:
cryptozoidberg 2022-07-13 16:11:10 +02:00
commit 6b4bfee7d9
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
7 changed files with 203 additions and 66 deletions

View file

@ -18,6 +18,7 @@
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "crypto/range_proofs.h"
#include "crypto/clsag.h"
#include "boost_serialization_maps.h"
//
@ -26,7 +27,7 @@
namespace crypto
{
struct bpp_signature_serialized : public crypto::bpp_signature
struct bpp_signature_serialized : public bpp_signature
{
BEGIN_SERIALIZE_OBJECT()
FIELD(L)
@ -51,7 +52,7 @@ namespace crypto
END_BOOST_SERIALIZATION()
};
struct bppe_signature_serialized : public crypto::bppe_signature
struct bppe_signature_serialized : public bppe_signature
{
BEGIN_SERIALIZE_OBJECT()
FIELD(L)
@ -77,7 +78,23 @@ namespace crypto
BOOST_SERIALIZE(delta_2)
END_BOOST_SERIALIZATION()
};
}
struct CLSAG_GG_signature_serialized : public CLSAG_GG_signature
{
BEGIN_SERIALIZE_OBJECT()
FIELD(c)
FIELD((std::vector<scalar_t>&)(r))
FIELD(K1)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(c)
BOOST_SERIALIZE((std::vector<scalar_t>&)(r))
BOOST_SERIALIZE(K1)
END_BOOST_SERIALIZATION()
};
} // namespace crypto
BLOB_SERIALIZER(crypto::chacha8_iv);
BLOB_SERIALIZER(crypto::hash);

19
src/crypto/clsag.cpp Normal file
View file

@ -0,0 +1,19 @@
// Copyright (c) 2022 Zano Project
// Copyright (c) 2022 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.
//
// This file contains implementation of CLSAG (s.a. https://eprint.iacr.org/2019/654.pdf by Goodel at el)
//
#include "clsag.h"
namespace crypto
{
bool generate_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki,
const scalar_t& secret_x, const scalar_t& secret_f, CLSAG_GG_signature& sig)
{
return false;
}
} // namespace crypto

34
src/crypto/clsag.h Normal file
View file

@ -0,0 +1,34 @@
// Copyright (c) 2022 Zano Project
// Copyright (c) 2022 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.
//
// This file contains implementation of CLSAG (s.a. https://eprint.iacr.org/2019/654.pdf by Goodel at el)
//
#pragma once
#include "crypto-sugar.h"
namespace crypto
{
// GG stands for double layers (ring dimentions) both with respect to group element G
struct CLSAG_GG_signature
{
scalar_t c;
scalar_vec_t r; // size = size of the ring
public_key K1; // auxiliary key image for layer 1
};
struct CLSAG_GG_input_ref_t
{
CLSAG_GG_input_ref_t(const public_key& stealth_address, const public_key& amount_commitment)
: stealth_address(stealth_address), amount_commitment(amount_commitment) {}
const public_key& stealth_address;
const public_key& amount_commitment;
};
bool generate_CLSAG_GG(const hash& m, const std::vector<CLSAG_GG_input_ref_t>& ring, const point_t& pseudo_out_amount_commitment, const key_image& ki,
const scalar_t& secret_x, const scalar_t& secret_f, CLSAG_GG_signature& sig);
} // namespace crypto

View file

@ -413,26 +413,29 @@ namespace currency
{
struct input_proofs_t
{
crypto::public_key real_out_amount_commitment;
crypto::public_key pseudo_out_amount_commitment;
// crypto::public_key real_out_token_masked_generator;
BEGIN_SERIALIZE_OBJECT()
FIELD(real_out_amount_commitment)
FIELD(pseudo_out_amount_commitment)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(real_out_amount_commitment)
BOOST_SERIALIZE(pseudo_out_amount_commitment)
END_BOOST_SERIALIZATION()
};
std::vector<input_proofs_t> input_proofs; // for each input
std::vector<input_proofs_t> input_proofs; // for each input
std::vector<crypto::CLSAG_GG_signature_serialized> clsags_gg;
BEGIN_SERIALIZE_OBJECT()
FIELD(input_proofs)
FIELD(clsags_gg)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(input_proofs)
BOOST_SERIALIZE(clsags_gg)
END_BOOST_SERIALIZATION()
};
//#pragma pack(pop)

View file

@ -1320,23 +1320,87 @@ namespace currency
//std::vector<keypair> participants_derived_keys;
};
//--------------------------------------------------------------------------------
bool generate_zarcanum_signature(const crypto::hash& prefix_hash, const std::vector<const tx_source_entry*>& sources, const txin_zarcanum_inputs& zins, zarcanum_sig& result)
bool generate_zc_sig(const crypto::hash& tx_prefix_hash, const std::vector<const tx_source_entry*>& sources, const account_keys& sender_account_keys,
const std::vector<input_generation_context_data>& in_contexts, const crypto::scalar_t& blinding_masks_sum, const uint64_t tx_flags, transaction& tx)
{
CHECK_AND_ASSERT_MES(tx.vin.back().type() == typeid(txin_zarcanum_inputs), false, "Unexpected input type");
txin_zarcanum_inputs& zarcanum_inputs = boost::get<txin_zarcanum_inputs>(tx.vin.back());
CHECK_AND_ASSERT_MES(zarcanum_inputs.elements.size() == sources.size(), false, "sources size differs from zarcanum_inputs.elements size");
CHECK_AND_ASSERT_MES(zarcanum_inputs.elements.size() == in_contexts.size(), false, "in_contexts size differs from zarcanum_inputs.elements size");
tx.signatures.push_back(zarcanum_sig());
zarcanum_sig& sig = boost::get<zarcanum_sig>(tx.signatures.back());
crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, tx.vin.size() - 1, tx_prefix_hash);
CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "prepare_prefix_hash_for_sign failed");
crypto::scalar_t local_blinding_masks_sum = 0;
size_t ring_size = 0;
for(size_t i = 0; i < sources.size(); ++i)
{
CHECK_AND_ASSERT_MES(sources[i] != nullptr, false, "sources[" << i << "] contains nullptr");
const tx_source_entry& se = *sources[i];
CHECK_AND_ASSERT_MES(se.is_zarcanum(), false, "sources[" << i << "] contains a non-zarcanum input");
zarcanum_input& in = zarcanum_inputs.elements[i];
sig.input_proofs.emplace_back();
zarcanum_sig::input_proofs_t zsip = sig.input_proofs.back();
sig.clsags_gg.emplace_back();
crypto::CLSAG_GG_signature& clsag_gg = sig.clsags_gg.back();
if (ring_size == 0)
ring_size = se.outputs.size();
else
CHECK_AND_ASSERT_MES(ring_size == se.outputs.size(), false, "sources[" << i << "] has ring size " << se.outputs.size() << ", expected: " << ring_size);
#ifndef NDEBUG
{
crypto::point_t source_amount_commitment = crypto::c_scalar_1div8 * se.amount * crypto::c_point_H + crypto::c_scalar_1div8 * se.real_out_amount_blinding_mask * crypto::c_point_G;
CHECK_AND_ASSERT_MES(se.outputs[se.real_output].amount_commitment == source_amount_commitment.to_public_key(), false, "real output amount commitment check failed");
}
#endif
crypto::scalar_t blinding_mask = 0;
if ((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
blinding_mask = blinding_masks_sum + local_blinding_masks_sum;
}
else
{
blinding_mask.make_random();
local_blinding_masks_sum -= blinding_mask; // pseudo out masks are taken into account with negative sign
}
crypto::point_t pseudo_out_amount_commitment = se.amount * crypto::c_point_H + blinding_mask * crypto::c_point_G;
zsip.pseudo_out_amount_commitment = (crypto::c_scalar_1div8 * pseudo_out_amount_commitment).to_public_key();
// = two-layers ring signature data outline =
// (j in [0, ring_size-1])
// layer 0 ring
// se.outputs[j].stealth_address;
// layer 0 secret (with respect to G)
// in_contexts[i].in_ephemeral.sec;
// layer 0 linkability
// in.k_image;
//
// layer 1 ring
// crypto::point_t(se.outputs[j].amount_commitment) - pseudo_out_amount_commitment;
// layer 1 secret (with respect to G)
// se.real_out_amount_blinding_mask - blinding_mask;
std::vector<crypto::CLSAG_GG_input_ref_t> ring;
for(size_t j = 0; j < ring_size; ++j)
ring.emplace_back(se.outputs[j].stealth_address, se.outputs[j].amount_commitment);
bool r = crypto::generate_CLSAG_GG(tx_prefix_hash, ring, pseudo_out_amount_commitment, in.k_image, in_contexts[i].in_ephemeral.sec, se.real_out_amount_blinding_mask - blinding_mask, clsag_gg);
CHECK_AND_ASSERT_MES(r, false, "generate_CLSAG_GG failed for item " << i);
}
return true;
}
//--------------------------------------------------------------------------------
bool generate_zc_sig(const std::vector<const tx_source_entry*>& sources, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys)
{
//TODO: sender_account_keys is not used?
tx.signatures.push_back(zarcanum_sig());
CHECK_AND_ASSERT_THROW_MES(tx.vin.back().type() == typeid(txin_zarcanum_inputs), "Unexpected input type in generate_zc_sig");
crypto::hash tx_hash_for_signature = prepare_prefix_hash_for_sign(tx, tx.vin.size() - 1, tx_prefix_hash);
CHECK_AND_ASSERT_MES(tx_hash_for_signature != null_hash, false, "failed to prepare_prefix_hash_for_sign");
return generate_zarcanum_signature(tx_hash_for_signature, sources, boost::get<txin_zarcanum_inputs>(tx.vin.back()), boost::get<zarcanum_sig>(tx.signatures.back()));
}
//--------------------------------------------------------------------------------
bool generate_NLSAG_sig(const std::vector<const tx_source_entry*>& sources, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash, const account_keys& sender_account_keys, const std::vector<input_generation_context_data>& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s)
bool generate_NLSAG_sig(const std::vector<const tx_source_entry*>& sources, size_t input_starter_index, transaction& tx, const crypto::hash& tx_prefix_hash,
const account_keys& sender_account_keys, const std::vector<input_generation_context_data>& in_contexts, const keypair& txkey, std::stringstream& ss_ring_s)
{
bool watch_only_mode = sender_account_keys.spend_secret_key == null_skey;
size_t input_index = input_starter_index;
@ -1362,8 +1426,8 @@ namespace currency
std::vector<const crypto::public_key*> keys_ptrs;
BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs)
{
keys_ptrs.push_back(&o.second);
ss_ring_s << o.second << ENDL;
keys_ptrs.push_back(&o.stealth_address);
ss_ring_s << o.stealth_address << ENDL;
}
sigs.resize(src_entr.outputs.size());
@ -1538,11 +1602,11 @@ namespace currency
return false;
//check that derivated key is equal with real output key
if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second))
if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].stealth_address))
{
LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:"
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second));
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].stealth_address));
return false;
}
@ -1554,7 +1618,7 @@ namespace currency
//fill outputs array and use relative offsets
BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs)
input_to_key.key_offsets.push_back(out_entry.first);
input_to_key.key_offsets.push_back(out_entry.out_reference);
input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
tx.vin.push_back(input_to_key);
@ -1577,43 +1641,20 @@ namespace currency
return false;
//check that derivated key is equal with real output key
if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second))
if (!(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].stealth_address))
{
LOG_ERROR("derived public key missmatch with output public key! " << ENDL << "derived_key:"
<< string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:"
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second));
<< string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].stealth_address));
return false;
}
//put key image into tx input
txin_v in_v;
txin_to_key* ptokey = nullptr;
if (src_entr.htlc_origin.size())
{
//add txin_htlc
txin_htlc in_htlc = AUTO_VAL_INIT(in_htlc);
in_htlc.hltc_origin = src_entr.htlc_origin;
in_v = in_htlc;
txin_htlc& in_v_ref = boost::get<txin_htlc>(in_v);
ptokey = static_cast<txin_to_key*>(&in_v_ref);
}
else
{
in_v = txin_to_key();
txin_to_key& in_v_ref = boost::get<txin_to_key>(in_v);
ptokey = &in_v_ref;
}
txin_to_key& input_to_key = *ptokey;
//fill key_offsets array with relative offsets
std::vector<txout_ref_v> key_offsets;
for(const tx_source_entry::output_entry& out_entry : src_entr.outputs)
key_offsets.push_back(out_entry.out_reference);
input_to_key.amount = src_entr.amount;
input_to_key.k_image = img;
//fill outputs array and use relative offsets
BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs)
input_to_key.key_offsets.push_back(out_entry.first);
input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets);
key_offsets = absolute_output_offsets_to_relative(key_offsets);
//TODO: Might need some refactoring since this scheme is not the clearest one(did it this way for now to keep less changes to not broke anything)
//potentially this approach might help to support htlc and multisig without making to complicated code
@ -1621,12 +1662,17 @@ namespace currency
{
zarcanum_input zc_in = AUTO_VAL_INIT(zc_in);
zc_in.k_image = img;
zc_in.key_offsets = input_to_key.key_offsets;
zc_in.key_offsets = std::move(key_offsets);
ins_zc.elements.push_back(zc_in);
zc_sources.push_back(&src_entr);
}else
}
else
{
tx.vin.push_back(in_v);
txin_to_key input_to_key = AUTO_VAL_INIT(input_to_key);
input_to_key.amount = src_entr.amount;
input_to_key.k_image = img;
input_to_key.key_offsets = std::move(key_offsets);
tx.vin.push_back(input_to_key);
NLSAG_sources.push_back(&src_entr);
}
}
@ -1648,16 +1694,18 @@ namespace currency
size_t output_index = tx.vout.size(); // in case of append mode we need to start output indexing from the last one + 1
uint64_t range_proof_start_index = output_index;
std::set<uint16_t> deriv_cache;
crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret binging masks for each output. For range proof generation
crypto::scalar_vec_t amounts(destinations.size()); // vector of amounts, converted to scalars. For rnage proof generation
crypto::scalar_vec_t blinding_masks(destinations.size()); // vector of secret blinging masks for each output. For range proof generation
crypto::scalar_vec_t amounts(destinations.size()); // vector of amounts, converted to scalars. For ranage proof generation
crypto::scalar_t blinding_masks_sum = 0;
for(const tx_destination_entry& dst_entr : shuffled_dsts)
{
CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); // <<-- TODO @#@# consider removing this check
bool r = construct_tx_out(dst_entr, txkey.sec, output_index, tx, deriv_cache, sender_account_keys, blinding_masks[output_index], result, tx_outs_attr);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct tx out");
amounts[range_proof_start_index - output_index] = dst_entr.amount;
output_index++;
summary_outs_money += dst_entr.amount;
blinding_masks_sum += blinding_masks[output_index];
output_index++;
}
//check money
@ -1734,7 +1782,8 @@ namespace currency
if (zc_sources.size())
{
generate_zc_sig(zc_sources, tx, tx_prefix_hash, sender_account_keys);
// blinding_masks_sum is supposed to be sum(mask of all tx output) - sum(masks of all pseudo out commitments)
generate_zc_sig(tx_prefix_hash, zc_sources, sender_account_keys, in_contexts, blinding_masks_sum, flags, tx);
}

View file

@ -16,11 +16,25 @@ namespace currency
{
struct tx_source_entry
{
typedef serializable_pair<txout_ref_v, crypto::public_key> output_entry; // txout_ref_v is either global output index or ref_by_id; public_key - is output's stealth address
struct output_entry
{
//output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address)
// : out_reference(out_reference), stealth_address(stealth_address), concealing_point(null_pkey), amount_commitment(null_pkey) {}
//output_entry(const txout_ref_v& out_reference, const crypto::public_key& stealth_address, const crypto::public_key& concealing_point, const crypto::public_key& amount_commitment)
// : out_reference(out_reference), stealth_address(stealth_address), concealing_point(concealing_point), amount_commitment(amount_commitment) {}
std::vector<output_entry> outputs; //index + key
txout_ref_v out_reference; // either globbal output index or ref_by_id
crypto::public_key stealth_address; // a.k.a output's one-time public key
crypto::public_key concealing_point; // only for zarcaum outputs
crypto::public_key amount_commitment; // only for zarcaum outputs
};
//typedef serializable_pair<txout_ref_v, crypto::public_key> output_entry; // txout_ref_v is either global output index or ref_by_id; public_key - is output's stealth address
std::vector<output_entry> outputs;
uint64_t real_output; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key; //real output's transaction's public key
crypto::scalar_t real_out_amount_blinding_mask; //blinding mask of real out's amount committment (only for zarcanum inputs, otherwise must be 0)
size_t real_output_in_tx_index; //index in transaction outputs vector
uint64_t amount; //money
uint64_t transfer_index; //money
@ -31,12 +45,13 @@ namespace currency
std::string htlc_origin; //for htlc, specify origin
bool is_multisig() const { return ms_sigs_count > 0; }
bool is_zarcanum() const { return false; }
bool is_zarcanum() const { return !real_out_amount_blinding_mask.is_zero(); }
BEGIN_SERIALIZE_OBJECT()
FIELD(outputs)
FIELD(real_output)
FIELD(real_out_tx_key)
FIELD(real_out_amount_blinding_mask)
FIELD(real_output_in_tx_index)
FIELD(amount)
FIELD(transfer_index)

View file

@ -255,7 +255,7 @@ inline bool resign_tx(const currency::account_keys& sender_keys, const std::vect
{
std::vector<const crypto::public_key*> keys_ptrs;
for (const currency::tx_source_entry::output_entry& o : se.outputs)
keys_ptrs.push_back(&o.second);
keys_ptrs.push_back(&o.stealth_address);
sigs.resize(se.outputs.size());
generate_ring_signature(tx_hash_for_signature, boost::get<currency::txin_to_key>(tx.vin[i]).k_image, keys_ptrs, in_ephemeral_sec, se.real_output, sigs.data());