1
0
Fork 0
forked from lthn/blockchain

wallet: Zarcanum PoS mining itself is implemented in wallet2

This commit is contained in:
sowle 2022-09-12 18:47:38 +02:00
parent 634e777cfd
commit e9c3c7841f
No known key found for this signature in database
GPG key ID: C07A24B2D89D49FC
6 changed files with 121 additions and 77 deletions

View file

@ -1144,6 +1144,14 @@ namespace crypto
return hs_calculator.calc_hash();
}
static scalar_t hs(const char(&str32)[32], const crypto::point_t& p)
{
hs_t hs_calculator(2);
hs_calculator.add_32_chars(str32);
hs_calculator.add_point(p);
return hs_calculator.calc_hash();
}
static point_t hp(const point_t& p)
{
point_t result;

View file

@ -21,3 +21,4 @@
#define CRYPTO_HDS_CLSAG_GGXG_CHALLENGE "ZANO_HDS_CLSAG_GGXG_CHALLENGE__"
#define CRYPTO_HDS_ZARCANUM_LAST_POW_HASH "ZANO_HDS_ZARCANUM_LAST_POW_HASH"
#define CRYPTO_HDS_ZARCANUM_SECRET_Q "ZANO_HDS_ZARCANUM_SECRET_Q_____"

View file

@ -270,27 +270,31 @@ 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());
// input #0: txin_gen
txin_gen in;
in.height = height;
tx.vin.push_back(in);
if (pos)
{
// TODO: add Zarcanum part
txin_to_key posin;
posin.amount = pe.amount;
// TODO: using pe.index is deprecated, get input's global index by pe.tx_id and pe.tx_out_index
posin.key_offsets.push_back(pe.g_index);
posin.k_image = pe.keyimage;
tx.vin.push_back(posin);
//reserve place for ring signature
tx.signatures.resize(1);
tx.signatures[0] = NLSAG_sig();
boost::get<NLSAG_sig>(tx.signatures[0]).s.resize(posin.key_offsets.size());
// input #1: stake (for PoS blocks only)
if (tx.version > TRANSACTION_VERSION_PRE_HF4)
{
// TODO: add Zarcanum part
}
else
{
// old fashioned tx non-hidden amounts PoS scheme
txin_to_key stake_input;
stake_input.amount = pe.amount;
stake_input.key_offsets.push_back(pe.g_index);
stake_input.k_image = pe.keyimage;
tx.vin.push_back(stake_input);
//reserve place for ring signature
NLSAG_sig nlsag;
nlsag.s.resize(stake_input.key_offsets.size());
tx.signatures.push_back(nlsag); // consider using emplace_back and avoid copying
}
}
uint64_t no = 0;
@ -574,10 +578,10 @@ namespace currency
std::string print_stake_kernel_info(const stake_kernel& sk)
{
std::stringstream ss;
ss << "block_timestamp: " << sk.block_timestamp << ENDL
<< "kimage: " << sk.kimage << ENDL
<< "stake_modifier.last_pos_kernel_id: " << sk.stake_modifier.last_pos_kernel_id << ENDL
<< "stake_modifier.last_pow_id: " << sk.stake_modifier.last_pow_id << ENDL;
ss << "block timestamp: " << sk.block_timestamp << ENDL
<< "key image: " << sk.kimage << ENDL
<< "sm.last_pos_kernel_id: " << sk.stake_modifier.last_pos_kernel_id << ENDL
<< "sm.last_pow_id: " << sk.stake_modifier.last_pow_id << ENDL;
return ss.str();
}
@ -2430,7 +2434,7 @@ namespace currency
return true;
}
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)
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& blinding_mask)
{
crypto::scalar_t h = crypto::hash_helper_t::hs(reinterpret_cast<const crypto::public_key&>(derivation), output_index); // h = Hs(8 * r * V, i)
@ -2445,7 +2449,7 @@ namespace currency
crypto::scalar_t amount_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_AMOUNT_MASK, h);
decoded_amount = zo.encrypted_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, h)
blinding_mask = crypto::hash_helper_t::hs(CRYPTO_HDS_OUT_BLINDING_MASK, h); // f = Hs(domain_sep, h)
crypto::point_t A_prime;
A_prime.assign_mul_plus_G(decoded_amount, crypto::c_point_H, blinding_mask); // A' * 8 =? a * H + f * G
@ -2569,11 +2573,12 @@ namespace currency
VARIANT_SWITCH_END();
}
VARIANT_CASE_CONST(tx_out_zarcanum, zo)
//@#@
wallet_out_info woi(output_index, 0);
if (is_out_to_acc(acc, zo, derivation, output_index, woi.amount))
uint64_t amount = 0;
crypto::scalar_t blinding_mask = 0;
if (is_out_to_acc(acc, zo, derivation, output_index, amount, blinding_mask))
{
outs.emplace_back(woi);
outs.emplace_back(output_index, amount, blinding_mask);
money_transfered += amount;
}
VARIANT_SWITCH_END();
output_index++;

View file

@ -202,10 +202,15 @@ namespace currency
: index(index)
, amount(amount)
{}
wallet_out_info(size_t index, uint64_t amount, const crypto::scalar_t& blinding_mask)
: index(index)
, amount(amount)
, blinding_mask(blinding_mask)
{}
size_t index = SIZE_MAX;
uint64_t amount = 0;
//todo: additional input info
crypto::scalar_t blinding_mask = 0;
};

View file

@ -350,6 +350,12 @@ bool out_is_to_htlc(const currency::tx_out_v& out_t)
}
return false;
}
bool out_is_zc(const currency::tx_out_v& out_t)
{
return out_t.type() == typeid(currency::tx_out_zarcanum);
}
const currency::txout_htlc& out_get_htlc(const currency::tx_out_v& out_t)
{
return boost::get<currency::txout_htlc>(boost::get<currency::tx_out_bare>(out_t).target);
@ -380,11 +386,6 @@ uint8_t wallet2::out_get_mixin_attr(const currency::tx_out_v& out_t)
return false;
}
bool out_is_to_zarcanum(const currency::tx_out_v& out_t)
{
return out_t.type() == typeid(currency::tx_out_zarcanum);
}
const crypto::public_key& wallet2::out_get_pub_key(const currency::tx_out_v& out_t, std::list<currency::htlc_info>& htlc_info_list)
{
if (out_t.type() == typeid(tx_out_bare))
@ -541,12 +542,15 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
for (size_t i_in_outs = 0; i_in_outs != outs.size(); i_in_outs++)
{
size_t o = outs[i_in_outs].index;
const wallet_out_info& out = outs[i_in_outs];
size_t o = out.index;
WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(o < tx.vout.size(), "wrong out in transaction: internal index=" << o << ", total_outs=" << tx.vout.size());
{
const currency::tx_out_v& out_v = tx.vout[o];
if (out_is_to_key(out_v) || out_is_to_htlc(out_v) || out_is_to_zarcanum(out_v)) // out.target.type() == typeid(txout_to_key) || out.target.type() == typeid(txout_htlc))
bool out_type_zc = out_is_zc(out_v);
bool out_type_to_key = out_is_to_key(out_v);
if (out_type_zc || out_type_to_key || out_is_to_htlc(out_v))
{
crypto::public_key out_key = out_get_pub_key(out_v, htlc_info_list);
//const currency::txout_to_key& otk = boost::get<currency::txout_to_key>(out.target);
@ -601,7 +605,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
}
}
if (is_auditable() && (out_is_to_key(out_v) || out_is_to_zarcanum(out_v)) &&
if (is_auditable() && (out_type_to_key || out_type_zc) &&
out_get_mixin_attr(out_v) != CURRENCY_TO_KEY_OUT_FORCED_NO_MIX)
{
std::stringstream ss;
@ -643,6 +647,10 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER;
}
}
if (out_type_zc)
td.m_opt_blinding_mask = out.blinding_mask;
size_t transfer_index = m_transfers.size() - 1;
if (out_is_to_htlc(out_v))
{
@ -699,7 +707,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
if (max_out_unlock_time < get_tx_unlock_time(tx, o))
max_out_unlock_time = get_tx_unlock_time(tx, o);
if (out_is_to_key(out_v) || out_is_to_zarcanum(out_v))
if (out_type_to_key || out_type_zc)
{
WLT_LOG_L0("Received money, transfer #" << transfer_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height);
}
@ -3613,9 +3621,6 @@ bool wallet2::prepare_and_sign_pos_block(currency::block& b,
//------------------------------------------------------------------
bool wallet2::fill_mining_context(mining_context& ctx)
{
//bool r = get_pos_entries(ctx.sp.pos_entries); // TODO: Remove this call. Transfers are filtered in scan_pos
//WLT_CHECK_AND_ASSERT_MES(r, false, "Failed to get_pos_entries()");
currency::COMMAND_RPC_GET_POS_MINING_DETAILS::request pos_details_req = AUTO_VAL_INIT(pos_details_req);
currency::COMMAND_RPC_GET_POS_MINING_DETAILS::response pos_details_resp = AUTO_VAL_INIT(pos_details_resp);
ctx.status = API_RETURN_CODE_NOT_FOUND;
@ -3623,13 +3628,15 @@ bool wallet2::fill_mining_context(mining_context& ctx)
if (pos_details_resp.status != API_RETURN_CODE_OK)
return false;
ctx.basic_diff.assign(pos_details_resp.pos_basic_difficulty);
ctx.sm = pos_details_resp.sm;
ctx.sk = AUTO_VAL_INIT(ctx.sk);
ctx.sk.stake_modifier = pos_details_resp.sm;
if (get_core_runtime_config().hard_forks.is_hardfork_active_for_height(4, get_top_block_height() + 1))
if (is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM))
{
// Zarcanum (PoS with hidden amounts)
ctx.zarcanum = true;
ctx.last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, ctx.sm.last_pow_id);
ctx.last_pow_block_id_hashed = crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_LAST_POW_HASH, ctx.sk.stake_modifier.last_pow_id);
ctx.l_div_z_D = crypto::c_scalar_L.as_boost_mp_type<boost::multiprecision::uint256_t>() / (ctx.basic_diff);
}
ctx.last_block_hash = pos_details_resp.last_block_hash;
@ -3655,10 +3662,6 @@ bool wallet2::try_mint_pos(const currency::account_public_address& miner_address
return true;
}
//uint64_t pos_entries_amount = 0;
//for (auto& ent : ctx.sp.pos_entries)
// pos_entries_amount += ent.amount;
std::atomic<bool> stop(false);
scan_pos(ctx, stop, [this](){
size_t blocks_fetched;
@ -3681,61 +3684,76 @@ bool wallet2::try_mint_pos(const currency::account_public_address& miner_address
return true;
}
//------------------------------------------------------------------
void wallet2::do_pos_mining_prepare_entry(mining_context& cxt, size_t transfer_index)
void wallet2::do_pos_mining_prepare_entry(mining_context& context, size_t transfer_index)
{
if (cxt.zarcanum)
{
//uint64_t pos_entry_wallet_index = cxt.sp.pos_entries[pos_entry_index].wallet_index;
//CHECK_AND_ASSERT_MES_NO_RET(pos_entry_wallet_index < m_transfers.size(), "invalid pos_entry_wallet_index = " << pos_entry_wallet_index << ", m_transfers: " << m_transfers.size());
CHECK_AND_ASSERT_MES_NO_RET(transfer_index < m_transfers.size(), "transfer_index is out of bounds: " << transfer_index);
const transfer_details& td = m_transfers[transfer_index];
// pre build kernel
context.sk.kimage = td.m_key_image;
if (context.zarcanum)
{
crypto::point_t R(get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx));
crypto::scalar_t v = m_account.get_keys().view_secret_key;
context.secret_q = v * crypto::hash_helper_t::hs(CRYPTO_HDS_ZARCANUM_SECRET_Q, v * R);
}
}
//------------------------------------------------------------------
bool wallet2::do_pos_mining_iteration(mining_context& cxt, size_t transfer_index, uint64_t ts)
bool wallet2::do_pos_mining_iteration(mining_context& context, size_t transfer_index, uint64_t ts)
{
CHECK_AND_NO_ASSERT_MES(transfer_index < m_transfers.size(), false, "transfer_index is out of bounds: " << transfer_index);
const transfer_details& td = m_transfers[transfer_index];
// build kernel
currency::stake_kernel sk = AUTO_VAL_INIT(sk);
sk.kimage = td.m_key_image;
sk.stake_modifier = cxt.sm;
sk.block_timestamp = ts;
// calculate kernel hash
// update stake kernel and calculate it's hash
context.sk.block_timestamp = ts;
crypto::hash kernel_hash;
{
PROFILE_FUNC("calc_hash");
kernel_hash = crypto::cn_fast_hash(&sk, sizeof(sk));
kernel_hash = crypto::cn_fast_hash(&context.sk, sizeof(context.sk));
}
const uint64_t stake_amount = td.amount();
bool found = false;
if (cxt.zarcanum)
if (context.zarcanum && td.is_zc())
{
// TODO @#@#
PROFILE_FUNC("check_zarcanum");
crypto::scalar_t lhs_s = crypto::scalar_t(kernel_hash) * (*td.m_opt_blinding_mask + context.secret_q + context.last_pow_block_id_hashed); // == h * (f + q + f') mod l
boost::multiprecision::uint256_t lhs = lhs_s.as_boost_mp_type<boost::multiprecision::uint256_t>();
boost::multiprecision::uint256_t rhs = context.l_div_z_D * stake_amount; // == floor( l / (z * D) ) * a
if (lhs < rhs)
{
found = true;
LOG_PRINT_GREEN("Found Zarcanum kernel: amount: " << currency::print_money_brief(stake_amount) << ", gindex: " << td.m_global_output_index << ENDL
<< "difficulty: " << context.basic_diff << ENDL
<< "kernel info: " << ENDL
<< print_stake_kernel_info(context.sk) << ENDL
<< "kernel_hash: " << kernel_hash << ENDL
<< "lhs: " << lhs << ENDL
<< "rhs: " << rhs
, LOG_LEVEL_0);
}
++context.iterations_processed;
}
else
{
// old PoS with non-hidden amounts
currency::wide_difficulty_type final_diff = cxt.basic_diff / stake_amount;
currency::wide_difficulty_type final_diff = context.basic_diff / stake_amount;
{
PROFILE_FUNC("check_hash");
found = currency::check_hash(kernel_hash, final_diff);
++cxt.iterations_processed;
++context.iterations_processed;
}
if (found)
{
//cxt.index = pos_entry_index;
cxt.block_timestamp = ts;
LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(stake_amount) << ENDL
<< "difficulty: " << cxt.basic_diff << ", final_diff: " << final_diff << ENDL
<< "index: " << td.m_global_output_index << ENDL
<< "kernel info: " << ENDL
<< print_stake_kernel_info(sk) << ENDL
<< "kernel_hash(proof): " << kernel_hash,
LOG_PRINT_GREEN("Found kernel: amount: " << currency::print_money_brief(stake_amount)<< ", gindex: " << td.m_global_output_index << ENDL
<< "difficulty: " << context.basic_diff << ", final_diff: " << final_diff << ENDL
<< "kernel info: " << ENDL
<< print_stake_kernel_info(context.sk) << ENDL
<< "kernel_hash(proof): " << kernel_hash,
LOG_LEVEL_0);
}
}
@ -3828,8 +3846,8 @@ bool wallet2::build_minted_block(const mining_context& cxt,
const currency::txout_to_key& txtokey = boost::get<currency::txout_to_key>(target);
keys_ptrs.push_back(&txtokey.key);
// set a real timestamp
b.timestamp = cxt.block_timestamp;
// set the timestamp from stake kernel
b.timestamp = cxt.sk.block_timestamp;
uint64_t current_timestamp = m_core_runtime_config.get_core_time();
set_block_datetime(current_timestamp, b);
WLT_LOG_MAGENTA("Applying actual timestamp: " << current_timestamp, LOG_LEVEL_0);

View file

@ -381,6 +381,7 @@ namespace tools
uint64_t m_spent_height;
uint32_t m_flags;
uint64_t m_amount;
boost::optional<crypto::scalar_t> m_opt_blinding_mask;
// @#@ will throw if type is not tx_out_bare, TODO: change according to new model,
// need to replace all get_tx_out_bare_from_out_v() to proper code
@ -391,6 +392,7 @@ namespace tools
bool is_spent() const { return m_flags & WALLET_TRANSFER_DETAIL_FLAG_SPENT; }
bool is_spendable() const { return (m_flags & (WALLET_TRANSFER_DETAIL_FLAG_SPENT | WALLET_TRANSFER_DETAIL_FLAG_BLOCKED | WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION | WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION)) == 0; }
bool is_reserved_for_escrow() const { return ( (m_flags & WALLET_TRANSFER_DETAIL_FLAG_ESCROW_PROPOSAL_RESERVATION) != 0 ); }
bool is_zc() const { return m_opt_blinding_mask != boost::none; }
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_CUSTOM(m_ptx_wallet_info, const transaction_wallet_info&, tools::wallet2::transform_ptr_to_value, tools::wallet2::transform_value_to_ptr)
@ -398,6 +400,7 @@ namespace tools
KV_SERIALIZE(m_spent_height)
KV_SERIALIZE(m_flags)
KV_SERIALIZE(m_amount)
//KV_SERIALIZE_N(m_opt_blinding_mask, blinding_mask)
KV_SERIALIZE_EPHEMERAL_N(uint64_t, tools::wallet2::transfer_details_base_to_amount, "amount")
KV_SERIALIZE_EPHEMERAL_N(std::string, tools::wallet2::transfer_details_base_to_tx_hash, "tx_id")
END_KV_SERIALIZE_MAP()
@ -453,14 +456,18 @@ namespace tools
uint64_t index; // index in m_transfers
uint64_t stake_unlock_time;
uint64_t block_timestamp;
//uint64_t block_timestamp;
uint64_t height;
uint64_t starter_timestamp;
crypto::hash last_block_hash;
crypto::scalar_t last_pow_block_id_hashed; // Zarcanum notation: f'
crypto::scalar_t last_pow_block_id_hashed; // Zarcanum notation: f'
crypto::scalar_t secret_q; // Zarcanum notation: q
boost::multiprecision::uint256_t l_div_z_D; // Zarcanum notation: floor( l / (z * D) ) (max possible value: 2^252 / (2^64 * 1) ~= 2^188, or 2^252 / (1 * 1) = 2^252)
currency::wide_difficulty_type basic_diff;
currency::stake_modifier_type sm;
currency::stake_kernel sk;
//currency::stake_modifier_type sm;
uint64_t iterations_processed = 0;
uint64_t total_items_checked = 0;