1
0
Fork 0
forked from lthn/blockchain

Merge branch 'zarcanum' into zarcanum_wallet

This commit is contained in:
cryptozoidberg 2022-07-06 13:23:15 +02:00
commit fc0851922f
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
9 changed files with 250 additions and 112 deletions

View file

@ -16,10 +16,6 @@ namespace crypto
#include "crypto/crypto-ops.h"
} // extern "C"
#define CRYPTO_STR_(X) #X
#define CRYPTO_STR(X) CRYPTO_STR_(X)
#define CRYPTO_CHECK_AND_THROW_MES(cond, msg) if (!(cond)) { throw std::runtime_error(msg " @ " __FILE__ ":" CRYPTO_STR(__LINE__)); }
//
// Helpers
//
@ -1084,6 +1080,31 @@ namespace crypto
return hs_calculator.calc_hash();
}
static scalar_t hs(const crypto::public_key& pk, const uint64_t i)
{
hs_t hs_calculator(2);
hs_calculator.add_pub_key(pk);
hs_calculator.add_scalar(scalar_t(i));
return hs_calculator.calc_hash();
}
static scalar_t hs(const crypto::secret_key& sk, const uint64_t i)
{
hs_t hs_calculator(2);
hs_calculator.add_scalar(sk);
hs_calculator.add_scalar(scalar_t(i));
return hs_calculator.calc_hash();
}
static scalar_t hs(const char(&str32)[32], const crypto::public_key& pk, const uint64_t i)
{
hs_t hs_calculator(3);
hs_calculator.add_32_chars(str32);
hs_calculator.add_pub_key(pk);
hs_calculator.add_scalar(scalar_t(i));
return hs_calculator.calc_hash();
}
static point_t hp(const point_t& p)
{
point_t result;

View file

@ -1,4 +1,4 @@
// Copyright (c) 2014-2019 Zano Project
// Copyright (c) 2014-2022 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Copyright (c) 2012-2013 The Cryptonote developers
// Distributed under the MIT/X11 software license, see the accompanying
@ -19,9 +19,9 @@
#include "hash.h"
#if !defined(NDEBUG)
# define crypto_assert(expression) assert(expression)
# define crypto_assert(expression) assert(expression); CRYPTO_CHECK_AND_THROW_MES(expression, #expression)
#else
# define crypto_assert(expression) ((void)0)
# define crypto_assert(expression) CRYPTO_CHECK_AND_THROW_MES(expression, #expression)
#endif
namespace crypto {

View file

@ -17,6 +17,9 @@
#include "hash.h"
#include "warnings.h"
#define CRYPTO_STR_(X) #X
#define CRYPTO_STR(X) CRYPTO_STR_(X)
#define CRYPTO_CHECK_AND_THROW_MES(cond, msg) if (!(cond)) { throw std::runtime_error(msg " @ " __FILE__ ":" CRYPTO_STR(__LINE__)); }
PUSH_GCC_WARNINGS
DISABLE_CLANG_WARNING(unused-private-field)

View file

@ -2493,6 +2493,20 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU
<< out_ptr->out_no << " more than transaction outputs = " << tx_ptr->tx.vout.size() << ", for tx id = " << out_ptr->tx_id);
const transaction& tx = tx_ptr->tx;
CHECK_AND_ASSERT_MES(tx_ptr->m_spent_flags.size() == tx.vout.size(), false, "internal error: spent_flag.size()=" << tx_ptr->m_spent_flags.size() << ", tx.vout.size()=" << tx.vout.size());
//do not use outputs that obviously spent for mixins
if (tx_ptr->m_spent_flags[out_ptr->out_no])
return false;
//check if transaction is unlocked
if (!is_tx_spendtime_unlocked(get_tx_unlock_time(tx, out_ptr->out_no)))
return false;
// do not use burned coins
if (is_out_burned(tx.vout[out_ptr->out_no]))
return false;
VARIANT_SWITCH_BEGIN(tx.vout[out_ptr->out_no]);
VARIANT_CASE_CONST(tx_out_bare, o)
{
@ -2504,19 +2518,7 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "unknown tx out type");
const txout_to_key& otk = boost::get<txout_to_key>(o.target);
CHECK_AND_ASSERT_MES(tx_ptr->m_spent_flags.size() == tx.vout.size(), false, "internal error");
//do not use outputs that obviously spent for mixins
if (tx_ptr->m_spent_flags[out_ptr->out_no])
return false;
// do not use burned coins
if (otk.key == null_pkey)
return false;
//check if transaction is unlocked
if (!is_tx_spendtime_unlocked(get_tx_unlock_time(tx, out_ptr->out_no)))
return false;
// TODO #@#@ remove code duplication, make extracting mix_attr in a more generalized way
//use appropriate mix_attr out
uint8_t mix_attr = otk.mix_attr;
@ -2528,13 +2530,27 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU
else if (mix_attr != CURRENCY_TO_KEY_OUT_RELAXED && mix_attr > mix_count)
return false;//mix_attr set to specific minimum, and mix_count is less then desired count
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oen.global_amount_index = i;
oen.out_key = otk.key;
}
VARIANT_CASE_CONST(tx_out_zarcanum, toz)
//@#@
{
//use appropriate mix_attr out
uint8_t mix_attr = toz.mix_attr;
if (mix_attr == CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
return false; //COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS call means that ring signature will have more than one entry.
else if (use_only_forced_to_mix && mix_attr == CURRENCY_TO_KEY_OUT_RELAXED)
return false; //relaxed not allowed
else if (mix_attr != CURRENCY_TO_KEY_OUT_RELAXED && mix_attr > mix_count)
return false;//mix_attr set to specific minimum, and mix_count is less then desired count
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry());
oen.global_amount_index = i;
oen.out_key = toz.amount_commitment;
// TODO @#@# this is certainly not enough
}
VARIANT_SWITCH_END();
return true;
@ -3044,10 +3060,11 @@ void blockchain_storage::print_blockchain_outs_stats() const
{
VARIANT_SWITCH_BEGIN(p_tx->tx.vout[output_entry.out_no]);
VARIANT_CASE_CONST(tx_out_bare, o)
if (boost::get<txout_to_key>(o.target).mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
if (o.target.type() == typeid(txout_to_key) && boost::get<txout_to_key>(o.target).mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
++stat.mixable;
VARIANT_CASE_CONST(tx_out_zarcanum, toz)
//@#@
if (toz.mix_attr != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
++stat.mixable;
VARIANT_SWITCH_END();
}
return true;

View file

@ -8,3 +8,4 @@
//
#define CRYPTO_HDS_OUT_AMOUNT_MASK "ZANO_HDS_OUT_AMOUNT_MASK_______"
#define CRYPTO_HDS_OUT_BLINDING_MASK "ZANO_HDS_OUT_BLINDING_MASK_____"
#define CRYPTO_HDS_OUT_CONCEALING_POINT "ZANO_HDS_OUT_CONCEALING_POINT__"

View file

@ -391,6 +391,24 @@ namespace currency
END_BOOST_SERIALIZATION()
};
// non-consoditated txs must have one of this objects in the attachments (outputs_count == vout.size())
// consolidated -- one pre consolidated part (sum(outputs_count) == vout.size())
struct zarcanum_outs_range_proof
{
crypto::bpp_signature_serialized bpp;
uint8_t outputs_count; // how many outputs are included in the proof
BEGIN_SERIALIZE_OBJECT()
FIELD(bpp)
FIELD(outputs_count)
END_SERIALIZE()
BEGIN_BOOST_SERIALIZATION()
BOOST_SERIALIZE(bpp)
BOOST_SERIALIZE(outputs_count)
END_BOOST_SERIALIZATION()
};
struct zarcanum_sig
{
struct input_proofs_t
@ -689,10 +707,10 @@ namespace currency
END_SERIALIZE()
};
typedef boost::mpl::vector22<
typedef boost::mpl::vector23<
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
tx_payer, tx_receiver, extra_alias_entry, zarcanum_tx_data_v1, zarcanum_outs_range_proof
> all_payload_types;
typedef boost::make_variant_over<all_payload_types>::type payload_items_v;

View file

@ -614,106 +614,165 @@ namespace currency
//---------------------------------------------------------------
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, finalized_tx& result, uint8_t tx_outs_attr)
{
CHECK_AND_ASSERT_MES(de.addr.size() == 1 || (de.addr.size() > 1 && de.minimum_sigs <= de.addr.size()), false, "Invalid destination entry: amount: " << de.amount << " minimum_sigs: " << de.minimum_sigs << " addr.size(): " << de.addr.size());
std::vector<crypto::public_key> target_keys;
target_keys.reserve(de.addr.size());
for (auto& apa : de.addr)
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
{
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
// create tx_out_zarcanum
CHECK_AND_ASSERT_MES(de.addr.size() == 1, false, "zarcanum multisig not implemented yet");
// TODO @#@# implement multisig support
tx_out_zarcanum out = AUTO_VAL_INIT(out);
const account_public_address& apa = de.addr.front();
if (apa.spend_public_key == null_pkey && apa.view_public_key == null_pkey)
{
//burning money(for example alias reward)
out_eph_public_key = null_pkey;
// burn money
// calculate encrypted_amount and amount_commitment anyway, but using modified derivation
crypto::scalar_t h = crypto::hash_helper_t::hs(crypto::scalar_t(tx_sec_key), output_index); // h = Hs(r, i)
out.stealth_address = null_pkey;
out.concealing_point = null_pkey;
crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h);
out.encrypted_amount = de.amount ^ amount_mask.m_u64[0];
crypto::scalar_t blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
out.amount_commitment = (de.amount * crypto::c_point_H + blinding_mask * crypto::c_point_G).to_public_key();
out.mix_attr = tx_outs_attr; // TODO @#@# @CZ check this
}
else
{
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
bool r = derive_public_key_from_target_address(apa, tx_sec_key, output_index, out_eph_public_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
// normal output
crypto::public_key derivation = (crypto::scalar_t(tx_sec_key) * crypto::point_t(apa.view_public_key)).modify_mul8().to_public_key(); // d = 8 * r * V
crypto::scalar_t h = crypto::hash_helper_t::hs(derivation, output_index);
uint16_t hint = get_derivation_hint(derivation);
out.stealth_address = (h * crypto::c_point_G + crypto::point_t(apa.spend_public_key)).to_public_key();
out.concealing_point = (crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_CONCEALING_POINT, h) * crypto::point_t(apa.view_public_key)).to_public_key(); // Q = Hs(domain_sep, h) * V
crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h);
out.encrypted_amount = de.amount ^ amount_mask.m_u64[0];
crypto::scalar_t blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, d, i)
out.amount_commitment = (de.amount * crypto::c_point_H + blinding_mask * crypto::c_point_G).to_public_key();
if (de.addr.front().is_auditable())
out.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX; // override mix_attr to 1 for auditable target addresses
else
out.mix_attr = tx_outs_attr;
uint16_t hint = get_derivation_hint(reinterpret_cast<crypto::key_derivation&>(derivation));
if (deriv_cache.count(hint) == 0)
{
tx.extra.push_back(make_tx_derivation_hint_from_uint16(hint));
deriv_cache.insert(hint);
}
}
target_keys.push_back(out_eph_public_key);
}
tx_out_bare out;
out.amount = de.amount;
if (de.htlc_options.expiration != 0)
{
const destination_option_htlc_out& htlc_dest = de.htlc_options;
//out htlc
CHECK_AND_ASSERT_MES(target_keys.size() == 1, false, "Unexpected htl keys count = " << target_keys.size() << ", expected ==1");
txout_htlc htlc = AUTO_VAL_INIT(htlc);
htlc.expiration = htlc_dest.expiration;
htlc.flags = 0; //0 - SHA256, 1 - RIPEMD160, by default leave SHA256
//receiver key
htlc.pkey_redeem = *target_keys.begin();
//generate refund key
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
bool r = derive_public_key_from_target_address(self.account_address, tx_sec_key, output_index, out_eph_public_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
htlc.pkey_refund = out_eph_public_key;
//add derivation hint for refund address
uint16_t hint = get_derivation_hint(derivation);
if (deriv_cache.count(hint) == 0)
{
tx.extra.push_back(make_tx_derivation_hint_from_uint16(hint));
deriv_cache.insert(hint);
}
if (htlc_dest.htlc_hash == null_hash)
{
//we use deterministic origin, to make possible access origin on different wallets copies
result.htlc_origin = generate_origin_for_htlc(htlc, self);
//calculate hash
if (!htlc.flags&CURRENCY_TXOUT_HTLC_FLAGS_HASH_TYPE_MASK)
{
htlc.htlc_hash = crypto::sha256_hash(result.htlc_origin.data(), result.htlc_origin.size());
}
else
{
crypto::hash160 h160 = crypto::RIPEMD160_hash(result.htlc_origin.data(), result.htlc_origin.size());
std::memcpy(&htlc.htlc_hash, &h160, sizeof(h160));
}
}
else
{
htlc.htlc_hash = htlc_dest.htlc_hash;
}
out.target = htlc;
}
else if (target_keys.size() == 1)
{
//out to key
txout_to_key tk = AUTO_VAL_INIT(tk);
tk.key = target_keys.back();
if (de.addr.front().is_auditable()) // check only the first address because there's only one in this branch
tk.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX; // override mix_attr to 1 for auditable target addresses
else
tk.mix_attr = tx_outs_attr;
out.target = tk;
tx.vout.push_back(out);
}
else
{
//multisig out
txout_multisig ms = AUTO_VAL_INIT(ms);
ms.keys = std::move(target_keys);
ms.minimum_sigs = de.minimum_sigs;
out.target = ms;
// create tx_out_bare, this section can be removed after HF4
CHECK_AND_ASSERT_MES(de.addr.size() == 1 || (de.addr.size() > 1 && de.minimum_sigs <= de.addr.size()), false, "Invalid destination entry: amount: " << de.amount << " minimum_sigs: " << de.minimum_sigs << " addr.size(): " << de.addr.size());
std::vector<crypto::public_key> target_keys;
target_keys.reserve(de.addr.size());
for (auto& apa : de.addr)
{
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
if (apa.spend_public_key == null_pkey && apa.view_public_key == null_pkey)
{
//burning money(for example alias reward)
out_eph_public_key = null_pkey;
}
else
{
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
bool r = derive_public_key_from_target_address(apa, tx_sec_key, output_index, out_eph_public_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
uint16_t hint = get_derivation_hint(derivation);
if (deriv_cache.count(hint) == 0)
{
tx.extra.push_back(make_tx_derivation_hint_from_uint16(hint));
deriv_cache.insert(hint);
}
}
target_keys.push_back(out_eph_public_key);
}
tx_out_bare out;
out.amount = de.amount;
if (de.htlc_options.expiration != 0)
{
const destination_option_htlc_out& htlc_dest = de.htlc_options;
//out htlc
CHECK_AND_ASSERT_MES(target_keys.size() == 1, false, "Unexpected htl keys count = " << target_keys.size() << ", expected ==1");
txout_htlc htlc = AUTO_VAL_INIT(htlc);
htlc.expiration = htlc_dest.expiration;
htlc.flags = 0; //0 - SHA256, 1 - RIPEMD160, by default leave SHA256
//receiver key
htlc.pkey_redeem = *target_keys.begin();
//generate refund key
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key);
bool r = derive_public_key_from_target_address(self.account_address, tx_sec_key, output_index, out_eph_public_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "failed to derive_public_key_from_target_address");
htlc.pkey_refund = out_eph_public_key;
//add derivation hint for refund address
uint16_t hint = get_derivation_hint(derivation);
if (deriv_cache.count(hint) == 0)
{
tx.extra.push_back(make_tx_derivation_hint_from_uint16(hint));
deriv_cache.insert(hint);
}
if (htlc_dest.htlc_hash == null_hash)
{
//we use deterministic origin, to make possible access origin on different wallets copies
result.htlc_origin = generate_origin_for_htlc(htlc, self);
//calculate hash
if (!htlc.flags&CURRENCY_TXOUT_HTLC_FLAGS_HASH_TYPE_MASK)
{
htlc.htlc_hash = crypto::sha256_hash(result.htlc_origin.data(), result.htlc_origin.size());
}
else
{
crypto::hash160 h160 = crypto::RIPEMD160_hash(result.htlc_origin.data(), result.htlc_origin.size());
std::memcpy(&htlc.htlc_hash, &h160, sizeof(h160));
}
}
else
{
htlc.htlc_hash = htlc_dest.htlc_hash;
}
out.target = htlc;
}
else if (target_keys.size() == 1)
{
//out to key
txout_to_key tk = AUTO_VAL_INIT(tk);
tk.key = target_keys.back();
if (de.addr.front().is_auditable()) // check only the first address because there's only one in this branch
tk.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX; // override mix_attr to 1 for auditable target addresses
else
tk.mix_attr = tx_outs_attr;
out.target = tk;
}
else
{
//multisig out
txout_multisig ms = AUTO_VAL_INIT(ms);
ms.keys = std::move(target_keys);
ms.minimum_sigs = de.minimum_sigs;
out.target = ms;
}
tx.vout.push_back(out);
}
tx.vout.push_back(out);
return true;
}
//---------------------------------------------------------------
@ -1965,13 +2024,13 @@ namespace currency
bool is_out_to_acc(const account_keys& acc, const tx_out_zarcanum& zo, const crypto::key_derivation& derivation, size_t output_index, uint64_t& decoded_amount)
{
crypto::scalar_t h = {};
crypto::derivation_to_scalar(derivation, output_index, h.as_secret_key()); // h = Hs(r * V, i)
crypto::derivation_to_scalar(derivation, output_index, h.as_secret_key()); // h = Hs(8 * r * V, i)
crypto::point_t P_prime = h * crypto::c_point_G + crypto::point_t(acc.account_address.spend_public_key); // P =? Hs(rV, i) * G + S
crypto::point_t P_prime = h * crypto::c_point_G + crypto::point_t(acc.account_address.spend_public_key); // P =? Hs(8rV, i) * G + S
if (P_prime.to_public_key() != zo.stealth_address)
return false;
crypto::point_t Q_prime = h * crypto::point_t(acc.account_address.view_public_key); // Q =? v * Hs(rv, i) * G
crypto::point_t Q_prime = h * crypto::point_t(acc.account_address.view_public_key); // Q =? v * Hs(8rV, i) * G
if (Q_prime.to_public_key() != zo.concealing_point)
return false;

View file

@ -405,6 +405,25 @@ namespace currency
return stub;
}
template<typename out_t>
bool is_out_burned(const out_t& out) { CHECK_AND_ASSERT_THROW_MES(false, "incorrect out type: " << typeid(out).name()); }
bool is_out_burned(const tx_out_bare& o) { return is_out_burned(o.target); }
bool is_out_burned(const txout_to_key& o) { return o.key == null_pkey; }
bool is_out_burned(const tx_out_zarcanum& o) { return o.stealth_address == null_pkey; }
struct zz_is_out_burned_helper_visitor : boost::static_visitor<bool>
{
template<typename T>
bool operator()(const T& v) const { return is_out_burned(v); }
};
bool is_out_burned(const tx_out_v& v)
{
return boost::apply_visitor(zz_is_out_burned_helper_visitor(), v);
}
bool is_out_burned(const txout_target_v& v)
{
return boost::apply_visitor(zz_is_out_burned_helper_visitor(), v);
}
template<class t_extra_container>
bool add_attachments_info_to_extra(t_extra_container& extra_container, const std::vector<attachment_v>& attachments)
{

View file

@ -16,7 +16,7 @@ namespace currency
{
struct tx_source_entry
{
typedef serializable_pair<txout_ref_v, crypto::public_key> output_entry; // txout_v is either global output index or ref_by_id; public_key - is output ephemeral pub key
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; //index + key
uint64_t real_output; //index in outputs vector of real output_entry