1
0
Fork 0
forked from lthn/blockchain

implemented locked coins mining on wallet side

This commit is contained in:
cryptozoidberg 2019-07-24 19:14:35 +02:00
parent 12a6001ddf
commit 78dc9164e7
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
9 changed files with 149 additions and 26 deletions

View file

@ -563,12 +563,14 @@ namespace currency
uint64_t index;
crypto::key_image keyimage;
uint64_t block_timestamp;
uint64_t stake_unlock_time;
//not for serialization
uint64_t wallet_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(index)
KV_SERIALIZE(stake_unlock_time)
KV_SERIALIZE(block_timestamp)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(keyimage)
END_KV_SERIALIZE_MAP()

View file

@ -110,18 +110,39 @@ namespace currency
std::vector<tx_destination_entry> destinations;
for (auto a : out_amounts)
{
tx_destination_entry de;
tx_destination_entry de = AUTO_VAL_INIT(de);
de.addr.push_back(miner_address);
de.amount = a;
destinations.push_back(de);
}
if (pos)
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address));
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address, pe.stake_unlock_time));
return construct_miner_tx(height, median_size, already_generated_coins, current_block_size, fee, destinations, tx, extra_nonce, max_outs, pos, pe);
}
//------------------------------------------------------------------
bool apply_unlock_time(const std::vector<tx_destination_entry>& destinations, transaction& tx)
{
currency::etc_tx_details_unlock_time2 unlock_time2 = AUTO_VAL_INIT(unlock_time2);
unlock_time2.unlock_time_array.resize(destinations.size());
bool found_unlock_time = false;
for (size_t i = 0; i != unlock_time2.unlock_time_array.size(); i++)
{
if (destinations[i].unlock_time)
{
found_unlock_time = true;
unlock_time2.unlock_time_array[i] = destinations[i].unlock_time;
}
}
if (found_unlock_time)
{
tx.extra.push_back(unlock_time2);
}
return true;
}
//------------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
size_t current_block_size,
uint64_t fee,
@ -144,8 +165,7 @@ namespace currency
if (!add_tx_extra_userdata(tx, extra_nonce))
return false;
//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());
txin_gen in;
in.height = height;
@ -172,6 +192,11 @@ namespace currency
no++;
}
//at this moment we do apply_unlock_time only for coin_base transactions
apply_unlock_time(destinations, tx);
//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());
tx.version = CURRENT_TRANSACTION_VERSION;
set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);

View file

@ -31,6 +31,31 @@ namespace currency
return false; // 0 means it never expires
return expiration_time <= expiration_ts_median + TX_EXPIRATION_MEDIAN_SHIFT;
}
//---------------------------------------------------------------
uint64_t get_tx_max_unlock_time(const transaction& tx)
{
// etc_tx_details_expiration_time have priority over etc_tx_details_expiration_time2
uint64_t v = get_tx_x_detail<etc_tx_details_unlock_time>(tx);
if (v)
return v;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(tx.extra, ut2);
if (!ut2.unlock_time_array.size())
return 0;
uint64_t max_unlock_time = 0;
CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() == tx.vout.size(), "unlock_time_array.size=" << ut2.unlock_time_array.size()
<< " is not the same as tx.vout.size =" << tx.vout.size() << " in tx: " << get_transaction_hash(tx));
for (size_t i = 0; i != tx.vout.size(); i++)
{
if (ut2.unlock_time_array[i] > max_unlock_time)
max_unlock_time = ut2.unlock_time_array[i];
}
return max_unlock_time;
}
//---------------------------------------------------------------
uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i)
{

View file

@ -51,17 +51,20 @@ namespace currency
std::list<account_public_address> addr; //destination address, in case of 1 address - txout_to_key, in case of more - txout_multisig
size_t minimum_sigs; // if txout_multisig: minimum signatures that are required to spend this output (minimum_sigs <= addr.size()) IF txout_to_key - not used
uint64_t amount_to_provide; //amount money that provided by initial creator of tx, used with partially created transactions
uint64_t unlock_time;
tx_destination_entry() : amount(0), minimum_sigs(0), amount_to_provide(0) {}
tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0) {}
tx_destination_entry(uint64_t a, const std::list<account_public_address>& addr) : amount(a), addr(addr), minimum_sigs(addr.size()), amount_to_provide(0) {}
tx_destination_entry() : amount(0), minimum_sigs(0), amount_to_provide(0), unlock_time(0){}
tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0), unlock_time(0){}
tx_destination_entry(uint64_t a, const account_public_address& ad, uint64_t unlock_time) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0) {}
tx_destination_entry(uint64_t a, const std::list<account_public_address>& addr) : amount(a), addr(addr), minimum_sigs(addr.size()), amount_to_provide(0), unlock_time(0){}
BEGIN_SERIALIZE_OBJECT()
FIELD(amount)
FIELD(addr)
FIELD(minimum_sigs)
FIELD(amount_to_provide)
END_SERIALIZE()
FIELD(unlock_time)
END_SERIALIZE()
};
template<class extra_type_t>
@ -80,6 +83,7 @@ namespace currency
}
uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i);
uint64_t get_tx_max_unlock_time(const transaction& tx);
bool get_tx_max_min_unlock_time(const transaction& tx, uint64_t& max_unlock_time, uint64_t& min_unlock_time);
inline uint64_t get_tx_flags(const transaction& tx) { return get_tx_x_detail<etc_tx_details_flags>(tx); }
inline uint64_t get_tx_expiration_time(const transaction& tx) {return get_tx_x_detail<etc_tx_details_expiration_time>(tx); }
@ -88,6 +92,7 @@ namespace currency
inline void set_tx_expiration_time(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_expiration_time>(tx, v); }
account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector<tx_destination_entry>& destinations);
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median);
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);

View file

@ -798,6 +798,7 @@ namespace currency
currency::pos_entry pe = AUTO_VAL_INIT(pe);
pe.amount = req.pos_amount;
pe.index = req.pos_index;
pe.stake_unlock_time = req.stake_unlock_time;
//pe.keyimage key image will be set in the wallet
//pe.wallet_index is not included in serialization map, TODO: refactoring here

View file

@ -778,6 +778,7 @@ namespace currency
bool pos_block; //is pos block
uint64_t pos_amount; //
uint64_t pos_index; //
uint64_t stake_unlock_time;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(extra_text)
@ -786,6 +787,7 @@ namespace currency
KV_SERIALIZE(pos_block)
KV_SERIALIZE(pos_amount)
KV_SERIALIZE(pos_index)
KV_SERIALIZE(stake_unlock_time)
END_KV_SERIALIZE_MAP()
};

View file

@ -33,6 +33,35 @@ using namespace currency;
ENABLE_CHANNEL_BY_DEFAULT("wallet")
namespace tools
{
//---------------------------------------------------------------
uint64_t wallet2::get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const tools::money_transfer2_details& td)
{
uint64_t max_unlock_time = 0;
// etc_tx_details_expiration_time have priority over etc_tx_details_expiration_time2
uint64_t major_unlock_time = get_tx_x_detail<etc_tx_details_unlock_time>(tx);
if (major_unlock_time)
return major_unlock_time;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(tx.extra, ut2);
if (!ut2.unlock_time_array.size())
return 0;
CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() == tx.vout.size(), "Internal error: wrong tx transfer details: ut2.unlock_time_array.size()" << ut2.unlock_time_array.size() << " is not equal transaction outputs vector size=" << tx.vout.size());
for (auto ri : td.receive_indices)
{
CHECK_AND_ASSERT_THROW_MES(ri < tx.vout.size(), "Internal error: wrong tx transfer details: reciev index=" << ri << " is greater than transaction outputs vector " << tx.vout.size());
if (tx.vout[ri].target.type() == typeid(currency::txout_to_key))
{
//update unlock_time if needed
if (ut2.unlock_time_array[ri] > max_unlock_time)
max_unlock_time = ut2.unlock_time_array[ri];
}
}
return max_unlock_time;
}
//----------------------------------------------------------------------------------------------------
void wallet2::fill_transfer_details(const currency::transaction& tx, const tools::money_transfer2_details& td, tools::wallet_rpc::wallet_transfer_info_details& res_td) const
{
@ -48,7 +77,9 @@ void wallet2::fill_transfer_details(const currency::transaction& tx, const tools
{
WLT_CHECK_AND_ASSERT_MES(ri < tx.vout.size(), void(), "Internal error: wrong tx transfer details: reciev index=" << ri << " is greater than transaction outputs vector " << tx.vout.size());
if (tx.vout[ri].target.type() == typeid(currency::txout_to_key))
{
res_td.rcv.push_back(tx.vout[ri].amount);
}
}
}
//----------------------------------------------------------------------------------------------------
@ -258,8 +289,14 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
i++;
}
//check for transaction income
/*
collect unlock_time from every output that transfered coins to this account and use maximum of
all values m_payments entry, use this strict policy is required to protect exchanges from being feeded with
useless outputs
*/
uint64_t max_out_unlock_time = 0;
//check for transaction income
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs, derivation);
THROW_IF_TRUE_WALLET_EX(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
@ -355,6 +392,10 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
if (td.m_key_image != currency::null_ki)
m_key_images[td.m_key_image] = transfer_index;
add_transfer_to_transfers_cache(tx.vout[o].amount, transfer_index);
if (max_out_unlock_time < get_tx_unlock_time(tx, o))
max_out_unlock_time = get_tx_unlock_time(tx, o);
WLT_LOG_L0("Received money, transfer #" << transfer_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height);
}
else if (tx.vout[o].target.type() == typeid(txout_multisig))
@ -379,7 +420,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
payment.m_tx_hash = currency::get_transaction_hash(tx);
payment.m_amount = received;
payment.m_block_height = height;
payment.m_unlock_time = currency::get_tx_unlock_time(tx);
payment.m_unlock_time = max_out_unlock_time;
m_payments.emplace(payment_id, payment);
WLT_LOG_L2("Payment found, id (hex): " << epee::string_tools::buff_to_hex_nodelimer(payment_id) << ", tx: " << payment.m_tx_hash << ", amount: " << print_money_brief(payment.m_amount));
}
@ -919,9 +960,9 @@ void wallet2::prepare_wti(wallet_rpc::wallet_transfer_info& wti, uint64_t height
wti.amount = amount;
wti.height = height;
fill_transfer_details(tx, td, wti.td);
wti.unlock_time = get_max_unlock_time_from_receive_indices(tx, td);
wti.timestamp = timestamp;
wti.fee = currency::is_coinbase(tx) ? 0:currency::get_tx_fee(tx);
wti.unlock_time = get_tx_unlock_time(tx);
wti.tx_blob_size = static_cast<uint32_t>(currency::get_object_blobsize(wti.tx));
wti.tx_hash = currency::get_transaction_hash(tx);
wti.is_service = currency::is_service_tx(tx);
@ -2398,15 +2439,16 @@ bool wallet2::get_transfer_address(const std::string& adr_str, currency::account
return m_core_proxy->get_transfer_address(adr_str, addr, payment_id);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr)
bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time)
{
if (!tr.is_spendable())
return false;
//blockchain conditions
if (!is_transfer_unlocked(tr))
if (!is_transfer_unlocked(tr, true, stake_unlock_time))
return false;
//prevent staking of after-last-pow-coins
if (m_blockchain.size() - tr.m_ptx_wallet_info->m_block_height <= m_core_runtime_config.min_coinstake_age)
return false;
@ -2436,13 +2478,15 @@ bool wallet2::get_pos_entries(currency::COMMAND_RPC_SCAN_POS::request& req)
for (size_t i = 0; i != m_transfers.size(); i++)
{
auto& tr = m_transfers[i];
if (!is_transfer_okay_for_pos(tr))
uint64_t stake_unlock_time = 0;
if (!is_transfer_okay_for_pos(tr, stake_unlock_time))
continue;
currency::pos_entry pe = AUTO_VAL_INIT(pe);
pe.amount = tr.amount();
pe.index = tr.m_global_output_index;
pe.keyimage = tr.m_key_image;
pe.wallet_index = i;
pe.stake_unlock_time = stake_unlock_time;
pe.block_timestamp = tr.m_ptx_wallet_info->m_block_timestamp;
req.pos_entries.push_back(pe);
}
@ -2619,6 +2663,7 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request&
tmpl_req.pos_amount = req.pos_entries[rsp.index].amount;
tmpl_req.pos_index = req.pos_entries[rsp.index].index;
tmpl_req.extra_text = m_miner_text_info;
tmpl_req.stake_unlock_time = req.pos_entries[rsp.index].stake_unlock_time;
m_core_proxy->call_COMMAND_RPC_GETBLOCKTEMPLATE(tmpl_req, tmpl_rsp);
WLT_CHECK_AND_ASSERT_MES(tmpl_rsp.status == CORE_RPC_STATUS_OK, false, "Failed to create block template after kernel hash found!");
@ -2701,16 +2746,32 @@ currency::core_runtime_config& wallet2::get_core_runtime_config()
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td) const
{
uint64_t stub = 0;
return is_transfer_unlocked(td, false, stub);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td, bool for_pos_mining, uint64_t& stake_lock_time) const
{
if (td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_BLOCKED)
return false;
if (!currency::is_tx_spendtime_unlocked(get_tx_unlock_time(td.m_ptx_wallet_info->m_tx), m_blockchain.size(), m_core_runtime_config.get_core_time()))
if (td.m_ptx_wallet_info->m_block_height + WALLET_DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size())
return false;
if(td.m_ptx_wallet_info->m_block_height + WALLET_DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size())
return false;
uint64_t unlock_time = get_tx_unlock_time(td.m_ptx_wallet_info->m_tx, td.m_internal_output_index);
if (for_pos_mining && m_blockchain.size() > m_core_runtime_config.hard_fork1_starts_after_height)
{
//allowed of staking locked coins with
stake_lock_time = unlock_time;
}
else
{
if (!currency::is_tx_spendtime_unlocked(unlock_time, m_blockchain.size(), m_core_runtime_config.get_core_time()))
return false;
}
return true;
}
//----------------------------------------------------------------------------------------------------

View file

@ -683,6 +683,7 @@ namespace tools
bool build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request& req, const currency::COMMAND_RPC_SCAN_POS::response& rsp, const currency::account_public_address& miner_address, uint64_t new_block_expected_height = UINT64_MAX);
bool reset_history();
bool is_transfer_unlocked(const transfer_details& td) const;
bool is_transfer_unlocked(const transfer_details& td, bool for_pos_mining, uint64_t& stake_lock_time) const;
void get_mining_history(wallet_rpc::mining_history& hist);
void set_core_runtime_config(const currency::core_runtime_config& pc);
currency::core_runtime_config& get_core_runtime_config();
@ -720,7 +721,7 @@ namespace tools
void finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx);
std::string get_log_prefix() const { return m_log_prefix; }
static uint64_t get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const tools::money_transfer2_details& td);
private:
void add_transfers_to_expiration_list(const std::vector<uint64_t>& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id);
void remove_transfer_from_expiration_list(uint64_t transfer_index);
@ -768,7 +769,7 @@ private:
std::string get_alias_for_address(const std::string& addr);
static bool build_kernel(const currency::pos_entry& pe, const currency::stake_modifier_type& stake_modifier, currency::stake_kernel& kernel, uint64_t& coindays_weight, uint64_t timestamp);
bool is_connected_to_net();
bool is_transfer_okay_for_pos(const transfer_details& tr);
bool is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time);
bool scan_unconfirmed_outdate_tx();
const currency::transaction& get_transaction_by_id(const crypto::hash& tx_hash);
void rise_on_transfer2(const wallet_rpc::wallet_transfer_info& wti);
@ -815,6 +816,7 @@ private:
const std::vector<currency::payload_items_v>& decrypted_items, crypto::hash& ms_id, bc_services::contract_private_details& cpd,
const currency::transaction& proposal_template_tx);
void fill_transfer_details(const currency::transaction& tx, const tools::money_transfer2_details& td, tools::wallet_rpc::wallet_transfer_info_details& res_td) const;
void print_source_entry(const currency::tx_source_entry& src) const;
@ -954,7 +956,7 @@ namespace boost
//do not store this items in the file since it's quite easy to restore it from original tx
if (Archive::is_loading::value)
{
x.unlock_time = currency::get_tx_unlock_time(x.tx);
x.unlock_time = wallet::get_max_unlock_time_from_receive_indices(x.tx, x.td)
x.is_service = currency::is_service_tx(x.tx);
x.is_mixing = currency::is_mixin_tx(x.tx);
x.is_mining = currency::is_coinbase(x.tx);

View file

@ -26,7 +26,7 @@ bool wallet2::validate_escrow_proposal(const wallet_rpc::wallet_transfer_info& w
// I. validate escrow proposal tx
const transaction& escrow_proposal_tx = wti.tx;
uint64_t escrow_proposal_tx_unlock_time = get_tx_unlock_time(escrow_proposal_tx);
uint64_t escrow_proposal_tx_unlock_time = get_tx_max_unlock_time(escrow_proposal_tx);
LOC_CHK(escrow_proposal_tx_unlock_time == 0, "proposal tx unlock time is non-zero: " << escrow_proposal_tx_unlock_time);
uint64_t escrow_proposal_expiration_time = get_tx_expiration_time(escrow_proposal_tx);
@ -66,7 +66,7 @@ bool wallet2::validate_escrow_proposal(const wallet_rpc::wallet_transfer_info& w
uint64_t template_expiration_time = get_tx_expiration_time(prop.tx_template);
LOC_CHK(template_expiration_time != 0, "template has no expiration time");
uint64_t template_unlock_time = get_tx_unlock_time(prop.tx_template);
uint64_t template_unlock_time = get_tx_max_unlock_time(prop.tx_template);
LOC_CHK(template_unlock_time == 0, "template has non-zero unlock time: " << template_unlock_time);
// (3/5) outputs
@ -156,7 +156,7 @@ bool wallet2::validate_escrow_release(const transaction& tx, bool release_type_n
uint64_t expiration_time = get_tx_expiration_time(tx);
LOC_CHK(expiration_time == 0, "tx has non-zero expiration time: " << expiration_time);
uint64_t unlock_time = get_tx_unlock_time(tx);
uint64_t unlock_time = get_tx_max_unlock_time(tx);
LOC_CHK(unlock_time == 0, "tx has non-zero unlock time: " << unlock_time);
tx_service_attachment tsa = AUTO_VAL_INIT(tsa);
@ -285,7 +285,7 @@ bool wallet2::validate_escrow_contract(const wallet_rpc::wallet_transfer_info& w
uint64_t tx_expiration_time = get_tx_expiration_time(wti.tx);
LOC_CHK(tx_expiration_time != 0, "no or zero expiration time specified");
uint64_t tx_unlock_time = get_tx_unlock_time(wti.tx);
uint64_t tx_unlock_time = get_tx_max_unlock_time(wti.tx);
LOC_CHK(tx_unlock_time == 0, "non-zero unlock time: " << tx_unlock_time);
#undef LOC_CHK
@ -341,7 +341,7 @@ bool wallet2::validate_escrow_cancel_release(const currency::transaction& tx, co
uint64_t expiration_time = get_tx_expiration_time(tx);
LOC_CHK(expiration_time != 0, "tx has zero or not specified expiration time");
uint64_t unlock_time = get_tx_unlock_time(tx);
uint64_t unlock_time = get_tx_max_unlock_time(tx);
LOC_CHK(unlock_time == 0, "tx has non-zero unlock time: " << unlock_time);
tx_service_attachment tsa = AUTO_VAL_INIT(tsa);
@ -430,7 +430,7 @@ bool wallet2::validate_escrow_cancel_proposal(const wallet_rpc::wallet_transfer_
uint64_t flags = get_tx_flags(wti.tx);
LOC_CHK(flags == 0, "invalid tx flags: " << flags);
uint64_t unlock_time = get_tx_unlock_time(cancellation_request_tx);
uint64_t unlock_time = get_tx_max_unlock_time(cancellation_request_tx);
LOC_CHK(unlock_time == 0, "invalid unlock time: " << unlock_time);
uint64_t expiration_time = get_tx_expiration_time(cancellation_request_tx);