Added functions to cache and manage yield calculations
Removed "tx.amount_locked" field - "tx.amount_burnt" is technically correct for all cases. Removed invalid checkpoint data.
This commit is contained in:
@@ -466,6 +466,18 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
|
||||
rx_set_main_seedhash(seedhash.data, tools::get_max_concurrency());
|
||||
}
|
||||
|
||||
// Preload the yield_block_info cache
|
||||
uint64_t yield_lock_period = get_config(m_nettype).YIELD_LOCK_PERIOD;
|
||||
m_yield_block_info_cache.clear();
|
||||
uint64_t end_height = m_db->height();
|
||||
uint64_t start_height = (end_height > yield_lock_period) ? (end_height - yield_lock_period) : 0;
|
||||
for (uint64_t idx = start_height; idx < end_height; idx++) {
|
||||
yield_block_info ybi;
|
||||
int result = m_db->get_yield_block_info(idx, ybi);
|
||||
if (result)
|
||||
return false;
|
||||
m_yield_block_info_cache[idx] = ybi;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
@@ -1846,6 +1858,35 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||
protocol_entries.push_back(entry);
|
||||
}
|
||||
|
||||
// Get the YIELD TX information for matured staked coins
|
||||
uint64_t yield_lock_period = get_config(m_nettype).YIELD_LOCK_PERIOD;
|
||||
uint64_t start_height = height - yield_lock_period - 1;
|
||||
std::vector<cryptonote::yield_tx_info> yield_entries;
|
||||
int yield_tx_result = m_db->get_yield_tx_info(start_height, yield_entries);
|
||||
if (yield_entries.size()) {
|
||||
|
||||
// Get the YBI information for the 21,600 blocks that the matured TX(s), we can calculate yield
|
||||
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
|
||||
for (const auto& entry: yield_entries) {
|
||||
yield_payouts.emplace_back(std::make_pair(entry, 0));
|
||||
}
|
||||
|
||||
// Make sure the cache is fully populated and up to date
|
||||
if (!validate_ybi_cache()) {
|
||||
LOG_PRINT_L1("yield information cache is invalid - rebuilding cache");
|
||||
if (!rebuild_ybi_cache()) {
|
||||
LOG_ERROR("Failed to rebuild yield information cache - aborting");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over the cached data for block yield, calculating the yield payouts due
|
||||
if (!calculate_yield_payouts(start_height, yield_payouts)) {
|
||||
LOG_ERROR("Failed to obtain yield payout information - aborting");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Time to construct the protocol_tx
|
||||
uint64_t protocol_fee = 0;
|
||||
bool ok = construct_protocol_tx(height, protocol_fee, b.protocol_tx, protocol_entries, circ_supply, pr, b.major_version);
|
||||
@@ -4226,6 +4267,104 @@ uint64_t Blockchain::get_adjusted_time(uint64_t height) const
|
||||
// we do this since it's better to report a time in the past than a time in the future
|
||||
return (adjusted_current_block_ts < median_ts ? adjusted_current_block_ts : median_ts);
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_container)
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
// Iterate over the cached yield_block_info data
|
||||
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).YIELD_LOCK_PERIOD;
|
||||
for (uint64_t idx = start_height; idx < start_height + yield_lock_period; ++idx) {
|
||||
// Get the next block
|
||||
if (m_yield_block_info_cache.count(idx) == 0) {
|
||||
LOG_ERROR("failed to locate yield information for block height " << idx <<" - aborting");
|
||||
return false;
|
||||
}
|
||||
yield_block_info ybi = m_yield_block_info_cache[idx];
|
||||
boost::multiprecision::int128_t slippage_128 = ybi.slippage_total;
|
||||
slippage_128 = (slippage_128 * 3) / 10;
|
||||
|
||||
// Get the total number of coins locked at this height
|
||||
boost::multiprecision::int128_t locked_total_128 = ybi.locked_coins_tally;
|
||||
|
||||
// Iterate over the yield_container, adding each proportion of the yield
|
||||
for (const auto& entry: yield_container) {
|
||||
|
||||
boost::multiprecision::int128_t locked_coins_128 = entry.first.locked_coins;
|
||||
boost::multiprecision::int128_t yield_128 = (slippage_128 * locked_coins_128) / locked_total_128;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return success to caller
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::rebuild_ybi_cache()
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
// If we need to (re)build the cache, we need to pull the data from the blockchain directly
|
||||
|
||||
// Clear the existing cache
|
||||
m_yield_block_info_cache.clear();
|
||||
|
||||
// Get the size that the cache should be when fully populated (could be less than the lock period if the chain is young)
|
||||
uint64_t height = m_db->height();
|
||||
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).YIELD_LOCK_PERIOD;
|
||||
uint64_t ybi_cache_expected_size = std::min(height, yield_lock_period);
|
||||
|
||||
// Now get this number of entries from the blockchain
|
||||
for (uint64_t idx = height - ybi_cache_expected_size; idx < height; ++idx) {
|
||||
|
||||
// Get the specified YBI entry
|
||||
yield_block_info ybi;
|
||||
int result = m_db->get_yield_block_info(idx, ybi);
|
||||
if (result) {
|
||||
// Request failed - report error and bail out
|
||||
LOG_ERROR("failed to retrieve YBI entry for height " << idx << " - aborting");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store in the map
|
||||
m_yield_block_info_cache[idx] = ybi;
|
||||
}
|
||||
|
||||
// Return success to caller
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------
|
||||
bool Blockchain::validate_ybi_cache()
|
||||
{
|
||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||
|
||||
// Get the size that the cache should be if fully populated
|
||||
uint64_t height = m_db->height();
|
||||
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).YIELD_LOCK_PERIOD;
|
||||
uint64_t ybi_cache_expected_size = std::min(height, yield_lock_period);
|
||||
if (m_yield_block_info_cache.size() != ybi_cache_expected_size) {
|
||||
// It's not the right size - report error and bail out
|
||||
LOG_ERROR("YBI cache is incorrect size - should be " << ybi_cache_expected_size << ", but found " << m_yield_block_info_cache.size() << " - aborting");
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's the right size - check we have the correct limits
|
||||
if (m_yield_block_info_cache.count(height - 1) == 0) {
|
||||
// Missing the latest block - report error and bail out
|
||||
LOG_ERROR("Failed to locate YBI entry for height " << (height - 1) << " - aborting");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_yield_block_info_cache.count(height - ybi_cache_expected_size - 1) == 0) {
|
||||
// Missing the latest block - report error and bail out
|
||||
LOG_ERROR("Failed to locate YBI entry for height " << (height - ybi_cache_expected_size - 1) << " - aborting");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
//TODO: revisit, has changed a bit on upstream
|
||||
bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const
|
||||
@@ -4675,7 +4814,7 @@ leave:
|
||||
{
|
||||
uint64_t long_term_block_weight = get_next_long_term_block_weight(block_weight);
|
||||
cryptonote::blobdata bd = cryptonote::block_to_blob(bl);
|
||||
new_height = m_db->add_block(std::make_pair(std::move(bl), std::move(bd)), block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs);
|
||||
new_height = m_db->add_block(std::make_pair(std::move(bl), std::move(bd)), block_weight, long_term_block_weight, cumulative_difficulty, already_generated_coins, txs, m_nettype);
|
||||
}
|
||||
catch (const KEY_IMAGE_EXISTS& e)
|
||||
{
|
||||
|
||||
@@ -746,6 +746,8 @@ namespace cryptonote
|
||||
*/
|
||||
uint64_t get_current_cumulative_block_weight_median() const;
|
||||
|
||||
int get_yield_info(const uint64_t start_height, const uint64_t end_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_container);
|
||||
|
||||
/**
|
||||
* @brief gets the difficulty of the block with a given height
|
||||
*
|
||||
@@ -1145,6 +1147,33 @@ namespace cryptonote
|
||||
*/
|
||||
uint64_t get_adjusted_time(uint64_t height) const;
|
||||
|
||||
/**
|
||||
* calculate the yield payouts
|
||||
*
|
||||
* @return TRUE if the payouts were calculated successfully, FALSE otherwise
|
||||
*/
|
||||
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_payouts);
|
||||
|
||||
/**
|
||||
* (re)build the yield_block_info cache from the blockchain
|
||||
*
|
||||
* @return TRUE if the cache rebuilt correctly, FALSE otherwise
|
||||
*/
|
||||
bool rebuild_ybi_cache();
|
||||
|
||||
/**
|
||||
* @brief validate the yield_block_info cache
|
||||
*
|
||||
* Checks that the m_yield_block_info_cache is fully populated by
|
||||
* checking the size of the map, and making sure it has the most recent entry
|
||||
* and the oldest expected entry as well
|
||||
*
|
||||
* Returns TRUE if the cache is intact, full, and up-to-date, FALSE otherwise
|
||||
*
|
||||
* @return TRUE if cache is OK, FALSE otherwise
|
||||
*/
|
||||
bool validate_ybi_cache();
|
||||
|
||||
#ifndef IN_UNIT_TESTS
|
||||
private:
|
||||
#endif
|
||||
@@ -1250,6 +1279,8 @@ namespace cryptonote
|
||||
// cache for verifying transaction RCT non semantics
|
||||
mutable rct_ver_cache_t m_rct_ver_cache;
|
||||
|
||||
std::map<uint64_t, yield_block_info> m_yield_block_info_cache;
|
||||
|
||||
/**
|
||||
* @brief collects the keys for all outputs being "spent" as an input
|
||||
*
|
||||
|
||||
@@ -925,7 +925,12 @@ namespace cryptonote
|
||||
tx_info[n].result = false;
|
||||
break;
|
||||
case rct::RCTTypeSimple:
|
||||
if (!rct::verRctSemanticsSimple(rv, tx_info[n].tx->amount_burnt))
|
||||
if (!rct::verRctSemanticsSimple(rv,
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::YIELD ? tx_info[n].tx->amount_burnt :
|
||||
0
|
||||
))
|
||||
{
|
||||
MERROR_VER("rct signature semantics check failed");
|
||||
set_semantics_failed(tx_info[n].tx_hash);
|
||||
@@ -978,7 +983,7 @@ namespace cryptonote
|
||||
}
|
||||
if (!rvv.empty())
|
||||
{
|
||||
LOG_PRINT_L1("One transaction among this group has bad semantics, verifying one at a time");
|
||||
LOG_PRINT_L1("Verifying one TX at a time");
|
||||
ret = false;
|
||||
for (size_t n = 0; n < tx_info.size(); ++n)
|
||||
{
|
||||
@@ -986,7 +991,12 @@ namespace cryptonote
|
||||
continue;
|
||||
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus)
|
||||
continue;
|
||||
if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures, tx_info[n].tx->amount_burnt))
|
||||
if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures,
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
|
||||
tx_info[n].tx->type == cryptonote::transaction_type::YIELD ? tx_info[n].tx->amount_burnt :
|
||||
0
|
||||
))
|
||||
{
|
||||
set_semantics_failed(tx_info[n].tx_hash);
|
||||
tx_info[n].tvc.m_verifivation_failed = true;
|
||||
|
||||
@@ -553,12 +553,6 @@ namespace cryptonote
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/*
|
||||
// Print out the uniqueness
|
||||
crypto::public_key pk_uniq;
|
||||
std::memcpy(pk_uniq.data, uniqueness.data, sizeof(crypto::public_key));
|
||||
LOG_ERROR("*** UNIQUENESS : " << pk_uniq);
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------
|
||||
@@ -907,6 +901,12 @@ namespace cryptonote
|
||||
tx.amount_burnt += dst_entr.amount;
|
||||
continue;
|
||||
}
|
||||
} else if (tx_type == cryptonote::transaction_type::YIELD) {
|
||||
// Do not create outputs that are staked for yield - discard them as unused
|
||||
if (!dst_entr.is_change) {
|
||||
tx.amount_burnt += dst_entr.amount;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the uniqueness for this TX
|
||||
@@ -941,10 +941,29 @@ namespace cryptonote
|
||||
CHECK_AND_ASSERT_MES(calculate_uniqueness(tx.type, k_image, 0, 0, uniqueness), false, "Failed to calculate uniqueness for the transaction");
|
||||
|
||||
// Get the output public key for the change output
|
||||
crypto::public_key P_change;
|
||||
crypto::public_key P_change = crypto::null_pkey;
|
||||
CHECK_AND_ASSERT_MES(tx.vout.size() == 1, false, "Internal error - too many outputs for CONVERT tx");
|
||||
CHECK_AND_ASSERT_MES(cryptonote::get_output_public_key(tx.vout[0], P_change), false, "Internal error - failed to get TX change output public key");
|
||||
CHECK_AND_ASSERT_MES(P_change != crypto::null_pkey, false, "Internal error - not found TX change output for CONVERT tx");
|
||||
|
||||
// Now generate the return address
|
||||
CHECK_AND_ASSERT_MES(get_return_address(tx.version, uniqueness, sender_account_keys, P_change, txkey_pub, tx.return_address, hwdev), false, "Failed to get protocol destination address");
|
||||
|
||||
} else if (tx_type == cryptonote::transaction_type::YIELD) {
|
||||
|
||||
// Get the uniqueness for this TX - must be output zero we are interested in for a CONVERT or YIELD TX
|
||||
CHECK_AND_ASSERT_MES(!tx.vin.empty(), false, "tx.vin[] is empty");
|
||||
CHECK_AND_ASSERT_MES(tx.vin[0].type() == typeid(cryptonote::txin_to_key), false, "incorrect tx.vin[0] type for YIELD TX");
|
||||
crypto::key_image k_image = boost::get<cryptonote::txin_to_key>(tx.vin[0]).k_image;
|
||||
ec_scalar uniqueness;
|
||||
CHECK_AND_ASSERT_MES(calculate_uniqueness(tx.type, k_image, 0, 0, uniqueness), false, "Failed to calculate uniqueness for the transaction");
|
||||
|
||||
// Get the output public key for the change output
|
||||
crypto::public_key P_change = crypto::null_pkey;
|
||||
CHECK_AND_ASSERT_MES(tx.vout.size() == 1, false, "Internal error - incorrect number of outputs for YIELD tx");
|
||||
CHECK_AND_ASSERT_MES(cryptonote::get_output_public_key(tx.vout[0], P_change), false, "Internal error - failed to get TX change output public key");
|
||||
CHECK_AND_ASSERT_MES(P_change != crypto::null_pkey, false, "Internal error - not found TX change output for YIELD tx");
|
||||
|
||||
// Now generate the return address
|
||||
CHECK_AND_ASSERT_MES(get_return_address(tx.version, uniqueness, sender_account_keys, P_change, txkey_pub, tx.return_address, hwdev), false, "Failed to get protocol destination address");
|
||||
}
|
||||
@@ -1125,9 +1144,29 @@ namespace cryptonote
|
||||
if (sources[i].rct)
|
||||
boost::get<txin_to_key>(tx.vin[i]).amount = 0;
|
||||
}
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i)
|
||||
tx.vout[i].amount = 0;
|
||||
std::vector<bool> zero_masks;
|
||||
zero_masks.reserve(tx.vout.size());
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i) {
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
uint64_t unlock_time = 0;
|
||||
bool ok = get_output_unlock_time(tx.vout[i], unlock_time);
|
||||
if (!ok) {
|
||||
LOG_ERROR("failed to get output asset type for tx.vout[" << i << "]");
|
||||
return false;
|
||||
}
|
||||
if (unlock_time == 0) {
|
||||
zero_masks.emplace_back(false);
|
||||
} else {
|
||||
zero_masks.emplace_back(true);
|
||||
}
|
||||
} else {
|
||||
zero_masks.emplace_back(false);
|
||||
}
|
||||
|
||||
// Clear the amount in the output
|
||||
tx.vout[i].amount = 0;
|
||||
}
|
||||
|
||||
crypto::hash tx_prefix_hash;
|
||||
get_transaction_prefix_hash(tx, tx_prefix_hash, hwdev);
|
||||
rct::ctkeyV outSk;
|
||||
@@ -1139,6 +1178,7 @@ namespace cryptonote
|
||||
tx_type,
|
||||
source_asset,
|
||||
destination_asset_types,
|
||||
zero_masks,
|
||||
inamounts,
|
||||
outamounts,
|
||||
fee,
|
||||
|
||||
Reference in New Issue
Block a user