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:
@@ -247,6 +247,13 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
|
||||
}
|
||||
}
|
||||
add_tx_amount_output_indices(tx_id, amount_output_indices);
|
||||
|
||||
// Check to see if this is a YIELD TX
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
|
||||
// We now need to insert a record into the "yield_tx_data" table to record the TX
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
@@ -255,6 +262,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, blobdata>>& txs
|
||||
, const cryptonote::network_type& nettype
|
||||
)
|
||||
{
|
||||
const block &blk = blck.first;
|
||||
@@ -295,6 +303,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
}
|
||||
|
||||
std::map<std::string, int64_t> slippage_counts;
|
||||
uint64_t yield_total = 0;
|
||||
if (blk.protocol_tx.version == 2)
|
||||
{
|
||||
num_rct_outs += blk.protocol_tx.vout.size();
|
||||
@@ -333,44 +342,60 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
|
||||
num_rct_outs_by_asset_type.add(asset_type, 1);
|
||||
}
|
||||
|
||||
// Update the amount tallies by ADDING the burnt amount
|
||||
// Is this a CONVERT TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::CONVERT) {
|
||||
// Update the amount tallies by ADDING the burnt amount
|
||||
if (slippage_counts.count(asset_type) == 0)
|
||||
slippage_counts[asset_type] = 0;
|
||||
slippage_counts[asset_type] += tx.first.amount_burnt;
|
||||
}
|
||||
|
||||
// Is this a YIELD TX?
|
||||
if (tx.first.type == cryptonote::transaction_type::YIELD) {
|
||||
yield_total += tx.first.amount_burnt;
|
||||
}
|
||||
}
|
||||
++tx_i;
|
||||
}
|
||||
|
||||
// SRCG: This is the code that calculates the total slippage for the block
|
||||
// Now convert all of the residual balances into FULM
|
||||
/*
|
||||
boost::multiprecision::int128_t slippage_total_128 = 0;
|
||||
uint64_t slippage_total = 0;
|
||||
for (const auto& tally: slippage_counts) {
|
||||
if (tally.second < 0)
|
||||
throw std::runtime_error("Found a negative tally when summing the burnt/minted amounts");
|
||||
uint64_t slippage_amount = 0;
|
||||
boost::multiprecision::int128_t slippage_amount_128 = 0;
|
||||
if (tally.first == "FULM") {
|
||||
slippage_amount = tally.second;
|
||||
slippage_amount_128 = tally.second;
|
||||
} else {
|
||||
// Sanity check - do we have a price for this asset type in the PR?
|
||||
if (blk.pricing_record.count(tally.first) == 0) {
|
||||
// Sanity check - do we have a price for both source asset type and FULM in the PR?
|
||||
boost::multiprecision::int128_t fulm_price = blk.pricing_record["FULM"];
|
||||
boost::multiprecision::int128_t asset_price = blk.pricing_record[tally.first];
|
||||
if (fulm_price == 0) {
|
||||
// No price available - bail out, because block is invalid
|
||||
throw std::runtime_error("Asset type is not present in available pricing record:" + tally.first);
|
||||
throw std::runtime_error("Asset type 'FULM' is not present in available pricing record");
|
||||
}
|
||||
// Convert the amount
|
||||
//boost::multiprecision::uint128_t tally_128 = tally.second;
|
||||
if (asset_price == 0) {
|
||||
// No price available - bail out, because block is invalid
|
||||
throw std::runtime_error("Asset type '" + tally.first + "' is not present in available pricing record");
|
||||
}
|
||||
// Convert the amount into FULM
|
||||
boost::multiprecision::int128_t tally_128 = tally.second;
|
||||
tally_128 *= asset_price;
|
||||
tally_128 /= fulm_price;
|
||||
slippage_amount_128 = tally_128.convert_to<int64_t>();
|
||||
}
|
||||
slippage_total_128 += slippage_amount_128;
|
||||
}
|
||||
*/
|
||||
if (slippage_total_128 < 0)
|
||||
throw std::runtime_error("Found a negative slippage total when summing the burnt/minted amounts");
|
||||
slippage_total = slippage_total_128.convert_to<uint64_t>();
|
||||
|
||||
TIME_MEASURE_FINISH(time1);
|
||||
time_add_transaction += time1;
|
||||
|
||||
// call out to subclass implementation to add the block & metadata
|
||||
time1 = epee::misc_utils::get_tick_count();
|
||||
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash);
|
||||
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, nettype);
|
||||
TIME_MEASURE_FINISH(time1);
|
||||
time_add_block1 += time1;
|
||||
|
||||
|
||||
@@ -197,6 +197,20 @@ struct txpool_tx_meta_t
|
||||
}
|
||||
};
|
||||
|
||||
typedef struct yield_block_info {
|
||||
uint64_t block_height;
|
||||
uint64_t slippage_total;
|
||||
uint64_t locked_coins;
|
||||
uint64_t locked_coins_tally;
|
||||
uint8_t network_health_percentage;
|
||||
} yield_block_info;
|
||||
|
||||
typedef struct yield_tx_info {
|
||||
uint64_t block_height;
|
||||
crypto::hash tx_hash;
|
||||
uint64_t locked_coins;
|
||||
crypto::public_key return_address;
|
||||
} yield_tx_info;
|
||||
|
||||
#define DBF_SAFE 1
|
||||
#define DBF_FAST 2
|
||||
@@ -408,16 +422,21 @@ private:
|
||||
* @param cumulative_difficulty the accumulated difficulty after this block
|
||||
* @param coins_generated the number of coins generated total after this block
|
||||
* @param blk_hash the hash of the block
|
||||
* @param slippage_total the total value (expressed in FULM coins) of all slippage for this block
|
||||
* @param yield_total the total of FULM coins that have been locked for yield in this block
|
||||
*/
|
||||
virtual void add_block( const block& blk
|
||||
, size_t block_weight
|
||||
, uint64_t long_term_block_weight
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, uint64_t num_rct_outs
|
||||
, oracle::asset_type_counts& cum_rct_by_asset_type
|
||||
, const crypto::hash& blk_hash
|
||||
) = 0;
|
||||
virtual void add_block( const block& blk,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
const cryptonote::network_type& nettype
|
||||
) = 0;
|
||||
|
||||
/**
|
||||
* @brief remove data about the top block
|
||||
@@ -869,7 +888,8 @@ public:
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, blobdata>>& txs
|
||||
);
|
||||
, const cryptonote::network_type& nettype
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief checks if a block exists
|
||||
@@ -1898,6 +1918,10 @@ public:
|
||||
*/
|
||||
virtual uint64_t get_database_size() const = 0;
|
||||
|
||||
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) = 0;
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief set whether or not to automatically remove logs
|
||||
*
|
||||
|
||||
@@ -212,8 +212,8 @@ namespace
|
||||
*
|
||||
* alt_blocks block hash {block data, block blob}
|
||||
*
|
||||
* yield_block_data block height {}
|
||||
* yield_tx_data block height {txn hash, dest address, amount}
|
||||
* yield_block_data block height {slippage_coins, locked_coins, lc_total, network_health}
|
||||
* yield_tx_data block height {txn hash, locked_coins, return_address}
|
||||
*
|
||||
* Note: where the data items are of uniform size, DUPFIXED tables have
|
||||
* been used to save space. In most of these cases, a dummy "zerokval"
|
||||
@@ -223,6 +223,7 @@ namespace
|
||||
*
|
||||
* The output_amounts table doesn't use a dummy key, but uses DUPSORT.
|
||||
*/
|
||||
|
||||
const char* const LMDB_BLOCKS = "blocks";
|
||||
const char* const LMDB_BLOCK_HEIGHTS = "block_heights";
|
||||
const char* const LMDB_BLOCK_INFO = "block_info";
|
||||
@@ -258,17 +259,18 @@ const char* const LMDB_CIRC_SUPPLY_TALLY = "circ_supply_tally";
|
||||
*
|
||||
* block_height (uint64_t) (this is the key field)
|
||||
* ---------------------------------------------------------
|
||||
* txn_hash (crypto:hash) (so we can verify)
|
||||
* dest_address (crypto::key) (where to send the yield)
|
||||
* amount_locked (uint64_t) (how much was locked)
|
||||
* txn_hash (crypto:hash) (so we can verify)
|
||||
* dest_address (crypto::key) (where to send the yield)
|
||||
* amount_locked (uint64_t) (how much was locked)
|
||||
*
|
||||
* We also have the following information that will go into a "yield_blocks" table:
|
||||
*
|
||||
* block_height (uint64_t) (this is the key field)
|
||||
* block_height (uint64_t) (this is the key field)
|
||||
* --------------------------------------------------------
|
||||
* slippage_amount (uint64_t) (amount needed to determine yield payout for the block)
|
||||
* coins_locked (uint64_t) (total number of coins locked at this height)
|
||||
* network_health (uint8_t) (a fudge factor used to adjust the slippage:yield ratio dynamically)
|
||||
* slippage_amount (uint64_t) (amount needed to determine yield payout for the block)
|
||||
* locked_coins (uint64_t) (total number of coins locked at this height)
|
||||
* locked_coins_total (uint64_t) (total number of coins locked at this height)
|
||||
* network_health (uint8_t) (a fudge factor used to adjust the slippage:yield ratio dynamically)
|
||||
*
|
||||
* So, let's say that we have a block height h for which we want to assess the yield payments. First off,
|
||||
* we are ONLY interested in making ANY payment if we have YIELD.block_height == h + 21600 (i.e. the yield
|
||||
@@ -375,33 +377,12 @@ typedef struct outassettype {
|
||||
uint64_t output_id;
|
||||
} outassettype;
|
||||
|
||||
typedef struct circ_supply {
|
||||
crypto::hash tx_hash;
|
||||
uint32_t asset_type;
|
||||
uint64_t amount_burnt;
|
||||
uint64_t amount_minted;
|
||||
} circ_supply;
|
||||
|
||||
typedef struct circ_supply_tally {
|
||||
bool is_negative;
|
||||
uint64_t amount_hi;
|
||||
uint64_t amount_lo;
|
||||
} circ_supply_tally;
|
||||
|
||||
typedef struct yield_tx_data {
|
||||
uint64_t block_height;
|
||||
crypto::hash tx_hash;
|
||||
crypto::public_key return_address;
|
||||
uint64_t amount;
|
||||
} yield_tx_data;
|
||||
|
||||
typedef struct yield_block_data {
|
||||
uint64_t block_height;
|
||||
uint64_t slippage_total;
|
||||
uint64_t locked_coins_total;
|
||||
uint8_t network_health_percentage;
|
||||
} yield_block_data;
|
||||
|
||||
std::atomic<uint64_t> mdb_txn_safe::num_active_txns{0};
|
||||
std::atomic_flag mdb_txn_safe::creation_gate = ATOMIC_FLAG_INIT;
|
||||
|
||||
@@ -819,7 +800,66 @@ estim:
|
||||
return threshold_size;
|
||||
}
|
||||
|
||||
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash)
|
||||
int BlockchainLMDB::get_yield_block_info(const uint64_t height, yield_block_info& ybi)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
mdb_txn_cursors *m_cursors = &m_wcursors;
|
||||
|
||||
// Clear the YBI, just in case
|
||||
std::memset(&ybi, 0, sizeof(struct yield_block_info));
|
||||
|
||||
// Query for the matured YIELD_BLOCK_INFO information
|
||||
CURSOR(yield_blocks)
|
||||
MDB_val v;
|
||||
MDB_val_set(k, height);
|
||||
int ret = mdb_cursor_get(m_cur_yield_blocks, &k, &v, MDB_SET);
|
||||
if (ret == MDB_NOTFOUND) {
|
||||
LOG_ERROR("Failed to locate YBI for block height " << height);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to enumerate yield block info: ", ret).c_str()));
|
||||
|
||||
yield_block_info *p = (yield_block_info*)v.mv_data;
|
||||
ybi = *p;
|
||||
|
||||
// Return success to caller
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
mdb_txn_cursors *m_cursors = &m_wcursors;
|
||||
|
||||
// Clear the container
|
||||
yti_container.clear();
|
||||
|
||||
CURSOR(yield_txs)
|
||||
MDB_val v;
|
||||
MDB_val_set(k, height);
|
||||
MDB_cursor_op op = MDB_FIRST_DUP;
|
||||
while (1)
|
||||
{
|
||||
int ret = mdb_cursor_get(m_cur_yield_txs, &k, &v, op);
|
||||
op = MDB_NEXT_DUP;
|
||||
if (ret == MDB_NOTFOUND)
|
||||
break;
|
||||
if (ret)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to enumerate yield TX info: ", ret).c_str()));
|
||||
|
||||
// Push result back into the container
|
||||
yield_tx_info *p = (yield_tx_info*)v.mv_data;
|
||||
yti_container.emplace_back(*p);
|
||||
}
|
||||
|
||||
// Return success to caller
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, const cryptonote::network_type& nettype)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
@@ -849,11 +889,50 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
|
||||
|
||||
int result = 0;
|
||||
|
||||
CURSOR(yield_blocks)
|
||||
yield_block_info ybi_matured, ybi_prev, ybi;
|
||||
uint64_t yield_lock_period = cryptonote::get_config(nettype).YIELD_LOCK_PERIOD;
|
||||
if (m_height >= yield_lock_period) {
|
||||
uint64_t height_matured = m_height - yield_lock_period - 1;
|
||||
result = get_yield_block_info(height_matured, ybi_matured);
|
||||
if (result)
|
||||
{
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get YBI for matured height: ", result).c_str()));
|
||||
}
|
||||
} else {
|
||||
// Chain is too new - just clear the memory of the "matured" YBI struct
|
||||
std::memset(&ybi_matured, 0, sizeof(struct yield_block_info));
|
||||
ybi_prev.network_health_percentage = 100;
|
||||
}
|
||||
if (m_height >= 1) {
|
||||
// Query for the latest YIELD_BLOCK_INFO information
|
||||
result = get_yield_block_info(m_height - 1, ybi_prev);
|
||||
if (result)
|
||||
{
|
||||
throw0(DB_ERROR(lmdb_error("Failed to get YBI for last block: ", result).c_str()));
|
||||
}
|
||||
} else {
|
||||
// Chain is too new - just clear the memory of the "prev" YBI struct
|
||||
std::memset(&ybi_prev, 0, sizeof(struct yield_block_info));
|
||||
ybi_prev.network_health_percentage = 100;
|
||||
}
|
||||
|
||||
// Create the YIELD_BLOCK_INFO instance for this block
|
||||
ybi.block_height = m_height;
|
||||
ybi.slippage_total = slippage_total;
|
||||
ybi.locked_coins = yield_total;
|
||||
ybi.locked_coins_tally = ybi_prev.locked_coins_tally - ybi_matured.locked_coins_tally + yield_total;
|
||||
ybi.network_health_percentage = 100;
|
||||
|
||||
// Put the YBI into the table
|
||||
MDB_val_set(key, m_height);
|
||||
MDB_val_set(ybi_val, ybi);
|
||||
result = mdb_cursor_put(m_cur_yield_blocks, &key, &ybi_val, MDB_APPEND);
|
||||
if (result)
|
||||
throw0(DB_ERROR(lmdb_error("Failed to add YBI to db: ", result).c_str()));
|
||||
|
||||
CURSOR(blocks)
|
||||
CURSOR(block_info)
|
||||
CURSOR(circ_supply_tally)
|
||||
|
||||
// this call to mdb_cursor_put will change height()
|
||||
cryptonote::blobdata block_blob(block_to_blob(blk));
|
||||
@@ -1143,11 +1222,13 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
|
||||
|
||||
// Is there yield_tx data to add?
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
|
||||
// Create the object we are going to write to the database
|
||||
yield_tx_data yield_data;
|
||||
yield_tx_info yield_data;
|
||||
yield_data.block_height = m_height;
|
||||
yield_data.tx_hash = tx_hash;
|
||||
yield_data.return_address = tx.return_address;
|
||||
yield_data.amount = tx.amount_burnt; // SRCG - this feels as though we are bastardising the variable for an invalid purpose
|
||||
yield_data.locked_coins = tx.amount_burnt;
|
||||
MDB_val_set(val_height, m_height);
|
||||
MDB_val_set(val_yield_tx_data, yield_data);
|
||||
result = mdb_cursor_put(m_cur_yield_txs, &val_height, &val_yield_tx_data, MDB_APPEND);
|
||||
@@ -1175,7 +1256,6 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
CURSOR(txs_prunable_hash)
|
||||
CURSOR(txs_prunable_tip)
|
||||
CURSOR(tx_outputs)
|
||||
CURSOR(circ_supply)
|
||||
CURSOR(circ_supply_tally)
|
||||
CURSOR(yield_txs)
|
||||
|
||||
@@ -1266,20 +1346,6 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
LOG_PRINT_L1("tx ID " << tip->data.tx_id << "\n\tAsset Type = " << cryptonote::asset_type_from_id(asset.first) << "\n\tTally before undoing mint =" << source_tally.str() << "\n\tTally after undoing mint =" << final_source_tally.str());
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Update the circ_supply table by deleting all entries for this TX
|
||||
if ((result = mdb_cursor_get(m_cur_circ_supply, &val_tx_id, NULL, MDB_SET))) {
|
||||
if (result == MDB_NOTFOUND) {
|
||||
LOG_PRINT_L1("failed to obtain circulating supply data - no burns / conversions made yet?");
|
||||
} else {
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate circulating supply for removal: ", result).c_str()));
|
||||
}
|
||||
} else {
|
||||
result = mdb_cursor_del(m_cur_circ_supply, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of circulating supply to db transaction: ", result).c_str()));
|
||||
}
|
||||
*/
|
||||
remove_tx_outputs(tip->data.tx_id, tx);
|
||||
|
||||
result = mdb_cursor_get(m_cur_tx_outputs, &val_tx_id, NULL, MDB_SET);
|
||||
@@ -1297,16 +1363,21 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
|
||||
// Is there yield_tx data to remove?
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
// Remove any yield_tx data for this transaction
|
||||
result = mdb_cursor_get(m_cur_yield_txs, &val_tx_id, NULL, MDB_SET);
|
||||
if (result == MDB_NOTFOUND)
|
||||
LOG_PRINT_L1("tx has no yield_tx data to remove: " << tx_hash);
|
||||
else if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate yield_tx data for removal: ", result).c_str()));
|
||||
if (!result)
|
||||
{
|
||||
result = mdb_cursor_del(m_cur_yield_txs, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of yield_tx data to db transaction: ", result).c_str()));
|
||||
MDB_val_set(val_height, m_height);
|
||||
MDB_val v;
|
||||
while (1) {
|
||||
result = mdb_cursor_get(m_cur_yield_txs, &val_height, &v, MDB_SET);
|
||||
if (result == MDB_NOTFOUND)
|
||||
break;
|
||||
else if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to locate yield_tx data for removal: ", result).c_str()));
|
||||
const yield_tx_info yti = *(const yield_tx_info*)v.mv_data;
|
||||
if (yti.tx_hash == tx_hash) {
|
||||
result = mdb_cursor_del(m_cur_yield_txs, 0);
|
||||
if (result)
|
||||
throw1(DB_ERROR(lmdb_error("Failed to add removal of yield_tx data to db transaction: ", result).c_str()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1790,6 +1861,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||
lmdb_db_open(txn, LMDB_CIRC_SUPPLY_TALLY, MDB_CREATE, m_circ_supply_tally, "Failed to open db handle for m_circ_supply_tally");
|
||||
|
||||
lmdb_db_open(txn, LMDB_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_yield_txs, "Failed to open db handle for m_yield_txs");
|
||||
lmdb_db_open(txn, LMDB_YIELD_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_yield_blocks, "Failed to open db handle for m_yield_blocks");
|
||||
|
||||
mdb_set_dupsort(txn, m_spent_keys, compare_hash32);
|
||||
mdb_set_dupsort(txn, m_block_heights, compare_hash32);
|
||||
@@ -1993,6 +2065,8 @@ void BlockchainLMDB::reset()
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_properties: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_yield_txs, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_yield_txs: ", result).c_str()));
|
||||
if (auto result = mdb_drop(txn, m_yield_blocks, 0))
|
||||
throw0(DB_ERROR(lmdb_error("Failed to drop m_yield_blocks: ", result).c_str()));
|
||||
|
||||
// init with current version
|
||||
MDB_val_str(k, "version");
|
||||
@@ -4532,7 +4606,7 @@ void BlockchainLMDB::block_rtxn_abort() const
|
||||
}
|
||||
|
||||
uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
|
||||
const std::vector<std::pair<transaction, blobdata>>& txs)
|
||||
const std::vector<std::pair<transaction, blobdata>>& txs, const cryptonote::network_type& nettype)
|
||||
{
|
||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||
check_open();
|
||||
@@ -4550,7 +4624,7 @@ uint64_t BlockchainLMDB::add_block(const std::pair<block, blobdata>& blk, size_t
|
||||
|
||||
try
|
||||
{
|
||||
BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs);
|
||||
BlockchainDB::add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, nettype);
|
||||
}
|
||||
catch (const DB_ERROR_TXN_START& e)
|
||||
{
|
||||
|
||||
@@ -341,6 +341,7 @@ public:
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, const std::vector<std::pair<transaction, blobdata>>& txs
|
||||
, const cryptonote::network_type& nettype
|
||||
);
|
||||
|
||||
virtual void set_batch_transactions(bool batch_transactions);
|
||||
@@ -388,15 +389,18 @@ private:
|
||||
void check_and_resize_for_batch(uint64_t batch_num_blocks, uint64_t batch_bytes);
|
||||
uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const;
|
||||
|
||||
virtual void add_block( const block& blk
|
||||
, size_t block_weight
|
||||
, uint64_t long_term_block_weight
|
||||
, const difficulty_type& cumulative_difficulty
|
||||
, const uint64_t& coins_generated
|
||||
, uint64_t num_rct_outs
|
||||
, oracle::asset_type_counts& cum_rct_by_asset_type
|
||||
, const crypto::hash& block_hash
|
||||
);
|
||||
virtual void add_block( const block& blk,
|
||||
size_t block_weight,
|
||||
uint64_t long_term_block_weight,
|
||||
const difficulty_type& cumulative_difficulty,
|
||||
const uint64_t& coins_generated,
|
||||
uint64_t num_rct_outs,
|
||||
oracle::asset_type_counts& cum_rct_by_asset_type,
|
||||
const crypto::hash& blk_hash,
|
||||
uint64_t slippage_total,
|
||||
uint64_t yield_total,
|
||||
const cryptonote::network_type& nettype
|
||||
);
|
||||
|
||||
virtual void remove_block();
|
||||
|
||||
@@ -453,6 +457,9 @@ private:
|
||||
//void migrate_0_1();
|
||||
void cleanup_batch();
|
||||
|
||||
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi);
|
||||
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container);
|
||||
|
||||
private:
|
||||
MDB_env* m_env;
|
||||
|
||||
@@ -486,7 +493,9 @@ private:
|
||||
|
||||
MDB_dbi m_circ_supply;
|
||||
MDB_dbi m_circ_supply_tally;
|
||||
|
||||
MDB_dbi m_yield_txs;
|
||||
MDB_dbi m_yield_blocks;
|
||||
|
||||
mutable uint64_t m_cum_size; // used in batch size estimation
|
||||
mutable unsigned int m_cum_count;
|
||||
|
||||
@@ -488,7 +488,7 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
|
||||
try
|
||||
{
|
||||
uint64_t long_term_block_weight = core.get_blockchain_storage().get_next_long_term_block_weight(block_weight);
|
||||
core.get_blockchain_storage().get_db().add_block(std::make_pair(b, block_to_blob(b)), block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs);
|
||||
core.get_blockchain_storage().get_db().add_block(std::make_pair(b, block_to_blob(b)), block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, txs, opt_testnet ? cryptonote::TESTNET : opt_stagenet ? cryptonote::STAGENET : cryptonote::MAINNET);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
|
||||
Binary file not shown.
@@ -218,11 +218,13 @@ namespace cryptonote
|
||||
FIELD(vout)
|
||||
FIELD(extra)
|
||||
VARINT_FIELD(type)
|
||||
FIELD(return_address)
|
||||
FIELD(source_asset_type)
|
||||
FIELD(destination_asset_type)
|
||||
VARINT_FIELD(amount_burnt)
|
||||
VARINT_FIELD(amount_slippage_limit)
|
||||
if (type != cryptonote::transaction_type::MINER && type != cryptonote::transaction_type::PROTOCOL) {
|
||||
FIELD(return_address)
|
||||
FIELD(source_asset_type)
|
||||
FIELD(destination_asset_type)
|
||||
VARINT_FIELD(amount_burnt)
|
||||
VARINT_FIELD(amount_slippage_limit)
|
||||
}
|
||||
END_SERIALIZE()
|
||||
|
||||
public:
|
||||
|
||||
@@ -167,11 +167,13 @@ namespace boost
|
||||
a & x.vout;
|
||||
a & x.extra;
|
||||
a & x.type;
|
||||
a & x.return_address;
|
||||
a & x.source_asset_type;
|
||||
a & x.destination_asset_type;
|
||||
a & x.amount_burnt;
|
||||
a & x.amount_slippage_limit;
|
||||
if (x.type != cryptonote::transaction_type::MINER && x.type != cryptonote::transaction_type::PROTOCOL) {
|
||||
a & x.return_address;
|
||||
a & x.source_asset_type;
|
||||
a & x.destination_asset_type;
|
||||
a & x.amount_burnt;
|
||||
a & x.amount_slippage_limit;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
@@ -183,20 +185,22 @@ namespace boost
|
||||
a & x.vout;
|
||||
a & x.extra;
|
||||
a & x.type;
|
||||
a & x.return_address;
|
||||
a & x.source_asset_type;
|
||||
a & x.destination_asset_type;
|
||||
a & x.amount_burnt;
|
||||
a & x.amount_slippage_limit;
|
||||
if (x.version == 1)
|
||||
{
|
||||
a & x.signatures;
|
||||
}
|
||||
else
|
||||
{
|
||||
a & (rct::rctSigBase&)x.rct_signatures;
|
||||
if (x.rct_signatures.type != rct::RCTTypeNull)
|
||||
a & x.rct_signatures.p;
|
||||
if (x.type != cryptonote::transaction_type::MINER && x.type != cryptonote::transaction_type::PROTOCOL) {
|
||||
a & x.return_address;
|
||||
a & x.source_asset_type;
|
||||
a & x.destination_asset_type;
|
||||
a & x.amount_burnt;
|
||||
a & x.amount_slippage_limit;
|
||||
if (x.version == 1)
|
||||
{
|
||||
a & x.signatures;
|
||||
}
|
||||
else
|
||||
{
|
||||
a & (rct::rctSigBase&)x.rct_signatures;
|
||||
if (x.rct_signatures.type != rct::RCTTypeNull)
|
||||
a & x.rct_signatures.p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-7
@@ -52,12 +52,11 @@
|
||||
|
||||
// MONEY_SUPPLY - total number coins to be generated
|
||||
#define MONEY_SUPPLY ((uint64_t)(1800000000000000ull))
|
||||
#define EMISSION_SPEED_FACTOR_PER_MINUTE (19)
|
||||
#define EMISSION_SPEED_FACTOR_PER_MINUTE (20)
|
||||
#define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)30000000) // 3 * pow(10, 7)
|
||||
|
||||
#define BURN_LOCK_PERIOD 0
|
||||
#define CONVERT_LOCK_PERIOD 0
|
||||
#define YIELD_LOCK_PERIOD 30*24*30
|
||||
|
||||
#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100
|
||||
#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size
|
||||
@@ -260,10 +259,12 @@ namespace config
|
||||
0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10
|
||||
} }; // Bender's nightmare
|
||||
|
||||
std::string const GENESIS_TX = "020001ff000180c09e90acbb1402e8ee31433aa5ef7eb19af9740660381fe858b541d8c87e5ed38044d061c7072c0446554c4d3c0000000000000021019d1150e5f49422643dfeab470b89c6266f2b1b4469ab969f3bfba0bed74bcd1f0100000000000000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
std::string const GENESIS_TX = "020001ff000180c09e90acbb140228a98ddaae317689e1deb19444b8d60a5132a1f249d8d0ce72b6eba6b79c22240446554c4d3c000000000000002101d52228aa3413ee1bfe2b10fbc1a8cbe2ef9ab2cea0c6bd338103e5f7546384290100";
|
||||
|
||||
uint32_t const GENESIS_NONCE = 10000;
|
||||
|
||||
const uint64_t YIELD_LOCK_PERIOD = 30*24*30;
|
||||
|
||||
// Hash domain separators
|
||||
const char HASH_KEY_BULLETPROOF_EXPONENT[] = "bulletproof";
|
||||
const char HASH_KEY_BULLETPROOF_PLUS_EXPONENT[] = "bulletproof_plus";
|
||||
@@ -333,6 +334,8 @@ namespace config
|
||||
std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1";
|
||||
uint32_t const GENESIS_NONCE = 10001;
|
||||
|
||||
const uint64_t YIELD_LOCK_PERIOD = 20;
|
||||
|
||||
std::array<std::string, 3> const ORACLE_URLS = {{"oracle.fulmo.network:8443", "oracle.fulmo.network:8443", "oracle.fulmo.network:8443"}};
|
||||
|
||||
std::string const ORACLE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n"
|
||||
@@ -355,6 +358,8 @@ namespace config
|
||||
std::string const GENESIS_TX = "013c01ff0001ffffffffffff0302df5d56da0c7d643ddd1ce61901c7bdc5fb1738bfe39fbe69c28a3a7032729c0f2101168d0c4ca86fb55a4cf6a36d31431be1c53a3bd7411bb24e8832410289fa6f3b";
|
||||
uint32_t const GENESIS_NONCE = 10002;
|
||||
|
||||
const uint64_t YIELD_LOCK_PERIOD = 20;
|
||||
|
||||
std::array<std::string, 3> const ORACLE_URLS = {{"oracle.fulmo.network:8443", "oracle.fulmo.network:8443", "oracle.fulmo.network:8443"}};
|
||||
|
||||
std::string const ORACLE_PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n"
|
||||
@@ -387,6 +392,7 @@ namespace cryptonote
|
||||
uint32_t const GENESIS_NONCE;
|
||||
std::array<std::string, 3> const ORACLE_URLS;
|
||||
std::string const ORACLE_PUBLIC_KEY;
|
||||
uint64_t YIELD_LOCK_PERIOD;
|
||||
};
|
||||
inline const config_t& get_config(network_type nettype)
|
||||
{
|
||||
@@ -401,7 +407,8 @@ namespace cryptonote
|
||||
::config::GENESIS_TX,
|
||||
::config::GENESIS_NONCE,
|
||||
::config::ORACLE_URLS,
|
||||
::config::ORACLE_PUBLIC_KEY
|
||||
::config::ORACLE_PUBLIC_KEY,
|
||||
::config::YIELD_LOCK_PERIOD
|
||||
};
|
||||
static const config_t testnet = {
|
||||
::config::testnet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
@@ -414,7 +421,8 @@ namespace cryptonote
|
||||
::config::testnet::GENESIS_TX,
|
||||
::config::testnet::GENESIS_NONCE,
|
||||
::config::testnet::ORACLE_URLS,
|
||||
::config::testnet::ORACLE_PUBLIC_KEY
|
||||
::config::testnet::ORACLE_PUBLIC_KEY,
|
||||
::config::testnet::YIELD_LOCK_PERIOD
|
||||
};
|
||||
static const config_t stagenet = {
|
||||
::config::stagenet::CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX,
|
||||
@@ -427,7 +435,8 @@ namespace cryptonote
|
||||
::config::stagenet::GENESIS_TX,
|
||||
::config::stagenet::GENESIS_NONCE,
|
||||
::config::stagenet::ORACLE_URLS,
|
||||
::config::stagenet::ORACLE_PUBLIC_KEY
|
||||
::config::stagenet::ORACLE_PUBLIC_KEY,
|
||||
::config::stagenet::YIELD_LOCK_PERIOD
|
||||
};
|
||||
switch (nettype)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
+31
-16
@@ -121,12 +121,18 @@ namespace
|
||||
|
||||
namespace rct {
|
||||
|
||||
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev)
|
||||
Bulletproof proveRangeBulletproof(keyV &C, keyV &masks, const std::vector<bool> &zero_masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes");
|
||||
CHECK_AND_ASSERT_THROW_MES(amounts.size() == zero_masks.size(), "Invalid amounts/zero_masks sizes");
|
||||
masks.resize(amounts.size());
|
||||
for (size_t i = 0; i < masks.size(); ++i)
|
||||
masks[i] = hwdev.genCommitmentMask(sk[i]);
|
||||
for (size_t i = 0; i < masks.size(); ++i) {
|
||||
if (zero_masks[i] == true) {
|
||||
masks[i] = rct::identity();
|
||||
} else {
|
||||
masks[i] = hwdev.genCommitmentMask(sk[i]);
|
||||
}
|
||||
}
|
||||
Bulletproof proof = bulletproof_PROVE(amounts, masks);
|
||||
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size");
|
||||
C = proof.V;
|
||||
@@ -147,12 +153,18 @@ namespace rct {
|
||||
catch (...) { return false; }
|
||||
}
|
||||
|
||||
BulletproofPlus proveRangeBulletproofPlus(keyV &C, keyV &masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev)
|
||||
BulletproofPlus proveRangeBulletproofPlus(keyV &C, keyV &masks, const std::vector<bool> &zero_masks, const std::vector<uint64_t> &amounts, epee::span<const key> sk, hw::device &hwdev)
|
||||
{
|
||||
CHECK_AND_ASSERT_THROW_MES(amounts.size() == sk.size(), "Invalid amounts/sk sizes");
|
||||
CHECK_AND_ASSERT_THROW_MES(amounts.size() == zero_masks.size(), "Invalid amounts/zero_masks sizes");
|
||||
masks.resize(amounts.size());
|
||||
for (size_t i = 0; i < masks.size(); ++i)
|
||||
masks[i] = hwdev.genCommitmentMask(sk[i]);
|
||||
for (size_t i = 0; i < masks.size(); ++i) {
|
||||
if (zero_masks[i] == true) {
|
||||
masks[i] = rct::identity();
|
||||
} else {
|
||||
masks[i] = hwdev.genCommitmentMask(sk[i]);
|
||||
}
|
||||
}
|
||||
BulletproofPlus proof = bulletproof_plus_PROVE(amounts, masks);
|
||||
CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size");
|
||||
C = proof.V;
|
||||
@@ -1110,8 +1122,9 @@ namespace rct {
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string& in_asset_type,
|
||||
const std::vector<std::string> & destination_asset_types,
|
||||
const vector<xmr_amount> &inamounts,
|
||||
const vector<xmr_amount> &outamounts,
|
||||
const std::vector<bool> &zero_masks,
|
||||
const std::vector<xmr_amount> &inamounts,
|
||||
const std::vector<xmr_amount> &outamounts,
|
||||
xmr_amount txnFee,
|
||||
const ctkeyM & mixRing,
|
||||
const keyV &amount_keys,
|
||||
@@ -1126,6 +1139,7 @@ namespace rct {
|
||||
CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
|
||||
CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
|
||||
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
|
||||
CHECK_AND_ASSERT_THROW_MES(zero_masks.size() == destinations.size(), "Different number of zero_masks/destinations");
|
||||
CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk");
|
||||
CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk");
|
||||
for (size_t n = 0; n < mixRing.size(); ++n) {
|
||||
@@ -1192,9 +1206,9 @@ namespace rct {
|
||||
{
|
||||
const epee::span<const key> keys{&amount_keys[0], amount_keys.size()};
|
||||
if (plus)
|
||||
rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, outamounts, keys, hwdev));
|
||||
rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, zero_masks, outamounts, keys, hwdev));
|
||||
else
|
||||
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts, keys, hwdev));
|
||||
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, zero_masks, outamounts, keys, hwdev));
|
||||
#ifdef DBG
|
||||
if (plus)
|
||||
CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof");
|
||||
@@ -1230,9 +1244,9 @@ namespace rct {
|
||||
{
|
||||
const epee::span<const key> keys{&amount_keys[amounts_proved], batch_size};
|
||||
if (plus)
|
||||
rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, batch_amounts, keys, hwdev));
|
||||
rv.p.bulletproofs_plus.push_back(proveRangeBulletproofPlus(C, masks, zero_masks, batch_amounts, keys, hwdev));
|
||||
else
|
||||
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, batch_amounts, keys, hwdev));
|
||||
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, zero_masks, batch_amounts, keys, hwdev));
|
||||
#ifdef DBG
|
||||
if (plus)
|
||||
CHECK_AND_ASSERT_THROW_MES(verBulletproofPlus(rv.p.bulletproofs_plus.back()), "verBulletproofPlus failed on newly created proof");
|
||||
@@ -1308,9 +1322,10 @@ namespace rct {
|
||||
const keyV & destinations,
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string& in_asset_type,
|
||||
const std::vector<std::string> & destination_asset_types,
|
||||
const vector<xmr_amount> &inamounts,
|
||||
const vector<xmr_amount> &outamounts,
|
||||
const std::vector<std::string> & destination_asset_types,
|
||||
const std::vector<bool> &zero_masks,
|
||||
const std::vector<xmr_amount> &inamounts,
|
||||
const std::vector<xmr_amount> &outamounts,
|
||||
const keyV &amount_keys,
|
||||
xmr_amount txnFee,
|
||||
unsigned int mixin,
|
||||
@@ -1326,7 +1341,7 @@ namespace rct {
|
||||
mixRing[i].resize(mixin+1);
|
||||
index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
|
||||
}
|
||||
return genRctSimple(message, inSk, destinations, tx_type, in_asset_type, destination_asset_types, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev);
|
||||
return genRctSimple(message, inSk, destinations, tx_type, in_asset_type, destination_asset_types, zero_masks, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, rct_config, hwdev);
|
||||
}
|
||||
|
||||
//RingCT protocol
|
||||
|
||||
@@ -135,6 +135,7 @@ namespace rct {
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string& in_asset_type,
|
||||
const std::vector<std::string> & destination_asset_types,
|
||||
const std::vector<bool> &zero_masks,
|
||||
const std::vector<xmr_amount> & inamounts,
|
||||
const std::vector<xmr_amount> & outamounts,
|
||||
const keyV &amount_keys,
|
||||
@@ -150,6 +151,7 @@ namespace rct {
|
||||
const cryptonote::transaction_type tx_type,
|
||||
const std::string& in_asset_type,
|
||||
const std::vector<std::string> & destination_asset_types,
|
||||
const std::vector<bool> &zero_masks,
|
||||
const std::vector<xmr_amount> & inamounts,
|
||||
const std::vector<xmr_amount> & outamounts,
|
||||
xmr_amount txnFee,
|
||||
|
||||
@@ -6827,17 +6827,19 @@ bool simple_wallet::transfer_main(
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::CONVERT, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
case LockForYield:
|
||||
unlock_block = YIELD_LOCK_PERIOD;
|
||||
unlock_block = get_config(m_wallet->nettype()).YIELD_LOCK_PERIOD;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::YIELD, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
case TransferLocked:
|
||||
/*
|
||||
bc_height = get_daemon_blockchain_height(err);
|
||||
if (!err.empty())
|
||||
{
|
||||
fail_msg_writer() << tr("failed to get blockchain height: ") << err;
|
||||
return false;
|
||||
}
|
||||
unlock_block = bc_height + locked_blocks;
|
||||
*/
|
||||
unlock_block = locked_blocks;
|
||||
ptx_vector = m_wallet->create_transactions_2(dsts, source_asset, dest_asset, cryptonote::transaction_type::TRANSFER, fake_outs_count, unlock_block /* unlock_time */, priority, extra, m_current_subaddress_account, subaddr_indices);
|
||||
break;
|
||||
default:
|
||||
|
||||
+42
-21
@@ -2044,11 +2044,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
|
||||
std::string source_asset = tx.source_asset_type;
|
||||
std::string dest_asset = tx.destination_asset_type;
|
||||
cryptonote::transaction_type tx_type_verify = tx.type;
|
||||
if (!miner_tx) {
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_tx_type(source_asset, dest_asset, tx_type_verify), error::wallet_internal_error, "Failed to get TX type");
|
||||
THROW_WALLET_EXCEPTION_IF(tx_type_verify != tx.type, error::wallet_internal_error, "Incorrect TX type");
|
||||
}
|
||||
|
||||
// per receiving subaddress index
|
||||
std::unordered_map<cryptonote::subaddress_index, std::map<std::string, uint64_t>> tx_money_got_in_outs;
|
||||
@@ -2257,20 +2252,20 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
//usually we have only one transfer for user in transaction
|
||||
if (!pool)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size() || tx.vout.size() != asset_type_output_indices.size(), error::wallet_internal_error,
|
||||
"transactions outputs size=" + std::to_string(tx.vout.size()) +
|
||||
" not match with daemon response size=" + std::to_string(o_indices.size())
|
||||
+ " or with asset outputs size=" + std::to_string(asset_type_output_indices.size()));
|
||||
THROW_WALLET_EXCEPTION_IF(tx.vout.size() != o_indices.size() || tx.vout.size() != asset_type_output_indices.size(), error::wallet_internal_error,
|
||||
"transactions outputs size=" + std::to_string(tx.vout.size()) +
|
||||
" not match with daemon response size=" + std::to_string(o_indices.size())
|
||||
+ " or with asset outputs size=" + std::to_string(asset_type_output_indices.size()));
|
||||
}
|
||||
|
||||
for(size_t o: outs)
|
||||
{
|
||||
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
|
||||
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
|
||||
THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" +
|
||||
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
|
||||
|
||||
auto kit = m_pub_keys.find(tx_scan_info[o].in_ephemeral.pub);
|
||||
|
||||
THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
|
||||
THROW_WALLET_EXCEPTION_IF(kit != m_pub_keys.end() && kit->second >= m_transfers.size(),
|
||||
error::wallet_internal_error, std::string("Unexpected transfer index from public key: ")
|
||||
+ "got " + (kit == m_pub_keys.end() ? "<none>" : boost::lexical_cast<std::string>(kit->second))
|
||||
+ ", m_transfers.size() is " + boost::lexical_cast<std::string>(m_transfers.size()));
|
||||
@@ -2337,10 +2332,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
td.m_rct = false;
|
||||
}
|
||||
td.m_frozen = false;
|
||||
set_unspent(m_transfers.size()-1);
|
||||
set_unspent(m_transfers.size()-1);
|
||||
if (td.m_key_image_known)
|
||||
m_key_images[td.m_key_image] = m_transfers.size()-1;
|
||||
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
|
||||
m_key_images[td.m_key_image] = m_transfers.size()-1;
|
||||
m_pub_keys[tx_scan_info[o].in_ephemeral.pub] = m_transfers.size()-1;
|
||||
if (output_tracker_cache)
|
||||
(*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = m_transfers.size() - 1;
|
||||
if (m_multisig)
|
||||
@@ -2366,14 +2361,21 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
// Add the change output_public_key to the list of subaddresses to check
|
||||
crypto::public_key P_change = crypto::null_pkey;
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_public_key(tx.vout[0], P_change), error::wallet_internal_error, "Failed to get change output public key");
|
||||
//m_subaddresses[P_change] = {0x50524F54,0x4F434F4C};
|
||||
//m_subaddresses[P_change] = {0x50524F54,0x4F434F4C}; /* {PROT,OCOL} - seemed like a good idea at the time, but harder to implement! */
|
||||
m_subaddresses[P_change] = {0,0};
|
||||
m_protocol_txs.insert({P_change, m_transfers.size()-1});
|
||||
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
// Additionally, with YIELD TXs, we need to update our "balance staked" subtotal, because otherwise our balance is out by the staked coins until they mature!
|
||||
// SRCG: must remember to deduct the number of staked coins when they mature!!
|
||||
LOG_ERROR("***** STAKED COINS : " << tx.amount_burnt << " *****");
|
||||
m_locked_coins.insert({P_change, {0, tx.amount_burnt}});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx_scan_info[o].amount)
|
||||
{
|
||||
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
|
||||
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
|
||||
<< " from received " << print_money(tx_scan_info[o].amount) << " output already exists with "
|
||||
<< (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " "
|
||||
<< print_money(m_transfers[kit->second].amount()) << " in tx " << m_transfers[kit->second].m_txid << ", received output ignored");
|
||||
@@ -2389,7 +2391,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
|
||||
LOG_ERROR("Public key " << epee::string_tools::pod_to_hex(kit->first)
|
||||
<< " from received " << print_money(tx_scan_info[o].amount) << " output already exists with "
|
||||
<< print_money(m_transfers[kit->second].amount()) << ", replacing with new output");
|
||||
// The new larger output replaced a previous smaller one
|
||||
@@ -4031,6 +4033,7 @@ bool wallet2::clear()
|
||||
m_blockchain.clear();
|
||||
m_transfers.clear();
|
||||
m_transfers_indices.clear();
|
||||
m_locked_coins.clear();
|
||||
m_key_images.clear();
|
||||
m_pub_keys.clear();
|
||||
m_unconfirmed_txs.clear();
|
||||
@@ -4054,6 +4057,7 @@ void wallet2::clear_soft(bool keep_key_images)
|
||||
m_blockchain.clear();
|
||||
m_transfers.clear();
|
||||
m_transfers_indices.clear();
|
||||
m_locked_coins.clear();
|
||||
if (!keep_key_images)
|
||||
m_key_images.clear();
|
||||
m_pub_keys.clear();
|
||||
@@ -6163,6 +6167,11 @@ uint64_t wallet2::balance(uint32_t index_major, const std::string& asset_type, b
|
||||
uint64_t amount = 0;
|
||||
for (const auto& i : balance_per_subaddress(index_major, asset_type, strict))
|
||||
amount += i.second;
|
||||
if (asset_type == "FULM") {
|
||||
// Iterate over the locked coins, adding them to the _locked_ balance
|
||||
for (const auto& i : m_locked_coins)
|
||||
amount += i.second.m_amount;
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
@@ -9329,6 +9338,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
|
||||
|
||||
// we still keep a copy, since we want to keep dsts free of change for user feedback purposes
|
||||
std::vector<cryptonote::tx_destination_entry> splitted_dsts = dsts;
|
||||
|
||||
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
|
||||
change_dts.amount = found_money - needed_money;
|
||||
change_dts.asset_type = source_asset;
|
||||
@@ -10351,7 +10361,12 @@ bool wallet2::sanity_check(const std::vector<wallet2::pending_tx> &ptx_vector, s
|
||||
check_tx_proof(ptx.tx, address, r.second.second, "automatic-sanity-check", proof, received);
|
||||
}
|
||||
catch (const std::exception &e) { received = 0; }
|
||||
received += ptx.tx.amount_burnt;
|
||||
|
||||
if (ptx.tx.type == cryptonote::transaction_type::CONVERT)
|
||||
received += ptx.tx.amount_burnt;
|
||||
else if (ptx.tx.type == cryptonote::transaction_type::YIELD)
|
||||
received += ptx.tx.amount_burnt;
|
||||
|
||||
total_received += received;
|
||||
}
|
||||
|
||||
@@ -11415,7 +11430,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
|
||||
k_image = boost::get<cryptonote::txin_to_key>(tx.vin[0]).k_image;
|
||||
}
|
||||
crypto::ec_scalar uniqueness;
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::calculate_uniqueness(tx.type, k_image, ((size_t)(-1)), 0, uniqueness), error::wallet_internal_error, "Failed to calculate uniqueness");
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::calculate_uniqueness(tx.type, k_image, ((size_t)(-1)), n, uniqueness), error::wallet_internal_error, "Failed to calculate uniqueness");
|
||||
|
||||
crypto::key_derivation found_derivation;
|
||||
if (is_out_to_acc(address, output_public_key, derivation, additional_derivations, n, uniqueness, get_output_view_tag(tx.vout[n]), found_derivation))
|
||||
@@ -11431,7 +11446,13 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
|
||||
//crypto::hash uniqueness = cn_fast_hash(reinterpret_cast<void*>(&n), sizeof(size_t));
|
||||
crypto::derivation_to_scalar(found_derivation, uniqueness, scalar1);
|
||||
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
|
||||
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
|
||||
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
|
||||
if (tx.type == cryptonote::transaction_type::YIELD) {
|
||||
uint64_t unlock_time = 0;
|
||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::get_output_unlock_time(tx.vout[n], unlock_time), error::wallet_internal_error, "Failed to get output unlock time");
|
||||
if (unlock_time == get_config(m_nettype).YIELD_LOCK_PERIOD)
|
||||
ecdh_info.mask = rct::identity();
|
||||
}
|
||||
const rct::key C = tx.rct_signatures.outPk[n].mask;
|
||||
rct::key Ctmp;
|
||||
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
|
||||
|
||||
@@ -471,6 +471,17 @@ private:
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct locked_yield_details
|
||||
{
|
||||
uint32_t m_index_major;
|
||||
uint64_t m_amount;
|
||||
|
||||
BEGIN_SERIALIZE_OBJECT()
|
||||
VARINT_FIELD(m_index_major)
|
||||
VARINT_FIELD(m_amount)
|
||||
END_SERIALIZE()
|
||||
};
|
||||
|
||||
struct unconfirmed_transfer_details
|
||||
{
|
||||
cryptonote::transaction_prefix m_tx;
|
||||
@@ -1155,6 +1166,7 @@ private:
|
||||
}
|
||||
a & m_transfers;
|
||||
a & m_transfers_indices;
|
||||
a & m_locked_coins;
|
||||
a & m_account_public_address;
|
||||
a & m_key_images.parent();
|
||||
if(ver < 6)
|
||||
@@ -1266,6 +1278,7 @@ private:
|
||||
FIELD(m_blockchain)
|
||||
FIELD(m_transfers)
|
||||
FIELD(m_transfers_indices)
|
||||
FIELD(m_locked_coins)
|
||||
FIELD(m_account_public_address)
|
||||
FIELD(m_key_images)
|
||||
FIELD(m_unconfirmed_txs)
|
||||
@@ -1807,6 +1820,7 @@ private:
|
||||
|
||||
transfer_container m_transfers;
|
||||
transfer_details_indices m_transfers_indices;
|
||||
serializable_unordered_map<crypto::public_key, locked_yield_details> m_locked_coins;
|
||||
payment_container m_payments;
|
||||
serializable_unordered_map<crypto::key_image, size_t> m_key_images;
|
||||
serializable_unordered_map<crypto::public_key, size_t> m_pub_keys;
|
||||
@@ -2127,6 +2141,13 @@ namespace boost
|
||||
a & x.m_signers;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, tools::wallet2::locked_yield_details &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
a & x.m_index_major;
|
||||
a & x.m_amount;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
inline void serialize(Archive &a, tools::wallet2::unconfirmed_transfer_details &x, const boost::serialization::version_type ver)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user