v8: per byte fee, pad bulletproofs, fixed 11 ring size

This commit is contained in:
moneromooo-monero 2018-07-18 22:24:53 +01:00
parent 869b3bf824
commit 5ffb2ff9b7
No known key found for this signature in database
GPG Key ID: 686F07454D6CEFC3
55 changed files with 1241 additions and 878 deletions

View File

@ -224,7 +224,7 @@ struct Dbt_safe : public Dbt
namespace cryptonote namespace cryptonote
{ {
void BlockchainBDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) void BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash)
{ {
LOG_PRINT_L3("BlockchainBDB::" << __func__); LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open(); check_open();
@ -255,7 +255,7 @@ void BlockchainBDB::add_block(const block& blk, const size_t& block_size, const
if (res) if (res)
throw0(DB_ERROR("Failed to add block blob to db transaction.")); throw0(DB_ERROR("Failed to add block blob to db transaction."));
Dbt_copy<size_t> sz(block_size); Dbt_copy<size_t> sz(block_weight);
if (m_block_sizes->put(DB_DEFAULT_TX, &key, &sz, 0)) if (m_block_sizes->put(DB_DEFAULT_TX, &key, &sz, 0))
throw0(DB_ERROR("Failed to add block size to db transaction.")); throw0(DB_ERROR("Failed to add block size to db transaction."));
@ -1353,7 +1353,7 @@ uint64_t BlockchainBDB::get_top_block_timestamp() const
return get_block_timestamp(m_height - 1); return get_block_timestamp(m_height - 1);
} }
size_t BlockchainBDB::get_block_size(const uint64_t& height) const size_t BlockchainBDB::get_block_weight(const uint64_t& height) const
{ {
LOG_PRINT_L3("BlockchainBDB::" << __func__); LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open(); check_open();
@ -1861,7 +1861,7 @@ void BlockchainBDB::block_txn_abort()
// TODO // TODO
} }
uint64_t BlockchainBDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<transaction>& txs) uint64_t BlockchainBDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const std::vector<transaction>& txs)
{ {
LOG_PRINT_L3("BlockchainBDB::" << __func__); LOG_PRINT_L3("BlockchainBDB::" << __func__);
check_open(); check_open();
@ -1874,7 +1874,7 @@ uint64_t BlockchainBDB::add_block(const block& blk, const size_t& block_size, co
uint64_t num_outputs = m_num_outputs; uint64_t num_outputs = m_num_outputs;
try try
{ {
BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs);
m_write_txn = NULL; m_write_txn = NULL;
TIME_MEASURE_START(time1); TIME_MEASURE_START(time1);

View File

@ -266,7 +266,7 @@ public:
virtual uint64_t get_top_block_timestamp() const; virtual uint64_t get_top_block_timestamp() const;
virtual size_t get_block_size(const uint64_t& height) const; virtual size_t get_block_weight(const uint64_t& height) const;
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const;
@ -318,7 +318,7 @@ public:
virtual bool has_key_image(const crypto::key_image& img) const; virtual bool has_key_image(const crypto::key_image& img) const;
virtual uint64_t add_block( const block& blk virtual uint64_t add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -353,7 +353,7 @@ public:
private: private:
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const crypto::hash& block_hash , const crypto::hash& block_hash

View File

@ -196,7 +196,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
} }
uint64_t BlockchainDB::add_block( const block& blk uint64_t BlockchainDB::add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -241,7 +241,7 @@ uint64_t BlockchainDB::add_block( const block& blk
// call out to subclass implementation to add the block & metadata // call out to subclass implementation to add the block & metadata
time1 = epee::misc_utils::get_tick_count(); time1 = epee::misc_utils::get_tick_count();
add_block(blk, block_size, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash); add_block(blk, block_weight, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash);
TIME_MEASURE_FINISH(time1); TIME_MEASURE_FINISH(time1);
time_add_block1 += time1; time_add_block1 += time1;

View File

@ -137,7 +137,7 @@ struct txpool_tx_meta_t
{ {
crypto::hash max_used_block_id; crypto::hash max_used_block_id;
crypto::hash last_failed_id; crypto::hash last_failed_id;
uint64_t blob_size; uint64_t weight;
uint64_t fee; uint64_t fee;
uint64_t max_used_block_height; uint64_t max_used_block_height;
uint64_t last_failed_height; uint64_t last_failed_height;
@ -358,13 +358,13 @@ private:
* subclass of DB_EXCEPTION * subclass of DB_EXCEPTION
* *
* @param blk the block to be added * @param blk the block to be added
* @param block_size the size of the block (transactions and all) * @param block_weight the weight of the block (transactions and all)
* @param cumulative_difficulty the accumulated difficulty after this block * @param cumulative_difficulty the accumulated difficulty after this block
* @param coins_generated the number of coins generated total after this block * @param coins_generated the number of coins generated total after this block
* @param blk_hash the hash of the block * @param blk_hash the hash of the block
*/ */
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, uint64_t num_rct_outs , uint64_t num_rct_outs
@ -376,7 +376,7 @@ private:
* *
* The subclass implementing this will remove the block data from the top * The subclass implementing this will remove the block data from the top
* block in the chain. The data to be removed is that which was added in * block in the chain. The data to be removed is that which was added in
* BlockchainDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash) * BlockchainDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, const crypto::hash& blk_hash)
* *
* If any of this cannot be done, the subclass should throw the corresponding * If any of this cannot be done, the subclass should throw the corresponding
* subclass of DB_EXCEPTION * subclass of DB_EXCEPTION
@ -789,7 +789,7 @@ public:
* subclass of DB_EXCEPTION * subclass of DB_EXCEPTION
* *
* @param blk the block to be added * @param blk the block to be added
* @param block_size the size of the block (transactions and all) * @param block_weight the size of the block (transactions and all)
* @param cumulative_difficulty the accumulated difficulty after this block * @param cumulative_difficulty the accumulated difficulty after this block
* @param coins_generated the number of coins generated total after this block * @param coins_generated the number of coins generated total after this block
* @param txs the transactions in the block * @param txs the transactions in the block
@ -797,7 +797,7 @@ public:
* @return the height of the chain post-addition * @return the height of the chain post-addition
*/ */
virtual uint64_t add_block( const block& blk virtual uint64_t add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -930,18 +930,18 @@ public:
virtual uint64_t get_top_block_timestamp() const = 0; virtual uint64_t get_top_block_timestamp() const = 0;
/** /**
* @brief fetch a block's size * @brief fetch a block's weight
* *
* The subclass should return the size of the block with the * The subclass should return the weight of the block with the
* given height. * given height.
* *
* If the block does not exist, the subclass should throw BLOCK_DNE * If the block does not exist, the subclass should throw BLOCK_DNE
* *
* @param height the height requested * @param height the height requested
* *
* @return the size * @return the weight
*/ */
virtual size_t get_block_size(const uint64_t& height) const = 0; virtual size_t get_block_weight(const uint64_t& height) const = 0;
/** /**
* @brief fetch a block's cumulative difficulty * @brief fetch a block's cumulative difficulty

View File

@ -256,7 +256,7 @@ typedef struct mdb_block_info_old
uint64_t bi_height; uint64_t bi_height;
uint64_t bi_timestamp; uint64_t bi_timestamp;
uint64_t bi_coins; uint64_t bi_coins;
uint64_t bi_size; // a size_t really but we need 32-bit compat uint64_t bi_weight; // a size_t really but we need 32-bit compat
difficulty_type bi_diff; difficulty_type bi_diff;
crypto::hash bi_hash; crypto::hash bi_hash;
} mdb_block_info_old; } mdb_block_info_old;
@ -266,7 +266,7 @@ typedef struct mdb_block_info
uint64_t bi_height; uint64_t bi_height;
uint64_t bi_timestamp; uint64_t bi_timestamp;
uint64_t bi_coins; uint64_t bi_coins;
uint64_t bi_size; // a size_t really but we need 32-bit compat uint64_t bi_weight; // a size_t really but we need 32-bit compat
difficulty_type bi_diff; difficulty_type bi_diff;
crypto::hash bi_hash; crypto::hash bi_hash;
uint64_t bi_cum_rct; uint64_t bi_cum_rct;
@ -652,8 +652,11 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
block_rtxn_start(&rtxn, &rcurs); block_rtxn_start(&rtxn, &rcurs);
for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num) for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num)
{ {
uint32_t block_size = get_block_size(block_num); // we have access to block weight, which will be greater or equal to block size,
total_block_size += block_size; // so use this as a proxy. If it's too much off, we might have to check actual size,
// which involves reading more data, so is not really wanted
size_t block_weight = get_block_weight(block_num);
total_block_size += block_weight;
// Track number of blocks being totalled here instead of assuming, in case // Track number of blocks being totalled here instead of assuming, in case
// some blocks were to be skipped for being outliers. // some blocks were to be skipped for being outliers.
++num_blocks_used; ++num_blocks_used;
@ -674,7 +677,7 @@ estim:
return threshold_size; return threshold_size;
} }
void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, void BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
uint64_t num_rct_outs, const crypto::hash& blk_hash) uint64_t num_rct_outs, const crypto::hash& blk_hash)
{ {
LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -720,7 +723,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
bi.bi_height = m_height; bi.bi_height = m_height;
bi.bi_timestamp = blk.timestamp; bi.bi_timestamp = blk.timestamp;
bi.bi_coins = coins_generated; bi.bi_coins = coins_generated;
bi.bi_size = block_size; bi.bi_weight = block_weight;
bi.bi_diff = cumulative_difficulty; bi.bi_diff = cumulative_difficulty;
bi.bi_hash = blk_hash; bi.bi_hash = blk_hash;
bi.bi_cum_rct = num_rct_outs; bi.bi_cum_rct = num_rct_outs;
@ -743,7 +746,9 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
if (result) if (result)
throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str()));
m_cum_size += block_size; // we use weight as a proxy for size, since we don't have size but weight is >= size
// and often actually equal
m_cum_size += block_weight;
m_cum_count++; m_cum_count++;
} }
@ -2011,7 +2016,7 @@ uint64_t BlockchainLMDB::get_top_block_timestamp() const
return get_block_timestamp(m_height - 1); return get_block_timestamp(m_height - 1);
} }
size_t BlockchainLMDB::get_block_size(const uint64_t& height) const size_t BlockchainLMDB::get_block_weight(const uint64_t& height) const
{ {
LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open(); check_open();
@ -2029,7 +2034,7 @@ size_t BlockchainLMDB::get_block_size(const uint64_t& height) const
throw0(DB_ERROR("Error attempting to retrieve a block size from the db")); throw0(DB_ERROR("Error attempting to retrieve a block size from the db"));
mdb_block_info *bi = (mdb_block_info *)result.mv_data; mdb_block_info *bi = (mdb_block_info *)result.mv_data;
size_t ret = bi->bi_size; size_t ret = bi->bi_weight;
TXN_POSTFIX_RDONLY(); TXN_POSTFIX_RDONLY();
return ret; return ret;
} }
@ -3090,7 +3095,7 @@ void BlockchainLMDB::block_txn_abort()
} }
} }
uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t BlockchainLMDB::add_block(const block& blk, size_t block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
const std::vector<transaction>& txs) const std::vector<transaction>& txs)
{ {
LOG_PRINT_L3("BlockchainLMDB::" << __func__); LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -3109,7 +3114,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c
try try
{ {
BlockchainDB::add_block(blk, block_size, cumulative_difficulty, coins_generated, txs); BlockchainDB::add_block(blk, block_weight, cumulative_difficulty, coins_generated, txs);
} }
catch (const DB_ERROR_TXN_START& e) catch (const DB_ERROR_TXN_START& e)
{ {
@ -3698,9 +3703,9 @@ void BlockchainLMDB::migrate_0_1()
if (result) if (result)
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_sizes: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to get a record from block_sizes: ", result).c_str()));
if (v.mv_size == sizeof(uint32_t)) if (v.mv_size == sizeof(uint32_t))
bi.bi_size = *(uint32_t *)v.mv_data; bi.bi_weight = *(uint32_t *)v.mv_data;
else else
bi.bi_size = *(uint64_t *)v.mv_data; // this is a 32/64 compat bug in version 0 bi.bi_weight = *(uint64_t *)v.mv_data; // this is a 32/64 compat bug in version 0
result = mdb_cursor_get(c_timestamps, &k, &v, MDB_NEXT); result = mdb_cursor_get(c_timestamps, &k, &v, MDB_NEXT);
if (result) if (result)
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_timestamps: ", result).c_str())); throw0(DB_ERROR(lmdb_error("Failed to get a record from block_timestamps: ", result).c_str()));
@ -4259,7 +4264,7 @@ void BlockchainLMDB::migrate_2_3()
bi.bi_height = bi_old->bi_height; bi.bi_height = bi_old->bi_height;
bi.bi_timestamp = bi_old->bi_timestamp; bi.bi_timestamp = bi_old->bi_timestamp;
bi.bi_coins = bi_old->bi_coins; bi.bi_coins = bi_old->bi_coins;
bi.bi_size = bi_old->bi_size; bi.bi_weight = bi_old->bi_weight;
bi.bi_diff = bi_old->bi_diff; bi.bi_diff = bi_old->bi_diff;
bi.bi_hash = bi_old->bi_hash; bi.bi_hash = bi_old->bi_hash;
if (bi_old->bi_height >= distribution.size()) if (bi_old->bi_height >= distribution.size())

View File

@ -205,7 +205,7 @@ public:
virtual uint64_t get_top_block_timestamp() const; virtual uint64_t get_top_block_timestamp() const;
virtual size_t get_block_size(const uint64_t& height) const; virtual size_t get_block_weight(const uint64_t& height) const;
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const; virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const;
@ -273,7 +273,7 @@ public:
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const; virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const;
virtual uint64_t add_block( const block& blk virtual uint64_t add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, const std::vector<transaction>& txs , const std::vector<transaction>& txs
@ -317,7 +317,7 @@ private:
uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const; uint64_t get_estimated_batch_size(uint64_t batch_num_blocks, uint64_t batch_bytes) const;
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, uint64_t num_rct_outs , uint64_t num_rct_outs

View File

@ -474,17 +474,17 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
txs.push_back(tx); txs.push_back(tx);
} }
size_t block_size; size_t block_weight;
difficulty_type cumulative_difficulty; difficulty_type cumulative_difficulty;
uint64_t coins_generated; uint64_t coins_generated;
block_size = bp.block_size; block_weight = bp.block_weight;
cumulative_difficulty = bp.cumulative_difficulty; cumulative_difficulty = bp.cumulative_difficulty;
coins_generated = bp.coins_generated; coins_generated = bp.coins_generated;
try try
{ {
core.get_blockchain_storage().get_db().add_block(b, block_size, cumulative_difficulty, coins_generated, txs); core.get_blockchain_storage().get_db().add_block(b, block_weight, cumulative_difficulty, coins_generated, txs);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

View File

@ -236,11 +236,11 @@ void BootstrapFile::write_block(block& block)
bool include_extra_block_data = true; bool include_extra_block_data = true;
if (include_extra_block_data) if (include_extra_block_data)
{ {
size_t block_size = m_blockchain_storage->get_db().get_block_size(block_height); size_t block_weight = m_blockchain_storage->get_db().get_block_weight(block_height);
difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height); difficulty_type cumulative_difficulty = m_blockchain_storage->get_db().get_block_cumulative_difficulty(block_height);
uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height); uint64_t coins_generated = m_blockchain_storage->get_db().get_block_already_generated_coins(block_height);
bp.block_size = block_size; bp.block_weight = block_weight;
bp.cumulative_difficulty = cumulative_difficulty; bp.cumulative_difficulty = cumulative_difficulty;
bp.coins_generated = coins_generated; bp.coins_generated = coins_generated;
} }

View File

@ -70,14 +70,14 @@ namespace cryptonote
{ {
cryptonote::block block; cryptonote::block block;
std::vector<transaction> txs; std::vector<transaction> txs;
size_t block_size; size_t block_weight;
difficulty_type cumulative_difficulty; difficulty_type cumulative_difficulty;
uint64_t coins_generated; uint64_t coins_generated;
BEGIN_SERIALIZE() BEGIN_SERIALIZE()
FIELD(block) FIELD(block)
FIELD(txs) FIELD(txs)
VARINT_FIELD(block_size) VARINT_FIELD(block_weight)
VARINT_FIELD(cumulative_difficulty) VARINT_FIELD(cumulative_difficulty)
VARINT_FIELD(coins_generated) VARINT_FIELD(coins_generated)
END_SERIALIZE() END_SERIALIZE()

View File

@ -67,7 +67,7 @@ namespace cryptonote {
/* Cryptonote helper functions */ /* Cryptonote helper functions */
/************************************************************************/ /************************************************************************/
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
size_t get_min_block_size(uint8_t version) size_t get_min_block_weight(uint8_t version)
{ {
if (version < 2) if (version < 2)
return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; return CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
@ -86,7 +86,7 @@ namespace cryptonote {
return CRYPTONOTE_MAX_TX_SIZE; return CRYPTONOTE_MAX_TX_SIZE;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) { bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version) {
static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60"); static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60");
const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
const int target_minutes = target / 60; const int target_minutes = target / 60;
@ -98,37 +98,37 @@ namespace cryptonote {
base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes; base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes;
} }
uint64_t full_reward_zone = get_min_block_size(version); uint64_t full_reward_zone = get_min_block_weight(version);
//make it soft //make it soft
if (median_size < full_reward_zone) { if (median_weight < full_reward_zone) {
median_size = full_reward_zone; median_weight = full_reward_zone;
} }
if (current_block_size <= median_size) { if (current_block_weight <= median_weight) {
reward = base_reward; reward = base_reward;
return true; return true;
} }
if(current_block_size > 2 * median_size) { if(current_block_weight > 2 * median_weight) {
MERROR("Block cumulative size is too big: " << current_block_size << ", expected less than " << 2 * median_size); MERROR("Block cumulative weight is too big: " << current_block_weight << ", expected less than " << 2 * median_weight);
return false; return false;
} }
assert(median_size < std::numeric_limits<uint32_t>::max()); assert(median_weight < std::numeric_limits<uint32_t>::max());
assert(current_block_size < std::numeric_limits<uint32_t>::max()); assert(current_block_weight < std::numeric_limits<uint32_t>::max());
uint64_t product_hi; uint64_t product_hi;
// BUGFIX: 32-bit saturation bug (e.g. ARM7), the result was being // BUGFIX: 32-bit saturation bug (e.g. ARM7), the result was being
// treated as 32-bit by default. // treated as 32-bit by default.
uint64_t multiplicand = 2 * median_size - current_block_size; uint64_t multiplicand = 2 * median_weight - current_block_weight;
multiplicand *= current_block_size; multiplicand *= current_block_weight;
uint64_t product_lo = mul128(base_reward, multiplicand, &product_hi); uint64_t product_lo = mul128(base_reward, multiplicand, &product_hi);
uint64_t reward_hi; uint64_t reward_hi;
uint64_t reward_lo; uint64_t reward_lo;
div128_32(product_hi, product_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo); div128_32(product_hi, product_lo, static_cast<uint32_t>(median_weight), &reward_hi, &reward_lo);
div128_32(reward_hi, reward_lo, static_cast<uint32_t>(median_size), &reward_hi, &reward_lo); div128_32(reward_hi, reward_lo, static_cast<uint32_t>(median_weight), &reward_hi, &reward_lo);
assert(0 == reward_hi); assert(0 == reward_hi);
assert(reward_lo < base_reward); assert(reward_lo < base_reward);

View File

@ -86,10 +86,10 @@ namespace cryptonote {
/************************************************************************/ /************************************************************************/
/* Cryptonote helper functions */ /* Cryptonote helper functions */
/************************************************************************/ /************************************************************************/
size_t get_min_block_size(uint8_t version); size_t get_min_block_weight(uint8_t version);
size_t get_max_block_size(); size_t get_max_block_size();
size_t get_max_tx_size(); size_t get_max_tx_size();
bool get_block_reward(size_t median_size, size_t current_block_size, uint64_t already_generated_coins, uint64_t &reward, uint8_t version); bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version);
uint8_t get_account_address_checksum(const public_address_outer_blob& bl); uint8_t get_account_address_checksum(const public_address_outer_blob& bl);
uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl); uint8_t get_account_integrated_address_checksum(const public_integrated_address_outer_blob& bl);

View File

@ -142,24 +142,27 @@ namespace cryptonote
const bool bulletproof = rct::is_rct_bulletproof(rv.type); const bool bulletproof = rct::is_rct_bulletproof(rv.type);
if (bulletproof) if (bulletproof)
{ {
if (rct::n_bulletproof_amounts(rv.p.bulletproofs) != tx.vout.size()) if (rv.p.bulletproofs.size() != 1)
{ {
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx)); LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx));
return false; return false;
} }
size_t idx = 0; if (rv.p.bulletproofs[0].L.size() < 6)
for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n)
{ {
//rv.p.bulletproofs[n].V.resize(1); LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs L size in tx " << get_transaction_hash(tx));
//rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; return false;
CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits
const size_t n_amounts = rct::n_bulletproof_amounts(rv.p.bulletproofs[n]);
CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs[n].V.resize(n_amounts);
rv.p.bulletproofs[n].V.clear();
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[n].V[i] = rv.outPk[idx++].mask;
} }
const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6);
if (max_outputs < tx.vout.size())
{
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs max outputs in tx " << get_transaction_hash(tx));
return false;
}
const size_t n_amounts = tx.vout.size();
CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs[0].V.resize(n_amounts);
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[0].V[i] = rv.outPk[i].mask;
} }
} }
} }
@ -326,6 +329,37 @@ namespace cryptonote
return string_tools::get_xtype_from_string(amount, str_amount); return string_tools::get_xtype_from_string(amount, str_amount);
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size)
{
if (tx.version < 2)
return blob_size;
const rct::rctSig &rv = tx.rct_signatures;
if (!rct::is_rct_bulletproof(rv.type))
return blob_size;
const size_t n_outputs = tx.vout.size();
if (n_outputs <= 2)
return blob_size;
const uint64_t bp_base = 368;
const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs);
size_t nlr = 0;
for (const auto &bp: rv.p.bulletproofs)
nlr += bp.L.size() * 2;
const size_t bp_size = 32 * (9 + nlr);
CHECK_AND_ASSERT_THROW_MES_L1(bp_base * n_padded_outputs >= bp_size, "Invalid bulletproof clawback");
const uint64_t bp_clawback = (bp_base * n_padded_outputs - bp_size) * 4 / 5;
CHECK_AND_ASSERT_THROW_MES_L1(bp_clawback <= std::numeric_limits<uint64_t>::max() - blob_size, "Weight overflow");
return blob_size + bp_clawback;
}
//---------------------------------------------------------------
uint64_t get_transaction_weight(const transaction &tx)
{
std::ostringstream s;
binary_archive<true> a(s);
::serialization::serialize(a, const_cast<transaction&>(tx));
const cryptonote::blobdata blob = s.str();
return get_transaction_weight(tx, blob.size());
}
//---------------------------------------------------------------
bool get_tx_fee(const transaction& tx, uint64_t & fee) bool get_tx_fee(const transaction& tx, uint64_t & fee)
{ {
if (tx.version > 1) if (tx.version > 1)

View File

@ -117,6 +117,8 @@ namespace cryptonote
bool check_inputs_types_supported(const transaction& tx); bool check_inputs_types_supported(const transaction& tx);
bool check_outs_valid(const transaction& tx); bool check_outs_valid(const transaction& tx);
bool parse_amount(uint64_t& amount, const std::string& str_amount); bool parse_amount(uint64_t& amount, const std::string& str_amount);
uint64_t get_transaction_weight(const transaction &tx);
uint64_t get_transaction_weight(const transaction &tx, size_t blob_size);
bool check_money_overflow(const transaction& tx); bool check_money_overflow(const transaction& tx);
bool check_outs_overflow(const transaction& tx); bool check_outs_overflow(const transaction& tx);

View File

@ -65,9 +65,11 @@
#define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10) #define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10)
#define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) #define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9)
#define FEE_PER_BYTE ((uint64_t)300000)
#define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9) #define DYNAMIC_FEE_PER_KB_BASE_FEE ((uint64_t)2000000000) // 2 * pow(10,9)
#define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12) #define DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD ((uint64_t)10000000000000) // 10 * pow(10,12)
#define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5) #define DYNAMIC_FEE_PER_KB_BASE_FEE_V5 ((uint64_t)2000000000 * (uint64_t)CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5)
#define DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT ((uint64_t)3000)
#define ORPHANED_BLOCKS_MAX_COUNT 100 #define ORPHANED_BLOCKS_MAX_COUNT 100
@ -133,13 +135,15 @@
#define HF_VERSION_DYNAMIC_FEE 4 #define HF_VERSION_DYNAMIC_FEE 4
#define HF_VERSION_MIN_MIXIN_4 6 #define HF_VERSION_MIN_MIXIN_4 6
#define HF_VERSION_MIN_MIXIN_6 7 #define HF_VERSION_MIN_MIXIN_6 7
#define HF_VERSION_MIN_MIXIN_10 8
#define HF_VERSION_ENFORCE_RCT 6 #define HF_VERSION_ENFORCE_RCT 6
#define HF_VERSION_PER_BYTE_FEE 8
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8
#define HASH_OF_HASHES_STEP 256 #define HASH_OF_HASHES_STEP 256
#define DEFAULT_TXPOOL_MAX_SIZE 648000000ull // 3 days at 300000, in bytes #define DEFAULT_TXPOOL_MAX_WEIGHT 648000000ull // 3 days at 300000, in bytes
#define BULLETPROOF_MAX_OUTPUTS 16 #define BULLETPROOF_MAX_OUTPUTS 16

View File

@ -155,7 +155,7 @@ static const struct {
//------------------------------------------------------------------ //------------------------------------------------------------------
Blockchain::Blockchain(tx_memory_pool& tx_pool) : Blockchain::Blockchain(tx_memory_pool& tx_pool) :
m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_sz_limit(0), m_current_block_cumul_sz_median(0), m_db(), m_tx_pool(tx_pool), m_hardfork(NULL), m_timestamps_and_difficulties_height(0), m_current_block_cumul_weight_limit(0), m_current_block_cumul_weight_median(0),
m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false), m_enforce_dns_checkpoints(false), m_max_prepare_blocks_threads(4), m_db_sync_on_blocks(true), m_db_sync_threshold(1), m_db_sync_mode(db_async), m_db_default_sync(false), m_fast_sync(true), m_show_time_stats(false), m_sync_counter(0), m_bytes_to_sync(0), m_cancel(false),
m_difficulty_for_next_block_top_hash(crypto::null_hash), m_difficulty_for_next_block_top_hash(crypto::null_hash),
m_difficulty_for_next_block(1), m_difficulty_for_next_block(1),
@ -482,7 +482,7 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
} }
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -631,7 +631,7 @@ block Blockchain::pop_block_from_blockchain()
m_blocks_txs_check.clear(); m_blocks_txs_check.clear();
m_check_txin_table.clear(); m_check_txin_table.clear();
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id()); m_tx_pool.on_blockchain_dec(m_db->height()-1, get_tail_id());
invalidate_block_template_cache(); invalidate_block_template_cache();
@ -650,7 +650,7 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
block_verification_context bvc = boost::value_initialized<block_verification_context>(); block_verification_context bvc = boost::value_initialized<block_verification_context>();
add_new_block(b, bvc); add_new_block(b, bvc);
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -1113,7 +1113,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height)
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
// This function validates the miner transaction reward // This function validates the miner transaction reward
bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version) bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
//validate reward //validate reward
@ -1131,11 +1131,11 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
} }
} }
std::vector<size_t> last_blocks_sizes; std::vector<size_t> last_blocks_weights;
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version)) if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version))
{ {
MERROR_VER("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); MERROR_VER("block weight " << cumulative_block_weight << " is bigger than allowed for this blockchain");
return false; return false;
} }
if(base_reward + fee < money_in_use) if(base_reward + fee < money_in_use)
@ -1165,8 +1165,8 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
// get the block sizes of the last <count> blocks, and return by reference <sz>. // get the block weights of the last <count> blocks, and return by reference <sz>.
void Blockchain::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const void Blockchain::get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock); CRITICAL_REGION_LOCAL(m_blockchain_lock);
@ -1177,26 +1177,26 @@ void Blockchain::get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count)
return; return;
m_db->block_txn_start(true); m_db->block_txn_start(true);
// add size of last <count> blocks to vector <sz> (or less, if blockchain size < count) // add weight of last <count> blocks to vector <weights> (or less, if blockchain size < count)
size_t start_offset = h - std::min<size_t>(h, count); size_t start_offset = h - std::min<size_t>(h, count);
sz.reserve(sz.size() + h - start_offset); weights.reserve(weights.size() + h - start_offset);
for(size_t i = start_offset; i < h; i++) for(size_t i = start_offset; i < h; i++)
{ {
sz.push_back(m_db->get_block_size(i)); weights.push_back(m_db->get_block_weight(i));
} }
m_db->block_txn_stop(); m_db->block_txn_stop();
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_current_cumulative_blocksize_limit() const uint64_t Blockchain::get_current_cumulative_block_weight_limit() const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
return m_current_block_cumul_sz_limit; return m_current_block_cumul_weight_limit;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_current_cumulative_blocksize_median() const uint64_t Blockchain::get_current_cumulative_block_weight_median() const
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
return m_current_block_cumul_sz_median; return m_current_block_cumul_weight_median;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
//TODO: This function only needed minor modification to work with BlockchainDB, //TODO: This function only needed minor modification to work with BlockchainDB,
@ -1213,7 +1213,7 @@ uint64_t Blockchain::get_current_cumulative_blocksize_median() const
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce) bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce)
{ {
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_size; size_t median_weight;
uint64_t already_generated_coins; uint64_t already_generated_coins;
uint64_t pool_cookie; uint64_t pool_cookie;
@ -1250,20 +1250,20 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
diffic = get_difficulty_for_next_block(); diffic = get_difficulty_for_next_block();
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead."); CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
median_size = m_current_block_cumul_sz_limit / 2; median_weight = m_current_block_cumul_weight_limit / 2;
already_generated_coins = m_db->get_block_already_generated_coins(height - 1); already_generated_coins = m_db->get_block_already_generated_coins(height - 1);
CRITICAL_REGION_END(); CRITICAL_REGION_END();
size_t txs_size; size_t txs_weight;
uint64_t fee; uint64_t fee;
if (!m_tx_pool.fill_block_template(b, median_size, already_generated_coins, txs_size, fee, expected_reward, m_hardfork->get_current_version())) if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, m_hardfork->get_current_version()))
{ {
return false; return false;
} }
pool_cookie = m_tx_pool.cookie(); pool_cookie = m_tx_pool.cookie();
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0; size_t real_txs_weight = 0;
uint64_t real_fee = 0; uint64_t real_fee = 0;
CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock); CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock);
for(crypto::hash &cur_hash: b.tx_hashes) for(crypto::hash &cur_hash: b.tx_hashes)
@ -1275,11 +1275,11 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
continue; continue;
} }
tx_memory_pool::tx_details &cur_tx = cur_res->second; tx_memory_pool::tx_details &cur_tx = cur_res->second;
real_txs_size += cur_tx.blob_size; real_txs_weight += cur_tx.weight;
real_fee += cur_tx.fee; real_fee += cur_tx.fee;
if (cur_tx.blob_size != get_object_blobsize(cur_tx.tx)) if (cur_tx.weight != get_transaction_weight(cur_tx.tx))
{ {
LOG_ERROR("Creating block template: error: invalid transaction size"); LOG_ERROR("Creating block template: error: invalid transaction weight");
} }
if (cur_tx.tx.version == 1) if (cur_tx.tx.version == 1)
{ {
@ -1301,9 +1301,9 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
} }
} }
} }
if (txs_size != real_txs_size) if (txs_weight != real_txs_weight)
{ {
LOG_ERROR("Creating block template: error: wrongly calculated transaction size"); LOG_ERROR("Creating block template: error: wrongly calculated transaction weight");
} }
if (fee != real_fee) if (fee != real_fee)
{ {
@ -1311,70 +1311,70 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
} }
CRITICAL_REGION_END(); CRITICAL_REGION_END();
MDEBUG("Creating block template: height " << height << MDEBUG("Creating block template: height " << height <<
", median size " << median_size << ", median weight " << median_weight <<
", already generated coins " << already_generated_coins << ", already generated coins " << already_generated_coins <<
", transaction size " << txs_size << ", transaction weight " << txs_weight <<
", fee " << fee); ", fee " << fee);
#endif #endif
/* /*
two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know two-phase miner transaction generation: we don't know exact block weight until we prepare block, but we don't know reward until we know
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size block weight, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block weight
*/ */
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob size //make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
uint8_t hf_version = m_hardfork->get_current_version(); uint8_t hf_version = m_hardfork->get_current_version();
size_t max_outs = hf_version >= 4 ? 1 : 11; size_t max_outs = hf_version >= 4 ? 1 : 11;
bool r = construct_miner_tx(height, median_size, already_generated_coins, txs_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
size_t cumulative_size = txs_size + get_object_blobsize(b.miner_tx); size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) << MDEBUG("Creating block template: miner tx weight " << get_transaction_weight(b.miner_tx) <<
", cumulative size " << cumulative_size); ", cumulative weight " << cumulative_weight);
#endif #endif
for (size_t try_count = 0; try_count != 10; ++try_count) for (size_t try_count = 0; try_count != 10; ++try_count)
{ {
r = construct_miner_tx(height, median_size, already_generated_coins, cumulative_size, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version); r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance"); CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); size_t coinbase_weight = get_transaction_weight(b.miner_tx);
if (coinbase_blob_size > cumulative_size - txs_size) if (coinbase_weight > cumulative_weight - txs_weight)
{ {
cumulative_size = txs_size + coinbase_blob_size; cumulative_weight = txs_weight + coinbase_weight;
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative size " << cumulative_size << " is greater than before"); ", cumulative weight " << cumulative_weight << " is greater than before");
#endif #endif
continue; continue;
} }
if (coinbase_blob_size < cumulative_size - txs_size) if (coinbase_weight < cumulative_weight - txs_weight)
{ {
size_t delta = cumulative_size - txs_size - coinbase_blob_size; size_t delta = cumulative_weight - txs_weight - coinbase_weight;
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative size " << txs_size + coinbase_blob_size << ", cumulative weight " << txs_weight + coinbase_weight <<
" is less than before, adding " << delta << " zero bytes"); " is less than before, adding " << delta << " zero bytes");
#endif #endif
b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0);
//here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len.
if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) if (cumulative_weight != txs_weight + get_transaction_weight(b.miner_tx))
{ {
CHECK_AND_ASSERT_MES(cumulative_size + 1 == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); CHECK_AND_ASSERT_MES(cumulative_weight + 1 == txs_weight + get_transaction_weight(b.miner_tx), false, "unexpected case: cumulative_weight=" << cumulative_weight << " + 1 is not equal txs_cumulative_weight=" << txs_weight << " + get_transaction_weight(b.miner_tx)=" << get_transaction_weight(b.miner_tx));
b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1); b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1);
if (cumulative_size != txs_size + get_object_blobsize(b.miner_tx)) if (cumulative_weight != txs_weight + get_transaction_weight(b.miner_tx))
{ {
//fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size //fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_weight
MDEBUG("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1); MDEBUG("Miner tx creation has no luck with delta_extra size = " << delta << " and " << delta - 1);
cumulative_size += delta - 1; cumulative_weight += delta - 1;
continue; continue;
} }
MDEBUG("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count); MDEBUG("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count);
} }
} }
CHECK_AND_ASSERT_MES(cumulative_size == txs_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx)); CHECK_AND_ASSERT_MES(cumulative_weight == txs_weight + get_transaction_weight(b.miner_tx), false, "unexpected case: cumulative_weight=" << cumulative_weight << " is not equal txs_cumulative_weight=" << txs_weight << " + get_transaction_weight(b.miner_tx)=" << get_transaction_weight(b.miner_tx));
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE) #if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size << MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative size " << cumulative_size << " is now good"); ", cumulative weight " << cumulative_weight << " is now good");
#endif #endif
cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie); cache_block_template(b, miner_address, ex_nonce, diffic, expected_reward, pool_cookie);
@ -2540,7 +2540,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_heigh
if(m_show_time_stats) if(m_show_time_stats)
{ {
size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0; size_t ring_size = !tx.vin.empty() && tx.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(tx.vin[0]).key_offsets.size() : 0;
MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx)); MINFO("HASH: " << get_transaction_hash(tx) << " I/M/O: " << tx.vin.size() << "/" << ring_size << "/" << tx.vout.size() << " H: " << max_used_block_height << " ms: " << a + m_fake_scan_time << " B: " << get_object_blobsize(tx) << " W: " << get_transaction_weight(tx));
} }
if (!res) if (!res)
return false; return false;
@ -2597,6 +2597,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// from v8, allow bulletproofs // from v8, allow bulletproofs
if (hf_version < 8) { if (hf_version < 8) {
if (tx.version >= 2) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
{ {
@ -2605,6 +2606,20 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
return false; return false;
} }
} }
}
// from v9, forbid borromean range proofs
if (hf_version > 8) {
if (tx.version >= 2) {
const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type);
if (borromean)
{
MERROR("Borromean range proofs are not allowed after v8");
tvc.m_invalid_output = true;
return false;
}
}
}
return true; return true;
} }
@ -2714,7 +2729,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
{ {
size_t n_unmixable = 0, n_mixable = 0; size_t n_unmixable = 0, n_mixable = 0;
size_t mixin = std::numeric_limits<size_t>::max(); size_t mixin = std::numeric_limits<size_t>::max();
const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2;
for (const auto& txin : tx.vin) for (const auto& txin : tx.vin)
{ {
// non txin_to_key inputs will be rejected below // non txin_to_key inputs will be rejected below
@ -2743,6 +2758,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
} }
} }
if (hf_version >= HF_VERSION_MIN_MIXIN_10 && mixin != 10)
{
MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11");
tvc.m_low_mixin = true;
return false;
}
if (mixin < min_mixin) if (mixin < min_mixin)
{ {
if (n_unmixable == 0) if (n_unmixable == 0)
@ -3093,7 +3115,7 @@ void Blockchain::check_ring_signature(const crypto::hash &tx_prefix_hash, const
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
static uint64_t get_fee_quantization_mask() uint64_t Blockchain::get_fee_quantization_mask()
{ {
static uint64_t mask = 0; static uint64_t mask = 0;
if (mask == 0) if (mask == 0)
@ -3106,16 +3128,27 @@ static uint64_t get_fee_quantization_mask()
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version) uint64_t Blockchain::get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version)
{ {
const uint64_t min_block_size = get_min_block_size(version); const uint64_t min_block_weight = get_min_block_weight(version);
const uint64_t fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE; if (median_block_weight < min_block_weight)
median_block_weight = min_block_weight;
uint64_t hi, lo;
if (median_block_size < min_block_size) if (version >= HF_VERSION_PER_BYTE_FEE)
median_block_size = min_block_size; {
lo = mul128(block_reward, DYNAMIC_FEE_REFERENCE_TRANSACTION_WEIGHT, &hi);
div128_32(hi, lo, min_block_weight, &hi, &lo);
div128_32(hi, lo, median_block_weight, &hi, &lo);
assert(hi == 0);
lo /= 5;
return lo;
}
uint64_t unscaled_fee_per_kb = (fee_per_kb_base * min_block_size / median_block_size); const uint64_t fee_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE;
uint64_t hi, lo = mul128(unscaled_fee_per_kb, block_reward, &hi);
uint64_t unscaled_fee_base = (fee_base * min_block_weight / median_block_weight);
lo = mul128(unscaled_fee_base, block_reward, &hi);
static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000"); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD % 1000000 == 0, "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD must be divisible by 1000000");
static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large"); static_assert(DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD / 1000000 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large");
@ -3133,10 +3166,33 @@ uint64_t Blockchain::get_dynamic_per_kb_fee(uint64_t block_reward, size_t median
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const bool Blockchain::check_fee(size_t tx_weight, uint64_t fee) const
{ {
const uint8_t version = get_current_hard_fork_version(); const uint8_t version = get_current_hard_fork_version();
uint64_t median = 0;
uint64_t already_generated_coins = 0;
uint64_t base_reward = 0;
if (version >= HF_VERSION_DYNAMIC_FEE)
{
median = m_current_block_cumul_weight_limit / 2;
already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false;
}
uint64_t needed_fee;
if (version >= HF_VERSION_PER_BYTE_FEE)
{
uint64_t fee_per_byte = get_dynamic_base_fee(base_reward, median, version);
MDEBUG("Using " << print_money(fee_per_byte) << "/byte fee");
needed_fee = tx_weight * fee_per_byte;
// quantize fee up to 8 decimals
const uint64_t mask = get_fee_quantization_mask();
needed_fee = (needed_fee + mask - 1) / mask * mask;
}
else
{
uint64_t fee_per_kb; uint64_t fee_per_kb;
if (version < HF_VERSION_DYNAMIC_FEE) if (version < HF_VERSION_DYNAMIC_FEE)
{ {
@ -3144,18 +3200,14 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const
} }
else else
{ {
uint64_t median = m_current_block_cumul_sz_limit / 2; fee_per_kb = get_dynamic_base_fee(base_reward, median, version);
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
uint64_t base_reward;
if (!get_block_reward(median, 1, already_generated_coins, base_reward, version))
return false;
fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version);
} }
MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee"); MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee");
uint64_t needed_fee = blob_size / 1024; needed_fee = tx_weight / 1024;
needed_fee += (blob_size % 1024) ? 1 : 0; needed_fee += (tx_weight % 1024) ? 1 : 0;
needed_fee *= fee_per_kb; needed_fee *= fee_per_kb;
}
if (fee < needed_fee - needed_fee / 50) // keep a little 2% buffer on acceptance - no integer overflow if (fee < needed_fee - needed_fee / 50) // keep a little 2% buffer on acceptance - no integer overflow
{ {
@ -3166,7 +3218,7 @@ bool Blockchain::check_fee(size_t blob_size, uint64_t fee) const
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) const uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{ {
const uint8_t version = get_current_hard_fork_version(); const uint8_t version = get_current_hard_fork_version();
@ -3176,16 +3228,16 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW) if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
const uint64_t min_block_size = get_min_block_size(version); const uint64_t min_block_weight = get_min_block_weight(version);
std::vector<size_t> sz; std::vector<size_t> weights;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
sz.reserve(grace_blocks); weights.reserve(grace_blocks);
for (size_t i = 0; i < grace_blocks; ++i) for (size_t i = 0; i < grace_blocks; ++i)
sz.push_back(min_block_size); weights.push_back(min_block_weight);
uint64_t median = epee::misc_utils::median(sz); uint64_t median = epee::misc_utils::median(weights);
if(median <= min_block_size) if(median <= min_block_weight)
median = min_block_size; median = min_block_weight;
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
uint64_t base_reward; uint64_t base_reward;
@ -3195,8 +3247,9 @@ uint64_t Blockchain::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) cons
base_reward = BLOCK_REWARD_OVERESTIMATE; base_reward = BLOCK_REWARD_OVERESTIMATE;
} }
uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version); uint64_t fee = get_dynamic_base_fee(base_reward, median, version);
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB"); const bool per_byte = version < HF_VERSION_PER_BYTE_FEE;
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/" << (per_byte ? "byte" : "kB"));
return fee; return fee;
} }
@ -3372,11 +3425,11 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
for (const auto &txid: txids) for (const auto &txid: txids)
{ {
cryptonote::transaction tx; cryptonote::transaction tx;
size_t blob_size; size_t tx_weight;
uint64_t fee; uint64_t fee;
bool relayed, do_not_relay, double_spend_seen; bool relayed, do_not_relay, double_spend_seen;
MINFO("Removing txid " << txid << " from the pool"); MINFO("Removing txid " << txid << " from the pool");
if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) if(m_tx_pool.have_tx(txid) && !m_tx_pool.take_tx(txid, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
{ {
MERROR("Failed to remove txid " << txid << " from the pool"); MERROR("Failed to remove txid " << txid << " from the pool");
res = false; res = false;
@ -3536,8 +3589,8 @@ leave:
goto leave; goto leave;
} }
size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); size_t coinbase_weight = get_transaction_weight(bl.miner_tx);
size_t cumulative_block_size = coinbase_blob_size; size_t cumulative_block_weight = coinbase_weight;
std::vector<transaction> txs; std::vector<transaction> txs;
key_images_container keys; key_images_container keys;
@ -3559,7 +3612,7 @@ leave:
for (const crypto::hash& tx_id : bl.tx_hashes) for (const crypto::hash& tx_id : bl.tx_hashes)
{ {
transaction tx; transaction tx;
size_t blob_size = 0; size_t tx_weight = 0;
uint64_t fee = 0; uint64_t fee = 0;
bool relayed = false, do_not_relay = false, double_spend_seen = false; bool relayed = false, do_not_relay = false, double_spend_seen = false;
TIME_MEASURE_START(aa); TIME_MEASURE_START(aa);
@ -3578,7 +3631,7 @@ leave:
TIME_MEASURE_START(bb); TIME_MEASURE_START(bb);
// get transaction with hash <tx_id> from tx_pool // get transaction with hash <tx_id> from tx_pool
if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee, relayed, do_not_relay, double_spend_seen)) if(!m_tx_pool.take_tx(tx_id, tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen))
{ {
MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id); MERROR_VER("Block with id: " << id << " has at least one unknown transaction with id: " << tx_id);
bvc.m_verifivation_failed = true; bvc.m_verifivation_failed = true;
@ -3649,7 +3702,7 @@ leave:
TIME_MEASURE_FINISH(cc); TIME_MEASURE_FINISH(cc);
t_checktx += cc; t_checktx += cc;
fee_summary += fee; fee_summary += fee;
cumulative_block_size += blob_size; cumulative_block_weight += tx_weight;
} }
m_blocks_txs_check.clear(); m_blocks_txs_check.clear();
@ -3657,7 +3710,7 @@ leave:
TIME_MEASURE_START(vmt); TIME_MEASURE_START(vmt);
uint64_t base_reward = 0; uint64_t base_reward = 0;
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0; uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version())) if(!validate_miner_transaction(bl, cumulative_block_weight, fee_summary, base_reward, already_generated_coins, bvc.m_partial_block_reward, m_hardfork->get_current_version()))
{ {
MERROR_VER("Block with id: " << id << " has incorrect miner transaction"); MERROR_VER("Block with id: " << id << " has incorrect miner transaction");
bvc.m_verifivation_failed = true; bvc.m_verifivation_failed = true;
@ -3666,11 +3719,11 @@ leave:
} }
TIME_MEASURE_FINISH(vmt); TIME_MEASURE_FINISH(vmt);
size_t block_size; size_t block_weight;
difficulty_type cumulative_difficulty; difficulty_type cumulative_difficulty;
// populate various metadata about the block to be stored alongside it. // populate various metadata about the block to be stored alongside it.
block_size = cumulative_block_size; block_weight = cumulative_block_weight;
cumulative_difficulty = current_diffic; cumulative_difficulty = current_diffic;
// In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of // In the "tail" state when the minimum subsidy (implemented in get_block_reward) is in effect, the number of
// coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins // coins will eventually exceed MONEY_SUPPLY and overflow a uint64. To prevent overflow, cap already_generated_coins
@ -3691,7 +3744,7 @@ leave:
{ {
try try
{ {
new_height = m_db->add_block(bl, block_size, cumulative_difficulty, already_generated_coins, txs); new_height = m_db->add_block(bl, block_weight, cumulative_difficulty, already_generated_coins, txs);
} }
catch (const KEY_IMAGE_EXISTS& e) catch (const KEY_IMAGE_EXISTS& e)
{ {
@ -3716,14 +3769,14 @@ leave:
TIME_MEASURE_FINISH(addblock); TIME_MEASURE_FINISH(addblock);
// do this after updating the hard fork state since the size limit may change due to fork // do this after updating the hard fork state since the weight limit may change due to fork
update_next_cumulative_size_limit(); update_next_cumulative_weight_limit();
MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms"); MINFO("+++++ BLOCK SUCCESSFULLY ADDED" << std::endl << "id:\t" << id << std::endl << "PoW:\t" << proof_of_work << std::endl << "HEIGHT " << new_height-1 << ", difficulty:\t" << current_diffic << std::endl << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) << "), coinbase_weight: " << coinbase_weight << ", cumulative weight: " << cumulative_block_weight << ", " << block_processing_time << "(" << target_calculating_time << "/" << longhash_calculating_time << ")ms");
if(m_show_time_stats) if(m_show_time_stats)
{ {
MINFO("Height: " << new_height << " blob: " << coinbase_blob_size << " cumm: " MINFO("Height: " << new_height << " coinbase weight: " << coinbase_weight << " cumm: "
<< cumulative_block_size << " p/t: " << block_processing_time << " (" << cumulative_block_weight << " p/t: " << block_processing_time << " ("
<< target_calculating_time << "/" << longhash_calculating_time << "/" << target_calculating_time << "/" << longhash_calculating_time << "/"
<< t1 << "/" << t2 << "/" << t3 << "/" << t_exists << "/" << t_pool << t1 << "/" << t2 << "/" << t3 << "/" << t_exists << "/" << t_pool
<< "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms"); << "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms");
@ -3740,20 +3793,20 @@ leave:
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
bool Blockchain::update_next_cumulative_size_limit() bool Blockchain::update_next_cumulative_weight_limit()
{ {
uint64_t full_reward_zone = get_min_block_size(get_current_hard_fork_version()); uint64_t full_reward_zone = get_min_block_weight(get_current_hard_fork_version());
LOG_PRINT_L3("Blockchain::" << __func__); LOG_PRINT_L3("Blockchain::" << __func__);
std::vector<size_t> sz; std::vector<size_t> weights;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
uint64_t median = epee::misc_utils::median(sz); uint64_t median = epee::misc_utils::median(weights);
m_current_block_cumul_sz_median = median; m_current_block_cumul_weight_median = median;
if(median <= full_reward_zone) if(median <= full_reward_zone)
median = full_reward_zone; median = full_reward_zone;
m_current_block_cumul_sz_limit = median*2; m_current_block_cumul_weight_limit = median*2;
return true; return true;
} }
//------------------------------------------------------------------ //------------------------------------------------------------------
@ -4650,14 +4703,14 @@ void Blockchain::load_compiled_in_block_hashes()
std::vector<transaction> txs; std::vector<transaction> txs;
m_tx_pool.get_transactions(txs); m_tx_pool.get_transactions(txs);
size_t blob_size; size_t tx_weight;
uint64_t fee; uint64_t fee;
bool relayed, do_not_relay, double_spend_seen; bool relayed, do_not_relay, double_spend_seen;
transaction pool_tx; transaction pool_tx;
for(const transaction &tx : txs) for(const transaction &tx : txs)
{ {
crypto::hash tx_hash = get_transaction_hash(tx); crypto::hash tx_hash = get_transaction_hash(tx);
m_tx_pool.take_tx(tx_hash, pool_tx, blob_size, fee, relayed, do_not_relay, double_spend_seen); m_tx_pool.take_tx(tx_hash, pool_tx, tx_weight, fee, relayed, do_not_relay, double_spend_seen);
} }
} }
} }

View File

@ -95,7 +95,7 @@ namespace cryptonote
{ {
block bl; //!< the block block bl; //!< the block
uint64_t height; //!< the height of the block in the blockchain uint64_t height; //!< the height of the block in the blockchain
size_t block_cumulative_size; //!< the size (in bytes) of the block size_t block_cumulative_weight; //!< the weight of the block
difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block
uint64_t already_generated_coins; //!< the total coins minted after that block uint64_t already_generated_coins; //!< the total coins minted after that block
}; };
@ -579,46 +579,57 @@ namespace cryptonote
bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false);
/** /**
* @brief get dynamic per kB fee for a given block size * @brief get fee quantization mask
* *
* The dynamic fee is based on the block size in a past window, and * The dynamic fee may be quantized, to mask out the last decimal places
* the current block reward. It is expressed by kB.
* *
* @param block_reward the current block reward * @return the fee quantized mask
* @param median_block_size the median blob's size in the past window
* @param version hard fork version for rules and constants to use
*
* @return the per kB fee
*/ */
static uint64_t get_dynamic_per_kb_fee(uint64_t block_reward, size_t median_block_size, uint8_t version); static uint64_t get_fee_quantization_mask();
/** /**
* @brief get dynamic per kB fee estimate for the next few blocks * @brief get dynamic per kB or byte fee for a given block weight
* *
* The dynamic fee is based on the block size in a past window, and * The dynamic fee is based on the block weight in a past window, and
* the current block reward. It is expressed by kB. This function * the current block reward. It is expressed by kB before v8, and
* calculates an estimate for a dynamic fee which will be valid for * per byte from v8.
* the next grace_blocks *
* @param block_reward the current block reward
* @param median_block_weight the median block weight in the past window
* @param version hard fork version for rules and constants to use
*
* @return the fee
*/
static uint64_t get_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version);
/**
* @brief get dynamic per kB or byte fee estimate for the next few blocks
*
* The dynamic fee is based on the block weight in a past window, and
* the current block reward. It is expressed by kB before v8, and
* per byte from v8.
* This function calculates an estimate for a dynamic fee which will be
* valid for the next grace_blocks
* *
* @param grace_blocks number of blocks we want the fee to be valid for * @param grace_blocks number of blocks we want the fee to be valid for
* *
* @return the per kB fee estimate * @return the fee estimate
*/ */
uint64_t get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks) const; uint64_t get_dynamic_base_fee_estimate(uint64_t grace_blocks) const;
/** /**
* @brief validate a transaction's fee * @brief validate a transaction's fee
* *
* This function validates the fee is enough for the transaction. * This function validates the fee is enough for the transaction.
* This is based on the size of the transaction blob, and, after a * This is based on the weight of the transaction, and, after a
* height threshold, on the average size of transaction in a past window * height threshold, on the average weight of transaction in a past window
* *
* @param blob_size the transaction blob's size * @param tx_weight the transaction weight
* @param fee the fee * @param fee the fee
* *
* @return true if the fee is enough, false otherwise * @return true if the fee is enough, false otherwise
*/ */
bool check_fee(size_t blob_size, uint64_t fee) const; bool check_fee(size_t tx_weight, uint64_t fee) const;
/** /**
* @brief check that a transaction's outputs conform to current standards * @brief check that a transaction's outputs conform to current standards
@ -635,18 +646,18 @@ namespace cryptonote
bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc); bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc);
/** /**
* @brief gets the blocksize limit based on recent blocks * @brief gets the block weight limit based on recent blocks
* *
* @return the limit * @return the limit
*/ */
uint64_t get_current_cumulative_blocksize_limit() const; uint64_t get_current_cumulative_block_weight_limit() const;
/** /**
* @brief gets the blocksize median based on recent blocks (same window as for the limit) * @brief gets the block weight median based on recent blocks (same window as for the limit)
* *
* @return the median * @return the median
*/ */
uint64_t get_current_cumulative_blocksize_median() const; uint64_t get_current_cumulative_block_weight_median() const;
/** /**
* @brief gets the difficulty of the block with a given height * @brief gets the difficulty of the block with a given height
@ -1001,8 +1012,8 @@ namespace cryptonote
// main chain // main chain
transactions_container m_transactions; transactions_container m_transactions;
size_t m_current_block_cumul_sz_limit; size_t m_current_block_cumul_weight_limit;
size_t m_current_block_cumul_sz_median; size_t m_current_block_cumul_weight_median;
// metadata containers // metadata containers
std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table; std::unordered_map<crypto::hash, std::unordered_map<crypto::key_image, std::vector<output_data_t>>> m_scan_table;
@ -1225,7 +1236,7 @@ namespace cryptonote
* and that his miner transaction totals reward + fee. * and that his miner transaction totals reward + fee.
* *
* @param b the block containing the miner transaction to be validated * @param b the block containing the miner transaction to be validated
* @param cumulative_block_size the block's size * @param cumulative_block_weight the block's weight
* @param fee the total fees collected in the block * @param fee the total fees collected in the block
* @param base_reward return-by-reference the new block's generated coins * @param base_reward return-by-reference the new block's generated coins
* @param already_generated_coins the amount of currency generated prior to this block * @param already_generated_coins the amount of currency generated prior to this block
@ -1234,7 +1245,7 @@ namespace cryptonote
* *
* @return false if anything is found wrong with the miner transaction, otherwise true * @return false if anything is found wrong with the miner transaction, otherwise true
*/ */
bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version); bool validate_miner_transaction(const block& b, size_t cumulative_block_weight, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins, bool &partial_block_reward, uint8_t version);
/** /**
* @brief reverts the blockchain to its previous state following a failed switch * @brief reverts the blockchain to its previous state following a failed switch
@ -1251,14 +1262,14 @@ namespace cryptonote
bool rollback_blockchain_switching(std::list<block>& original_chain, uint64_t rollback_height); bool rollback_blockchain_switching(std::list<block>& original_chain, uint64_t rollback_height);
/** /**
* @brief gets recent block sizes for median calculation * @brief gets recent block weights for median calculation
* *
* get the block sizes of the last <count> blocks, and return by reference <sz>. * get the block weights of the last <count> blocks, and return by reference <sz>.
* *
* @param sz return-by-reference the list of sizes * @param sz return-by-reference the list of weights
* @param count the number of blocks to get sizes for * @param count the number of blocks to get weights for
*/ */
void get_last_n_blocks_sizes(std::vector<size_t>& sz, size_t count) const; void get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const;
/** /**
* @brief adds the given output to the requested set of random outputs * @brief adds the given output to the requested set of random outputs
@ -1373,11 +1384,11 @@ namespace cryptonote
bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps); bool complete_timestamps_vector(uint64_t start_height, std::vector<uint64_t>& timestamps);
/** /**
* @brief calculate the block size limit for the next block to be added * @brief calculate the block weight limit for the next block to be added
* *
* @return true * @return true
*/ */
bool update_next_cumulative_size_limit(); bool update_next_cumulative_weight_limit();
void return_tx_to_pool(std::vector<transaction> &txs); void return_tx_to_pool(std::vector<transaction> &txs);
/** /**

View File

@ -162,10 +162,10 @@ namespace cryptonote
, "Relay blocks as normal blocks" , "Relay blocks as normal blocks"
, false , false
}; };
static const command_line::arg_descriptor<size_t> arg_max_txpool_size = { static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
"max-txpool-size" "max-txpool-weight"
, "Set maximum txpool size in bytes." , "Set maximum txpool weight in bytes."
, DEFAULT_TXPOOL_MAX_SIZE , DEFAULT_TXPOOL_MAX_WEIGHT
}; };
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
@ -274,7 +274,7 @@ namespace cryptonote
command_line::add_arg(desc, arg_test_dbg_lock_sleep); command_line::add_arg(desc, arg_test_dbg_lock_sleep);
command_line::add_arg(desc, arg_offline); command_line::add_arg(desc, arg_offline);
command_line::add_arg(desc, arg_disable_dns_checkpoints); command_line::add_arg(desc, arg_disable_dns_checkpoints);
command_line::add_arg(desc, arg_max_txpool_size); command_line::add_arg(desc, arg_max_txpool_weight);
miner::init_options(desc); miner::init_options(desc);
BlockchainDB::init_options(desc); BlockchainDB::init_options(desc);
@ -402,7 +402,7 @@ namespace cryptonote
bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0; bool fast_sync = command_line::get_arg(vm, arg_fast_block_sync) != 0;
uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads); uint64_t blocks_threads = command_line::get_arg(vm, arg_prep_blocks_threads);
std::string check_updates_string = command_line::get_arg(vm, arg_check_updates); std::string check_updates_string = command_line::get_arg(vm, arg_check_updates);
size_t max_txpool_size = command_line::get_arg(vm, arg_max_txpool_size); size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight);
boost::filesystem::path folder(m_config_folder); boost::filesystem::path folder(m_config_folder);
if (m_nettype == FAKECHAIN) if (m_nettype == FAKECHAIN)
@ -551,7 +551,7 @@ namespace cryptonote
const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty); const difficulty_type fixed_difficulty = command_line::get_arg(vm, arg_fixed_difficulty);
r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty); r = m_blockchain_storage.init(db.release(), m_nettype, m_offline, regtest ? &regtest_test_options : test_options, fixed_difficulty);
r = m_mempool.init(max_txpool_size); r = m_mempool.init(max_txpool_weight);
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool");
// now that we have a valid m_blockchain_storage, we can clean out any // now that we have a valid m_blockchain_storage, we can clean out any
@ -692,43 +692,6 @@ namespace cryptonote
return false; return false;
} }
// resolve outPk references in rct txes
// outPk aren't the only thing that need resolving for a fully resolved tx,
// but outPk (1) are needed now to check range proof semantics, and
// (2) do not need access to the blockchain to find data
if (tx.version >= 2)
{
rct::rctSig &rv = tx.rct_signatures;
if (rv.outPk.size() != tx.vout.size())
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad outPk size in tx " << tx_hash << ", rejected");
tvc.m_verifivation_failed = true;
return false;
}
for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
if (bulletproof)
{
if (rct::n_bulletproof_amounts(rv.p.bulletproofs) != tx.vout.size())
{
LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected");
tvc.m_verifivation_failed = true;
return false;
}
size_t idx = 0;
for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n)
{
CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits
const size_t n_amounts = rct::n_bulletproof_amounts(rv.p.bulletproofs[n]);
CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs[n].V.clear();
for (size_t i = 0; i < n_amounts; ++i)
rv.p.bulletproofs[n].V.push_back(rv.outPk[idx++].mask);
}
}
}
return true; return true;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
@ -747,20 +710,11 @@ namespace cryptonote
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
static bool is_canonical_bulletproof_layout(const std::vector<rct::Bulletproof> &proofs) static bool is_canonical_bulletproof_layout(const std::vector<rct::Bulletproof> &proofs)
{ {
size_t n_amounts = rct::n_bulletproof_amounts(proofs), amounts_proved = 0; if (proofs.size() != 1)
size_t n = 0;
while (amounts_proved < n_amounts)
{
if (n >= proofs.size())
return false; return false;
size_t batch_size = 1; const size_t sz = proofs[0].V.size();
while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS) if (sz == 0 || sz > BULLETPROOF_MAX_OUTPUTS)
batch_size *= 2;
if (rct::n_bulletproof_amounts(proofs[n]) != batch_size)
return false; return false;
amounts_proved += batch_size;
++n;
}
return true; return true;
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
@ -816,11 +770,9 @@ namespace cryptonote
} }
break; break;
case rct::RCTTypeBulletproof: case rct::RCTTypeBulletproof:
// in addition to valid bulletproofs, we want multi-out
// proofs to be in decreasing power of 2 constituents
if (!is_canonical_bulletproof_layout(rv.p.bulletproofs)) if (!is_canonical_bulletproof_layout(rv.p.bulletproofs))
{ {
MERROR_VER("Bulletproof does not use decreasing power of 2 rule"); MERROR_VER("Bulletproof does not have canonical form");
set_semantics_failed(tx_info[n].tx_hash); set_semantics_failed(tx_info[n].tx_hash);
tx_info[n].tvc.m_verifivation_failed = true; tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false; tx_info[n].result = false;
@ -933,7 +885,8 @@ namespace cryptonote
continue; continue;
} }
ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, it->size(), tvc[i], keeped_by_block, relayed, do_not_relay); const size_t weight = get_transaction_weight(results[i].tx, it->size());
ok &= add_new_tx(results[i].tx, results[i].hash, results[i].prefix_hash, weight, tvc[i], keeped_by_block, relayed, do_not_relay);
if(tvc[i].m_verifivation_failed) if(tvc[i].m_verifivation_failed)
{MERROR_VER("Transaction verification failed: " << results[i].hash);} {MERROR_VER("Transaction verification failed: " << results[i].hash);}
else if(tvc[i].m_verifivation_impossible) else if(tvc[i].m_verifivation_impossible)
@ -1016,9 +969,9 @@ namespace cryptonote
} }
// for version > 1, ringct signatures check verifies amounts match // for version > 1, ringct signatures check verifies amounts match
if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) if(!keeped_by_block && get_transaction_weight(tx) >= m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE)
{ {
MERROR_VER("tx is too large " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); MERROR_VER("tx is too large " << get_transaction_weight(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_cumulative_block_weight_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE);
return false; return false;
} }
@ -1150,7 +1103,8 @@ namespace cryptonote
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
blobdata bl; blobdata bl;
t_serializable_object_to_blob(tx, bl); t_serializable_object_to_blob(tx, bl);
return add_new_tx(tx, tx_hash, tx_prefix_hash, bl.size(), tvc, keeped_by_block, relayed, do_not_relay); size_t tx_weight = get_transaction_weight(tx, bl.size());
return add_new_tx(tx, tx_hash, tx_prefix_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
size_t core::get_blockchain_total_transactions() const size_t core::get_blockchain_total_transactions() const
@ -1158,7 +1112,7 @@ namespace cryptonote
return m_blockchain_storage.get_total_transactions(); return m_blockchain_storage.get_total_transactions();
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay) bool core::add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay)
{ {
if (keeped_by_block) if (keeped_by_block)
get_blockchain_storage().on_new_tx_from_block(tx); get_blockchain_storage().on_new_tx_from_block(tx);
@ -1176,7 +1130,7 @@ namespace cryptonote
} }
uint8_t version = m_blockchain_storage.get_current_hard_fork_version(); uint8_t version = m_blockchain_storage.get_current_hard_fork_version();
return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); return m_mempool.add_tx(tx, tx_hash, tx_weight, tvc, keeped_by_block, relayed, do_not_relay, version);
} }
//----------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------
bool core::relay_txpool_transactions() bool core::relay_txpool_transactions()

View File

@ -788,12 +788,12 @@ namespace cryptonote
* *
* @param tx_hash the transaction's hash * @param tx_hash the transaction's hash
* @param tx_prefix_hash the transaction prefix' hash * @param tx_prefix_hash the transaction prefix' hash
* @param blob_size the size of the transaction * @param tx_weight the weight of the transaction
* @param relayed whether or not the transaction was relayed to us * @param relayed whether or not the transaction was relayed to us
* @param do_not_relay whether to prevent the transaction from being relayed * @param do_not_relay whether to prevent the transaction from being relayed
* *
*/ */
bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay); bool add_new_tx(transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t tx_weight, tx_verification_context& tvc, bool keeped_by_block, bool relayed, bool do_not_relay);
/** /**
* @brief add a new transaction to the transaction pool * @brief add a new transaction to the transaction pool

View File

@ -74,7 +74,7 @@ namespace cryptonote
LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses"); LOG_PRINT_L2("destinations include " << num_stdaddresses << " standard addresses and " << num_subaddresses << " subaddresses");
} }
//--------------------------------------------------------------- //---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) { bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
tx.vin.clear(); tx.vin.clear();
tx.vout.clear(); tx.vout.clear();
tx.extra.clear(); tx.extra.clear();
@ -89,7 +89,7 @@ namespace cryptonote
in.height = height; in.height = height;
uint64_t block_reward; uint64_t block_reward;
if(!get_block_reward(median_size, current_block_size, already_generated_coins, block_reward, hard_fork_version)) if(!get_block_reward(median_weight, current_block_weight, already_generated_coins, block_reward, hard_fork_version))
{ {
LOG_PRINT_L0("Block is too big"); LOG_PRINT_L0("Block is too big");
return false; return false;
@ -491,7 +491,7 @@ namespace cryptonote
// the non-simple version is slightly smaller, but assumes all real inputs // the non-simple version is slightly smaller, but assumes all real inputs
// are on the same index, so can only be used if there just one ring. // are on the same index, so can only be used if there just one ring.
bool use_simple_rct = sources.size() > 1 || range_proof_type == rct::RangeProofMultiOutputBulletproof || range_proof_type == rct::RangeProofBulletproof; bool use_simple_rct = sources.size() > 1 || range_proof_type != rct::RangeProofBorromean;
if (!use_simple_rct) if (!use_simple_rct)
{ {

View File

@ -37,7 +37,7 @@
namespace cryptonote namespace cryptonote
{ {
//--------------------------------------------------------------- //---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, uint64_t already_generated_coins, size_t current_block_size, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1); bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
struct tx_source_entry struct tx_source_entry
{ {

View File

@ -80,9 +80,13 @@ namespace cryptonote
return amount * ACCEPT_THRESHOLD; return amount * ACCEPT_THRESHOLD;
} }
uint64_t get_transaction_size_limit(uint8_t version) uint64_t get_transaction_weight_limit(uint8_t version)
{ {
return get_min_block_size(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; // from v8, limit a tx to 50% of the minimum block weight
if (version >= 8)
return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
else
return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
} }
// This class is meant to create a batch when none currently exists. // This class is meant to create a batch when none currently exists.
@ -102,12 +106,12 @@ namespace cryptonote
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_size(DEFAULT_TXPOOL_MAX_SIZE), m_txpool_size(0), m_cookie(0) tx_memory_pool::tx_memory_pool(Blockchain& bchs): m_blockchain(bchs), m_txpool_max_weight(DEFAULT_TXPOOL_MAX_WEIGHT), m_txpool_weight(0), m_cookie(0)
{ {
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version) bool tx_memory_pool::add_tx(transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version)
{ {
// this should already be called with that lock, but let's make it explicit for clarity // this should already be called with that lock, but let's make it explicit for clarity
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
@ -173,17 +177,17 @@ namespace cryptonote
fee = tx.rct_signatures.txnFee; fee = tx.rct_signatures.txnFee;
} }
if (!kept_by_block && !m_blockchain.check_fee(blob_size, fee)) if (!kept_by_block && !m_blockchain.check_fee(tx_weight, fee))
{ {
tvc.m_verifivation_failed = true; tvc.m_verifivation_failed = true;
tvc.m_fee_too_low = true; tvc.m_fee_too_low = true;
return false; return false;
} }
size_t tx_size_limit = get_transaction_size_limit(version); size_t tx_weight_limit = get_transaction_weight_limit(version);
if (!kept_by_block && blob_size > tx_size_limit) if (!kept_by_block && tx_weight > tx_weight_limit)
{ {
LOG_PRINT_L1("transaction is too big: " << blob_size << " bytes, maximum size: " << tx_size_limit); LOG_PRINT_L1("transaction is too heavy: " << tx_weight << " bytes, maximum weight: " << tx_weight_limit);
tvc.m_verifivation_failed = true; tvc.m_verifivation_failed = true;
tvc.m_too_big = true; tvc.m_too_big = true;
return false; return false;
@ -227,7 +231,7 @@ namespace cryptonote
// may become valid again, so ignore the failed inputs check. // may become valid again, so ignore the failed inputs check.
if(kept_by_block) if(kept_by_block)
{ {
meta.blob_size = blob_size; meta.weight = tx_weight;
meta.fee = fee; meta.fee = fee;
meta.max_used_block_id = null_hash; meta.max_used_block_id = null_hash;
meta.max_used_block_height = 0; meta.max_used_block_height = 0;
@ -248,7 +252,7 @@ namespace cryptonote
m_blockchain.add_txpool_tx(tx, meta); m_blockchain.add_txpool_tx(tx, meta);
if (!insert_key_images(tx, kept_by_block)) if (!insert_key_images(tx, kept_by_block))
return false; return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)blob_size, receive_time), id); m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -267,7 +271,7 @@ namespace cryptonote
}else }else
{ {
//update transactions container //update transactions container
meta.blob_size = blob_size; meta.weight = tx_weight;
meta.kept_by_block = kept_by_block; meta.kept_by_block = kept_by_block;
meta.fee = fee; meta.fee = fee;
meta.max_used_block_id = max_used_block_id; meta.max_used_block_id = max_used_block_id;
@ -290,7 +294,7 @@ namespace cryptonote
m_blockchain.add_txpool_tx(tx, meta); m_blockchain.add_txpool_tx(tx, meta);
if (!insert_key_images(tx, kept_by_block)) if (!insert_key_images(tx, kept_by_block))
return false; return false;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)blob_size, receive_time), id); m_txs_by_fee_and_receive_time.emplace(std::pair<double, std::time_t>(fee / (double)tx_weight, receive_time), id);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -304,13 +308,13 @@ namespace cryptonote
} }
tvc.m_verifivation_failed = false; tvc.m_verifivation_failed = false;
m_txpool_size += blob_size; m_txpool_weight += tx_weight;
++m_cookie; ++m_cookie;
MINFO("Transaction added to pool: txid " << id << " bytes: " << blob_size << " fee/byte: " << (fee / (double)blob_size)); MINFO("Transaction added to pool: txid " << id << " weight: " << tx_weight << " fee/byte: " << (fee / (double)tx_weight));
prune(m_txpool_max_size); prune(m_txpool_max_weight);
return true; return true;
} }
@ -321,26 +325,26 @@ namespace cryptonote
size_t blob_size = 0; size_t blob_size = 0;
if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0) if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0)
return false; return false;
return add_tx(tx, h, blob_size, tvc, keeped_by_block, relayed, do_not_relay, version); return add_tx(tx, h, get_transaction_weight(tx, blob_size), tvc, keeped_by_block, relayed, do_not_relay, version);
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
size_t tx_memory_pool::get_txpool_size() const size_t tx_memory_pool::get_txpool_weight() const
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
return m_txpool_size; return m_txpool_weight;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void tx_memory_pool::set_txpool_max_size(size_t bytes) void tx_memory_pool::set_txpool_max_weight(size_t bytes)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
m_txpool_max_size = bytes; m_txpool_max_weight = bytes;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void tx_memory_pool::prune(size_t bytes) void tx_memory_pool::prune(size_t bytes)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
if (bytes == 0) if (bytes == 0)
bytes = m_txpool_max_size; bytes = m_txpool_max_weight;
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain); LockedTXN lock(m_blockchain);
bool changed = false; bool changed = false;
@ -349,7 +353,7 @@ namespace cryptonote
auto it = --m_txs_by_fee_and_receive_time.end(); auto it = --m_txs_by_fee_and_receive_time.end();
while (it != m_txs_by_fee_and_receive_time.begin()) while (it != m_txs_by_fee_and_receive_time.begin())
{ {
if (m_txpool_size <= bytes) if (m_txpool_weight <= bytes)
break; break;
try try
{ {
@ -374,11 +378,11 @@ namespace cryptonote
return; return;
} }
// remove first, in case this throws, so key images aren't removed // remove first, in case this throws, so key images aren't removed
MINFO("Pruning tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); MINFO("Pruning tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first);
m_blockchain.remove_txpool_tx(txid); m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= txblob.size(); m_txpool_weight -= it->first.second;
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
MINFO("Pruned tx " << txid << " from txpool: size: " << it->first.second << ", fee/byte: " << it->first.first); MINFO("Pruned tx " << txid << " from txpool: weight: " << it->first.second << ", fee/byte: " << it->first.first);
m_txs_by_fee_and_receive_time.erase(it--); m_txs_by_fee_and_receive_time.erase(it--);
changed = true; changed = true;
} }
@ -390,8 +394,8 @@ namespace cryptonote
} }
if (changed) if (changed)
++m_cookie; ++m_cookie;
if (m_txpool_size > bytes) if (m_txpool_weight > bytes)
MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes); MINFO("Pool weight after pruning is larger than limit: " << m_txpool_weight << "/" << bytes);
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block) bool tx_memory_pool::insert_key_images(const transaction &tx, bool kept_by_block)
@ -446,7 +450,7 @@ namespace cryptonote
return true; return true;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen) bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
@ -470,7 +474,7 @@ namespace cryptonote
MERROR("Failed to parse tx from txpool"); MERROR("Failed to parse tx from txpool");
return false; return false;
} }
blob_size = meta.blob_size; tx_weight = meta.weight;
fee = meta.fee; fee = meta.fee;
relayed = meta.relayed; relayed = meta.relayed;
do_not_relay = meta.do_not_relay; do_not_relay = meta.do_not_relay;
@ -478,7 +482,7 @@ namespace cryptonote
// remove first, in case this throws, so key images aren't removed // remove first, in case this throws, so key images aren't removed
m_blockchain.remove_txpool_tx(id); m_blockchain.remove_txpool_tx(id);
m_txpool_size -= blob_size; m_txpool_weight -= tx_weight;
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
} }
catch (const std::exception &e) catch (const std::exception &e)
@ -552,7 +556,7 @@ namespace cryptonote
{ {
// remove first, so we only remove key images if the tx removal succeeds // remove first, so we only remove key images if the tx removal succeeds
m_blockchain.remove_txpool_tx(txid); m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= bd.size(); m_txpool_weight -= get_transaction_weight(tx, bd.size());
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
} }
} }
@ -670,7 +674,7 @@ namespace cryptonote
const uint64_t now = time(NULL); const uint64_t now = time(NULL);
backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes)); backlog.reserve(m_blockchain.get_txpool_tx_count(include_unrelayed_txes));
m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ m_blockchain.for_all_txpool_txes([&backlog, now](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
backlog.push_back({meta.blob_size, meta.fee, meta.receive_time - now}); backlog.push_back({meta.weight, meta.fee, meta.receive_time - now});
return true; return true;
}, false, include_unrelayed_txes); }, false, include_unrelayed_txes);
} }
@ -682,15 +686,15 @@ namespace cryptonote
const uint64_t now = time(NULL); const uint64_t now = time(NULL);
std::map<uint64_t, txpool_histo> agebytes; std::map<uint64_t, txpool_histo> agebytes;
stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes); stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
std::vector<uint32_t> sizes; std::vector<uint32_t> weights;
sizes.reserve(stats.txs_total); weights.reserve(stats.txs_total);
m_blockchain.for_all_txpool_txes([&stats, &sizes, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){ m_blockchain.for_all_txpool_txes([&stats, &weights, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
sizes.push_back(meta.blob_size); weights.push_back(meta.weight);
stats.bytes_total += meta.blob_size; stats.bytes_total += meta.weight;
if (!stats.bytes_min || meta.blob_size < stats.bytes_min) if (!stats.bytes_min || meta.weight < stats.bytes_min)
stats.bytes_min = meta.blob_size; stats.bytes_min = meta.weight;
if (meta.blob_size > stats.bytes_max) if (meta.weight > stats.bytes_max)
stats.bytes_max = meta.blob_size; stats.bytes_max = meta.weight;
if (!meta.relayed) if (!meta.relayed)
stats.num_not_relayed++; stats.num_not_relayed++;
stats.fee_total += meta.fee; stats.fee_total += meta.fee;
@ -702,12 +706,12 @@ namespace cryptonote
stats.num_failing++; stats.num_failing++;
uint64_t age = now - meta.receive_time + (now == meta.receive_time); uint64_t age = now - meta.receive_time + (now == meta.receive_time);
agebytes[age].txs++; agebytes[age].txs++;
agebytes[age].bytes += meta.blob_size; agebytes[age].bytes += meta.weight;
if (meta.double_spend_seen) if (meta.double_spend_seen)
++stats.num_double_spends; ++stats.num_double_spends;
return true; return true;
}, false, include_unrelayed_txes); }, false, include_unrelayed_txes);
stats.bytes_med = epee::misc_utils::median(sizes); stats.bytes_med = epee::misc_utils::median(weights);
if (stats.txs_total > 1) if (stats.txs_total > 1)
{ {
/* looking for 98th percentile */ /* looking for 98th percentile */
@ -771,7 +775,8 @@ namespace cryptonote
return true; return true;
} }
txi.tx_json = obj_to_json_str(tx); txi.tx_json = obj_to_json_str(tx);
txi.blob_size = meta.blob_size; txi.blob_size = bd->size();
txi.weight = meta.weight;
txi.fee = meta.fee; txi.fee = meta.fee;
txi.kept_by_block = meta.kept_by_block; txi.kept_by_block = meta.kept_by_block;
txi.max_used_block_height = meta.max_used_block_height; txi.max_used_block_height = meta.max_used_block_height;
@ -842,7 +847,8 @@ namespace cryptonote
return true; return true;
} }
txi.tx = tx; txi.tx = tx;
txi.blob_size = meta.blob_size; txi.blob_size = bd->size();
txi.weight = meta.weight;
txi.fee = meta.fee; txi.fee = meta.fee;
txi.kept_by_block = meta.kept_by_block; txi.kept_by_block = meta.kept_by_block;
txi.max_used_block_height = meta.max_used_block_height; txi.max_used_block_height = meta.max_used_block_height;
@ -1116,7 +1122,8 @@ namespace cryptonote
} }
ss << obj_to_json_str(tx) << std::endl; ss << obj_to_json_str(tx) << std::endl;
} }
ss << "blob_size: " << meta.blob_size << std::endl ss << "blob_size: " << (short_format ? "-" : std::to_string(txblob->size())) << std::endl
<< "weight: " << meta.weight << std::endl
<< "fee: " << print_money(meta.fee) << std::endl << "fee: " << print_money(meta.fee) << std::endl
<< "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl << "kept_by_block: " << (meta.kept_by_block ? 'T' : 'F') << std::endl
<< "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl << "double_spend_seen: " << (meta.double_spend_seen ? 'T' : 'F') << std::endl
@ -1131,25 +1138,25 @@ namespace cryptonote
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate //TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version) bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
uint64_t best_coinbase = 0, coinbase = 0; uint64_t best_coinbase = 0, coinbase = 0;
total_size = 0; total_weight = 0;
fee = 0; fee = 0;
//baseline empty block //baseline empty block
get_block_reward(median_size, total_size, already_generated_coins, best_coinbase, version); get_block_reward(median_weight, total_weight, already_generated_coins, best_coinbase, version);
size_t max_total_size_pre_v5 = (130 * median_size) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_size_v5 = 2 * median_size - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_size = version >= 5 ? max_total_size_v5 : max_total_size_pre_v5; size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5;
std::unordered_set<crypto::key_image> k_images; std::unordered_set<crypto::key_image> k_images;
LOG_PRINT_L2("Filling block template, median size " << median_size << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool"); LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
LockedTXN lock(m_blockchain); LockedTXN lock(m_blockchain);
@ -1162,12 +1169,12 @@ namespace cryptonote
MERROR(" failed to find tx meta"); MERROR(" failed to find tx meta");
continue; continue;
} }
LOG_PRINT_L2("Considering " << sorted_it->second << ", size " << meta.blob_size << ", current block size " << total_size << "/" << max_total_size << ", current coinbase " << print_money(best_coinbase)); LOG_PRINT_L2("Considering " << sorted_it->second << ", weight " << meta.weight << ", current block weight " << total_weight << "/" << max_total_weight << ", current coinbase " << print_money(best_coinbase));
// Can not exceed maximum block size // Can not exceed maximum block weight
if (max_total_size < total_size + meta.blob_size) if (max_total_weight < total_weight + meta.weight)
{ {
LOG_PRINT_L2(" would exceed maximum block size"); LOG_PRINT_L2(" would exceed maximum block weight");
continue; continue;
} }
@ -1177,9 +1184,9 @@ namespace cryptonote
// If we're getting lower coinbase tx, // If we're getting lower coinbase tx,
// stop including more tx // stop including more tx
uint64_t block_reward; uint64_t block_reward;
if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version)) if(!get_block_reward(median_weight, total_weight + meta.weight, already_generated_coins, block_reward, version))
{ {
LOG_PRINT_L2(" would exceed maximum block size"); LOG_PRINT_L2(" would exceed maximum block weight");
continue; continue;
} }
coinbase = block_reward + fee + meta.fee; coinbase = block_reward + fee + meta.fee;
@ -1191,11 +1198,11 @@ namespace cryptonote
} }
else else
{ {
// If we've exceeded the penalty free size, // If we've exceeded the penalty free weight,
// stop including more tx // stop including more tx
if (total_size > median_size) if (total_weight > median_weight)
{ {
LOG_PRINT_L2(" would exceed median block size"); LOG_PRINT_L2(" would exceed median block weight");
break; break;
} }
} }
@ -1241,16 +1248,16 @@ namespace cryptonote
} }
bl.tx_hashes.push_back(sorted_it->second); bl.tx_hashes.push_back(sorted_it->second);
total_size += meta.blob_size; total_weight += meta.weight;
fee += meta.fee; fee += meta.fee;
best_coinbase = coinbase; best_coinbase = coinbase;
append_key_images(k_images, tx); append_key_images(k_images, tx);
LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)); LOG_PRINT_L2(" added, new block weight " << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase));
} }
expected_reward = best_coinbase; expected_reward = best_coinbase;
LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size " LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, weight "
<< total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase) << total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase)
<< " (including " << print_money(fee) << " in fees)"); << " (including " << print_money(fee) << " in fees)");
return true; return true;
} }
@ -1259,14 +1266,14 @@ namespace cryptonote
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
size_t tx_size_limit = get_transaction_size_limit(version); size_t tx_weight_limit = get_transaction_weight_limit(version);
std::unordered_set<crypto::hash> remove; std::unordered_set<crypto::hash> remove;
m_txpool_size = 0; m_txpool_weight = 0;
m_blockchain.for_all_txpool_txes([this, &remove, tx_size_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) { m_blockchain.for_all_txpool_txes([this, &remove, tx_weight_limit](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata*) {
m_txpool_size += meta.blob_size; m_txpool_weight += meta.weight;
if (meta.blob_size > tx_size_limit) { if (meta.weight > tx_weight_limit) {
LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.blob_size << " bytes), removing it from pool"); LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.weight << " bytes), removing it from pool");
remove.insert(txid); remove.insert(txid);
} }
else if (m_blockchain.have_tx(txid)) { else if (m_blockchain.have_tx(txid)) {
@ -1293,7 +1300,7 @@ namespace cryptonote
} }
// remove tx from db first // remove tx from db first
m_blockchain.remove_txpool_tx(txid); m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= txblob.size(); m_txpool_weight -= get_transaction_weight(tx, txblob.size());
remove_transaction_keyimages(tx); remove_transaction_keyimages(tx);
auto sorted_it = find_tx_in_sorted_container(txid); auto sorted_it = find_tx_in_sorted_container(txid);
if (sorted_it == m_txs_by_fee_and_receive_time.end()) if (sorted_it == m_txs_by_fee_and_receive_time.end())
@ -1318,15 +1325,15 @@ namespace cryptonote
return n_removed; return n_removed;
} }
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
bool tx_memory_pool::init(size_t max_txpool_size) bool tx_memory_pool::init(size_t max_txpool_weight)
{ {
CRITICAL_REGION_LOCAL(m_transactions_lock); CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain); CRITICAL_REGION_LOCAL1(m_blockchain);
m_txpool_max_size = max_txpool_size ? max_txpool_size : DEFAULT_TXPOOL_MAX_SIZE; m_txpool_max_weight = max_txpool_weight ? max_txpool_weight : DEFAULT_TXPOOL_MAX_WEIGHT;
m_txs_by_fee_and_receive_time.clear(); m_txs_by_fee_and_receive_time.clear();
m_spent_key_images.clear(); m_spent_key_images.clear();
m_txpool_size = 0; m_txpool_weight = 0;
std::vector<crypto::hash> remove; std::vector<crypto::hash> remove;
// first add the not kept by block, then the kept by block, // first add the not kept by block, then the kept by block,
@ -1348,8 +1355,8 @@ namespace cryptonote
MFATAL("Failed to insert key images from txpool tx"); MFATAL("Failed to insert key images from txpool tx");
return false; return false;
} }
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.blob_size, meta.receive_time), txid); m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.weight, meta.receive_time), txid);
m_txpool_size += meta.blob_size; m_txpool_weight += meta.weight;
return true; return true;
}, true); }, true);
if (!r) if (!r)

View File

@ -84,7 +84,7 @@ namespace cryptonote
* *
* This handling includes: * This handling includes:
* storing the transactions * storing the transactions
* organizing the transactions by fee per size * organizing the transactions by fee per weight unit
* taking/giving transactions to and from various other components * taking/giving transactions to and from various other components
* saving the transactions to disk on shutdown * saving the transactions to disk on shutdown
* helping create a new block template by choosing transactions for it * helping create a new block template by choosing transactions for it
@ -105,9 +105,9 @@ namespace cryptonote
* @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t) * @copydoc add_tx(transaction&, tx_verification_context&, bool, bool, uint8_t)
* *
* @param id the transaction's hash * @param id the transaction's hash
* @param blob_size the transaction's size * @param tx_weight the transaction's weight
*/ */
bool add_tx(transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version); bool add_tx(transaction &tx, const crypto::hash &id, size_t tx_weight, tx_verification_context& tvc, bool kept_by_block, bool relayed, bool do_not_relay, uint8_t version);
/** /**
* @brief add a transaction to the transaction pool * @brief add a transaction to the transaction pool
@ -133,7 +133,7 @@ namespace cryptonote
* *
* @param id the hash of the transaction * @param id the hash of the transaction
* @param tx return-by-reference the transaction taken * @param tx return-by-reference the transaction taken
* @param blob_size return-by-reference the transaction's size * @param tx_weight return-by-reference the transaction's weight
* @param fee the transaction fee * @param fee the transaction fee
* @param relayed return-by-reference was transaction relayed to us by the network? * @param relayed return-by-reference was transaction relayed to us by the network?
* @param do_not_relay return-by-reference is transaction not to be relayed to the network? * @param do_not_relay return-by-reference is transaction not to be relayed to the network?
@ -141,7 +141,7 @@ namespace cryptonote
* *
* @return true unless the transaction cannot be found in the pool * @return true unless the transaction cannot be found in the pool
*/ */
bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen); bool take_tx(const crypto::hash &id, transaction &tx, size_t& tx_weight, uint64_t& fee, bool &relayed, bool &do_not_relay, bool &double_spend_seen);
/** /**
* @brief checks if the pool has a transaction with the given hash * @brief checks if the pool has a transaction with the given hash
@ -198,11 +198,11 @@ namespace cryptonote
/** /**
* @brief loads pool state (if any) from disk, and initializes pool * @brief loads pool state (if any) from disk, and initializes pool
* *
* @param max_txpool_size the max size in bytes * @param max_txpool_weight the max weight in bytes
* *
* @return true * @return true
*/ */
bool init(size_t max_txpool_size = 0); bool init(size_t max_txpool_weight = 0);
/** /**
* @brief attempts to save the transaction pool state to disk * @brief attempts to save the transaction pool state to disk
@ -219,16 +219,16 @@ namespace cryptonote
* @brief Chooses transactions for a block to include * @brief Chooses transactions for a block to include
* *
* @param bl return-by-reference the block to fill in with transactions * @param bl return-by-reference the block to fill in with transactions
* @param median_size the current median block size * @param median_weight the current median block weight
* @param already_generated_coins the current total number of coins "minted" * @param already_generated_coins the current total number of coins "minted"
* @param total_size return-by-reference the total size of the new block * @param total_weight return-by-reference the total weight of the new block
* @param fee return-by-reference the total of fees from the included transactions * @param fee return-by-reference the total of fees from the included transactions
* @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees * @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
* @param version hard fork version to use for consensus rules * @param version hard fork version to use for consensus rules
* *
* @return true * @return true
*/ */
bool fill_block_template(block &bl, size_t median_size, uint64_t already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t &expected_reward, uint8_t version); bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
/** /**
* @brief get a list of all transactions in the pool * @brief get a list of all transactions in the pool
@ -249,7 +249,7 @@ namespace cryptonote
void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const; void get_transaction_hashes(std::vector<crypto::hash>& txs, bool include_unrelayed_txes = true) const;
/** /**
* @brief get (size, fee, receive time) for all transaction in the pool * @brief get (weight, fee, receive time) for all transaction in the pool
* *
* @param txs return-by-reference that data * @param txs return-by-reference that data
* @param include_unrelayed_txes include unrelayed txes in the result * @param include_unrelayed_txes include unrelayed txes in the result
@ -370,21 +370,21 @@ namespace cryptonote
uint64_t cookie() const { return m_cookie; } uint64_t cookie() const { return m_cookie; }
/** /**
* @brief get the cumulative txpool size in bytes * @brief get the cumulative txpool weight in bytes
* *
* @return the cumulative txpool size in bytes * @return the cumulative txpool weight in bytes
*/ */
size_t get_txpool_size() const; size_t get_txpool_weight() const;
/** /**
* @brief set the max cumulative txpool size in bytes * @brief set the max cumulative txpool weight in bytes
* *
* @param bytes the max cumulative txpool size in bytes * @param bytes the max cumulative txpool weight in bytes
*/ */
void set_txpool_max_size(size_t bytes); void set_txpool_max_weight(size_t bytes);
#define CURRENT_MEMPOOL_ARCHIVE_VER 11 #define CURRENT_MEMPOOL_ARCHIVE_VER 11
#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 12 #define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 13
/** /**
* @brief information about a single transaction * @brief information about a single transaction
@ -393,6 +393,7 @@ namespace cryptonote
{ {
transaction tx; //!< the transaction transaction tx; //!< the transaction
size_t blob_size; //!< the transaction's size size_t blob_size; //!< the transaction's size
size_t weight; //!< the transaction's weight
uint64_t fee; //!< the transaction's fee amount uint64_t fee; //!< the transaction's fee amount
crypto::hash max_used_block_id; //!< the hash of the highest block referenced by an input crypto::hash max_used_block_id; //!< the hash of the highest block referenced by an input
uint64_t max_used_block_height; //!< the height of the highest block referenced by an input uint64_t max_used_block_height; //!< the height of the highest block referenced by an input
@ -522,7 +523,7 @@ namespace cryptonote
/** /**
* @brief prune lowest fee/byte txes till we're not above bytes * @brief prune lowest fee/byte txes till we're not above bytes
* *
* if bytes is 0, use m_txpool_max_size * if bytes is 0, use m_txpool_max_weight
*/ */
void prune(size_t bytes = 0); void prune(size_t bytes = 0);
@ -578,8 +579,8 @@ private:
Blockchain& m_blockchain; //!< reference to the Blockchain object Blockchain& m_blockchain; //!< reference to the Blockchain object
size_t m_txpool_max_size; size_t m_txpool_max_weight;
size_t m_txpool_size; size_t m_txpool_weight;
mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache; mutable std::unordered_map<crypto::hash, std::tuple<bool, tx_verification_context, uint64_t, crypto::hash>> m_input_cache;
}; };
@ -608,6 +609,9 @@ namespace boost
if (version < 12) if (version < 12)
return; return;
ar & td.do_not_relay; ar & td.do_not_relay;
if (version < 13)
return;
ar & td.weight;
} }
} }
} }

View File

@ -76,6 +76,7 @@ namespace {
<< "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl << "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl
<< "POW hash: " << header.pow_hash << std::endl << "POW hash: " << header.pow_hash << std::endl
<< "block size: " << header.block_size << std::endl << "block size: " << header.block_size << std::endl
<< "block weight: " << header.block_weight << std::endl
<< "num txes: " << header.num_txes << std::endl << "num txes: " << header.num_txes << std::endl
<< "reward: " << cryptonote::print_money(header.reward); << "reward: " << cryptonote::print_money(header.reward);
} }
@ -558,7 +559,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
std::cout << std::endl; std::cout << std::endl;
std::cout std::cout
<< "height: " << header.height << ", timestamp: " << header.timestamp << "height: " << header.height << ", timestamp: " << header.timestamp
<< ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl << ", size: " << header.block_size << ", weight: " << header.block_weight << ", transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl << "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl << "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl
<< "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl; << "difficulty: " << header.difficulty << ", nonce " << header.nonce << ", reward " << cryptonote::print_money(header.reward) << std::endl;
@ -857,8 +858,9 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
tools::msg_writer() << "id: " << tx_info.id_hash << std::endl tools::msg_writer() << "id: " << tx_info.id_hash << std::endl
<< tx_info.tx_json << std::endl << tx_info.tx_json << std::endl
<< "blob_size: " << tx_info.blob_size << std::endl << "blob_size: " << tx_info.blob_size << std::endl
<< "weight: " << tx_info.weight << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
<< "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.weight) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
@ -940,8 +942,9 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
{ {
tools::msg_writer() << "id: " << tx_info.id_hash << std::endl tools::msg_writer() << "id: " << tx_info.id_hash << std::endl
<< "blob_size: " << tx_info.blob_size << std::endl << "blob_size: " << tx_info.blob_size << std::endl
<< "weight: " << tx_info.weight << std::endl
<< "fee: " << cryptonote::print_money(tx_info.fee) << std::endl << "fee: " << cryptonote::print_money(tx_info.fee) << std::endl
<< "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.blob_size) << std::endl << "fee/byte: " << cryptonote::print_money(tx_info.fee / (double)tx_info.weight) << std::endl
<< "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl << "receive_time: " << tx_info.receive_time << " (" << get_human_time_ago(tx_info.receive_time, now) << ")" << std::endl
<< "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl << "relayed: " << [&](const cryptonote::tx_info &tx_info)->std::string { if (!tx_info.relayed) return "no"; return boost::lexical_cast<std::string>(tx_info.last_relayed_time) + " (" + get_human_time_ago(tx_info.last_relayed_time, now) + ")"; } (tx_info) << std::endl
<< "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl << "do_not_relay: " << (tx_info.do_not_relay ? 'T' : 'F') << std::endl
@ -996,7 +999,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
size_t avg_bytes = n_transactions ? res.pool_stats.bytes_total / n_transactions : 0; size_t avg_bytes = n_transactions ? res.pool_stats.bytes_total / n_transactions : 0;
std::string backlog_message; std::string backlog_message;
const uint64_t full_reward_zone = ires.block_size_limit / 2; const uint64_t full_reward_zone = ires.block_weight_limit / 2;
if (res.pool_stats.bytes_total <= full_reward_zone) if (res.pool_stats.bytes_total <= full_reward_zone)
{ {
backlog_message = "no backlog"; backlog_message = "no backlog";
@ -1701,8 +1704,8 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
cryptonote::COMMAND_RPC_GET_INFO::response ires; cryptonote::COMMAND_RPC_GET_INFO::response ires;
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request bhreq; cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request bhreq;
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres; cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres;
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request fereq; cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request fereq;
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response feres; cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response feres;
epee::json_rpc::error error_resp; epee::json_rpc::error error_resp;
std::string fail_message = "Problem fetching info"; std::string fail_message = "Problem fetching info";
@ -1726,7 +1729,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
tools::fail_msg_writer() << make_error(fail_message, ires.status); tools::fail_msg_writer() << make_error(fail_message, ires.status);
return true; return true;
} }
if (!m_rpc_server->on_get_per_kb_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK) if (!m_rpc_server->on_get_base_fee_estimate(fereq, feres, error_resp) || feres.status != CORE_RPC_STATUS_OK)
{ {
tools::fail_msg_writer() << make_error(fail_message, feres.status); tools::fail_msg_writer() << make_error(fail_message, feres.status);
return true; return true;
@ -1762,8 +1765,8 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
double avgdiff = 0; double avgdiff = 0;
double avgnumtxes = 0; double avgnumtxes = 0;
double avgreward = 0; double avgreward = 0;
std::vector<uint64_t> sizes; std::vector<uint64_t> weights;
sizes.reserve(nblocks); weights.reserve(nblocks);
uint64_t earliest = std::numeric_limits<uint64_t>::max(), latest = 0; uint64_t earliest = std::numeric_limits<uint64_t>::max(), latest = 0;
std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0); std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0);
for (const auto &bhr: bhres.headers) for (const auto &bhr: bhres.headers)
@ -1771,7 +1774,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
avgdiff += bhr.difficulty; avgdiff += bhr.difficulty;
avgnumtxes += bhr.num_txes; avgnumtxes += bhr.num_txes;
avgreward += bhr.reward; avgreward += bhr.reward;
sizes.push_back(bhr.block_size); weights.push_back(bhr.block_weight);
static_assert(sizeof(bhr.major_version) == 1, "major_version expected to be uint8_t"); static_assert(sizeof(bhr.major_version) == 1, "major_version expected to be uint8_t");
static_assert(sizeof(bhr.minor_version) == 1, "major_version expected to be uint8_t"); static_assert(sizeof(bhr.minor_version) == 1, "major_version expected to be uint8_t");
major_versions[(unsigned)bhr.major_version]++; major_versions[(unsigned)bhr.major_version]++;
@ -1782,9 +1785,9 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
avgdiff /= nblocks; avgdiff /= nblocks;
avgnumtxes /= nblocks; avgnumtxes /= nblocks;
avgreward /= nblocks; avgreward /= nblocks;
uint64_t median_block_size = epee::misc_utils::median(sizes); uint64_t median_block_weight = epee::misc_utils::median(weights);
tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes tools::msg_writer() << "Last " << nblocks << ": avg. diff " << (uint64_t)avgdiff << ", " << (latest - earliest) / nblocks << " avg sec/block, avg num txes " << avgnumtxes
<< ", avg. reward " << cryptonote::print_money(avgreward) << ", median block size " << median_block_size; << ", avg. reward " << cryptonote::print_money(avgreward) << ", median block weight " << median_block_weight;
unsigned int max_major = 256, max_minor = 256; unsigned int max_major = 256, max_minor = 256;
while (max_major > 0 && !major_versions[--max_major]); while (max_major > 0 && !major_versions[--max_major]);

View File

@ -758,7 +758,20 @@ namespace rct {
std::vector<uint64_t> proof_amounts; std::vector<uint64_t> proof_amounts;
size_t n_amounts = outamounts.size(); size_t n_amounts = outamounts.size();
size_t amounts_proved = 0; size_t amounts_proved = 0;
while (amounts_proved < n_amounts) if (range_proof_type == RangeProofPaddedBulletproof)
{
rct::keyV C, masks;
rv.p.bulletproofs.push_back(proveRangeBulletproof(C, masks, outamounts));
#ifdef DBG
CHECK_AND_ASSERT_THROW_MES(verBulletproof(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof");
#endif
for (i = 0; i < outamounts.size(); ++i)
{
rv.outPk[i].mask = C[i];
outSk[i].mask = masks[i];
}
}
else while (amounts_proved < n_amounts)
{ {
size_t batch_size = 1; size_t batch_size = 1;
if (range_proof_type == RangeProofMultiOutputBulletproof) if (range_proof_type == RangeProofMultiOutputBulletproof)

View File

@ -29,6 +29,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "misc_log_ex.h" #include "misc_log_ex.h"
#include "cryptonote_config.h"
#include "rctTypes.h" #include "rctTypes.h"
using namespace crypto; using namespace crypto;
using namespace std; using namespace std;
@ -233,11 +234,29 @@ namespace rct {
} }
} }
bool is_rct_borromean(int type)
{
switch (type)
{
case RCTTypeSimple:
case RCTTypeFull:
return true;
default:
return false;
}
}
size_t n_bulletproof_amounts(const Bulletproof &proof) size_t n_bulletproof_amounts(const Bulletproof &proof)
{ {
CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(proof.L.size() <= 31, 0, "Insane bulletproof L size"); CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
return 1 << (proof.L.size() - 6); static const size_t extra_bits = 4;
static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date");
CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(proof.V.size() <= (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L");
CHECK_AND_ASSERT_MES(proof.V.size() * 2 > (1u<<(proof.L.size()-6)), 0, "Invalid bulletproof V/L");
CHECK_AND_ASSERT_MES(proof.V.size() > 0, 0, "Empty bulletproof");
return proof.V.size();
} }
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs) size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs)
@ -254,4 +273,28 @@ namespace rct {
return n; return n;
} }
size_t n_bulletproof_max_amounts(const Bulletproof &proof)
{
CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size");
CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), 0, "Mismatched bulletproof L/R size");
static const size_t extra_bits = 4;
static_assert((1 << extra_bits) == BULLETPROOF_MAX_OUTPUTS, "log2(BULLETPROOF_MAX_OUTPUTS) is out of date");
CHECK_AND_ASSERT_MES(proof.L.size() <= 6 + extra_bits, 0, "Invalid bulletproof L size");
return 1 << (proof.L.size() - 6);
}
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs)
{
size_t n = 0;
for (const Bulletproof &proof: proofs)
{
size_t n2 = n_bulletproof_max_amounts(proof);
CHECK_AND_ASSERT_MES(n2 < std::numeric_limits<uint32_t>::max() - n, 0, "Invalid number of bulletproofs");
if (n2 == 0)
return 0;
n += n2;
}
return n;
}
} }

View File

@ -214,7 +214,9 @@ namespace rct {
}; };
size_t n_bulletproof_amounts(const Bulletproof &proof); size_t n_bulletproof_amounts(const Bulletproof &proof);
size_t n_bulletproof_max_amounts(const Bulletproof &proof);
size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs); size_t n_bulletproof_amounts(const std::vector<Bulletproof> &proofs);
size_t n_bulletproof_max_amounts(const std::vector<Bulletproof> &proofs);
//A container to hold all signatures necessary for RingCT //A container to hold all signatures necessary for RingCT
// rangeSigs holds all the rangeproof data of a transaction // rangeSigs holds all the rangeproof data of a transaction
@ -229,7 +231,7 @@ namespace rct {
RCTTypeSimple = 2, RCTTypeSimple = 2,
RCTTypeBulletproof = 3, RCTTypeBulletproof = 3,
}; };
enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof };
struct rctSigBase { struct rctSigBase {
uint8_t type; uint8_t type;
key message; key message;
@ -324,7 +326,7 @@ namespace rct {
if (nbp - i > 1) if (nbp - i > 1)
ar.delimit_array(); ar.delimit_array();
} }
if (n_bulletproof_amounts(bulletproofs) != outputs) if (n_bulletproof_max_amounts(bulletproofs) < outputs)
return false; return false;
ar.end_array(); ar.end_array();
} }
@ -528,6 +530,7 @@ namespace rct {
bool is_rct_simple(int type); bool is_rct_simple(int type);
bool is_rct_bulletproof(int type); bool is_rct_bulletproof(int type);
bool is_rct_borromean(int type);
static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; }
static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; } static inline const rct::key &sk2rct(const crypto::secret_key &sk) { return (const rct::key&)sk; }

View File

@ -195,8 +195,8 @@ namespace cryptonote
res.stagenet = m_nettype == STAGENET; res.stagenet = m_nettype == STAGENET;
res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
res.start_time = (uint64_t)m_core.get_start_time(); res.start_time = (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
@ -1313,7 +1313,7 @@ namespace cryptonote
response.hash = string_tools::pod_to_hex(hash); response.hash = string_tools::pod_to_hex(hash);
response.difficulty = m_core.get_blockchain_storage().block_difficulty(height); response.difficulty = m_core.get_blockchain_storage().block_difficulty(height);
response.reward = get_block_reward(blk); response.reward = get_block_reward(blk);
response.block_size = m_core.get_blockchain_storage().get_db().get_block_size(height); response.block_size = response.block_weight = m_core.get_blockchain_storage().get_db().get_block_weight(height);
response.num_txes = blk.tx_hashes.size(); response.num_txes = blk.tx_hashes.size();
response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : ""; response.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : "";
return true; return true;
@ -1646,8 +1646,8 @@ namespace cryptonote
res.stagenet = m_nettype == STAGENET; res.stagenet = m_nettype == STAGENET;
res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain"; res.nettype = m_nettype == MAINNET ? "mainnet" : m_nettype == TESTNET ? "testnet" : m_nettype == STAGENET ? "stagenet" : "fakechain";
res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1); res.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.height - 1);
res.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
res.start_time = (uint64_t)m_core.get_start_time(); res.start_time = (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
@ -1839,14 +1839,15 @@ namespace cryptonote
return true; return true;
} }
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp) bool core_rpc_server::on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp)
{ {
PERF_TIMER(on_get_per_kb_fee_estimate); PERF_TIMER(on_get_base_fee_estimate);
bool r; bool r;
if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r)) if (use_bootstrap_daemon_if_necessary<COMMAND_RPC_GET_BASE_FEE_ESTIMATE>(invoke_http_mode::JON_RPC, "get_fee_estimate", req, res, r))
return r; return r;
res.fee = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.grace_blocks); res.fee = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.grace_blocks);
res.quantization_mask = Blockchain::get_fee_quantization_mask();
res.status = CORE_RPC_STATUS_OK; res.status = CORE_RPC_STATUS_OK;
return true; return true;
} }

View File

@ -150,7 +150,7 @@ namespace cryptonote
MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM) MAP_JON_RPC_WE("get_output_histogram", on_get_output_histogram, COMMAND_RPC_GET_OUTPUT_HISTOGRAM)
MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION) MAP_JON_RPC_WE("get_version", on_get_version, COMMAND_RPC_GET_VERSION)
MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted) MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted)
MAP_JON_RPC_WE("get_fee_estimate", on_get_per_kb_fee_estimate, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE) MAP_JON_RPC_WE("get_fee_estimate", on_get_base_fee_estimate, COMMAND_RPC_GET_BASE_FEE_ESTIMATE)
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted) MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted) MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted) MAP_JON_RPC_WE_IF("sync_info", on_sync_info, COMMAND_RPC_SYNC_INFO, !m_restricted)
@ -214,7 +214,7 @@ namespace cryptonote
bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp); bool on_get_output_histogram(const COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request& req, COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response& res, epee::json_rpc::error& error_resp);
bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp); bool on_get_version(const COMMAND_RPC_GET_VERSION::request& req, COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& error_resp);
bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp); bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp);
bool on_get_per_kb_fee_estimate(const COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp); bool on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp);
bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp); bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp);
bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp); bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp);
bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp); bool on_sync_info(const COMMAND_RPC_SYNC_INFO::request& req, COMMAND_RPC_SYNC_INFO::response& res, epee::json_rpc::error& error_resp);

View File

@ -48,8 +48,8 @@ namespace cryptonote
// whether they can talk to a given daemon without having to know in // whether they can talk to a given daemon without having to know in
// advance which version they will stop working with // advance which version they will stop working with
// Don't go over 32767 for any of these // Don't go over 32767 for any of these
#define CORE_RPC_VERSION_MAJOR 1 #define CORE_RPC_VERSION_MAJOR 2
#define CORE_RPC_VERSION_MINOR 21 #define CORE_RPC_VERSION_MINOR 0
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor)) #define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR) #define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
@ -959,7 +959,9 @@ namespace cryptonote
std::string top_block_hash; std::string top_block_hash;
uint64_t cumulative_difficulty; uint64_t cumulative_difficulty;
uint64_t block_size_limit; uint64_t block_size_limit;
uint64_t block_weight_limit;
uint64_t block_size_median; uint64_t block_size_median;
uint64_t block_weight_median;
uint64_t start_time; uint64_t start_time;
uint64_t free_space; uint64_t free_space;
bool offline; bool offline;
@ -990,7 +992,9 @@ namespace cryptonote
KV_SERIALIZE(top_block_hash) KV_SERIALIZE(top_block_hash)
KV_SERIALIZE(cumulative_difficulty) KV_SERIALIZE(cumulative_difficulty)
KV_SERIALIZE(block_size_limit) KV_SERIALIZE(block_size_limit)
KV_SERIALIZE_OPT(block_weight_limit, (uint64_t)0)
KV_SERIALIZE(block_size_median) KV_SERIALIZE(block_size_median)
KV_SERIALIZE_OPT(block_weight_median, (uint64_t)0)
KV_SERIALIZE(start_time) KV_SERIALIZE(start_time)
KV_SERIALIZE(free_space) KV_SERIALIZE(free_space)
KV_SERIALIZE(offline) KV_SERIALIZE(offline)
@ -1195,6 +1199,7 @@ namespace cryptonote
difficulty_type difficulty; difficulty_type difficulty;
uint64_t reward; uint64_t reward;
uint64_t block_size; uint64_t block_size;
uint64_t block_weight;
uint64_t num_txes; uint64_t num_txes;
std::string pow_hash; std::string pow_hash;
@ -1211,6 +1216,7 @@ namespace cryptonote
KV_SERIALIZE(difficulty) KV_SERIALIZE(difficulty)
KV_SERIALIZE(reward) KV_SERIALIZE(reward)
KV_SERIALIZE(block_size) KV_SERIALIZE(block_size)
KV_SERIALIZE_OPT(block_weight, (uint64_t)0)
KV_SERIALIZE(num_txes) KV_SERIALIZE(num_txes)
KV_SERIALIZE(pow_hash) KV_SERIALIZE(pow_hash)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
@ -1451,6 +1457,7 @@ namespace cryptonote
std::string id_hash; std::string id_hash;
std::string tx_json; // TODO - expose this data directly std::string tx_json; // TODO - expose this data directly
uint64_t blob_size; uint64_t blob_size;
uint64_t weight;
uint64_t fee; uint64_t fee;
std::string max_used_block_id_hash; std::string max_used_block_id_hash;
uint64_t max_used_block_height; uint64_t max_used_block_height;
@ -1468,6 +1475,7 @@ namespace cryptonote
KV_SERIALIZE(id_hash) KV_SERIALIZE(id_hash)
KV_SERIALIZE(tx_json) KV_SERIALIZE(tx_json)
KV_SERIALIZE(blob_size) KV_SERIALIZE(blob_size)
KV_SERIALIZE_OPT(weight, (uint64_t)0)
KV_SERIALIZE(fee) KV_SERIALIZE(fee)
KV_SERIALIZE(max_used_block_id_hash) KV_SERIALIZE(max_used_block_id_hash)
KV_SERIALIZE(max_used_block_height) KV_SERIALIZE(max_used_block_height)
@ -1564,7 +1572,7 @@ namespace cryptonote
struct tx_backlog_entry struct tx_backlog_entry
{ {
uint64_t blob_size; uint64_t weight;
uint64_t fee; uint64_t fee;
uint64_t time_in_pool; uint64_t time_in_pool;
}; };
@ -2102,7 +2110,7 @@ namespace cryptonote
}; };
}; };
struct COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE struct COMMAND_RPC_GET_BASE_FEE_ESTIMATE
{ {
struct request struct request
{ {
@ -2117,11 +2125,13 @@ namespace cryptonote
{ {
std::string status; std::string status;
uint64_t fee; uint64_t fee;
uint64_t quantization_mask;
bool untrusted; bool untrusted;
BEGIN_KV_SERIALIZE_MAP() BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status) KV_SERIALIZE(status)
KV_SERIALIZE(fee) KV_SERIALIZE(fee)
KV_SERIALIZE_OPT(quantization_mask, (uint64_t)1)
KV_SERIALIZE(untrusted) KV_SERIALIZE(untrusted)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()
}; };

View File

@ -472,7 +472,8 @@ namespace rpc
res.info.testnet = m_core.get_nettype() == TESTNET; res.info.testnet = m_core.get_nettype() == TESTNET;
res.info.stagenet = m_core.get_nettype() == STAGENET; res.info.stagenet = m_core.get_nettype() == STAGENET;
res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1); res.info.cumulative_difficulty = m_core.get_blockchain_storage().get_db().get_block_cumulative_difficulty(res.info.height - 1);
res.info.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit(); res.info.block_size_limit = res.info.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.info.block_size_median = res.info.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.info.start_time = (uint64_t)m_core.get_start_time(); res.info.start_time = (uint64_t)m_core.get_start_time();
res.status = Message::STATUS_OK; res.status = Message::STATUS_OK;
@ -763,7 +764,7 @@ namespace rpc
void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res) void DaemonHandler::handle(const GetPerKBFeeEstimate::Request& req, GetPerKBFeeEstimate::Response& res)
{ {
res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_per_kb_fee_estimate(req.num_grace_blocks); res.estimated_fee_per_kb = m_core.get_blockchain_storage().get_dynamic_base_fee_estimate(req.num_grace_blocks);
res.status = Message::STATUS_OK; res.status = Message::STATUS_OK;
} }

View File

@ -85,6 +85,7 @@ namespace rpc
cryptonote::transaction tx; cryptonote::transaction tx;
crypto::hash tx_hash; crypto::hash tx_hash;
uint64_t blob_size; uint64_t blob_size;
uint64_t weight;
uint64_t fee; uint64_t fee;
crypto::hash max_used_block_hash; crypto::hash max_used_block_hash;
uint64_t max_used_block_height; uint64_t max_used_block_height;
@ -185,6 +186,9 @@ namespace rpc
crypto::hash top_block_hash; crypto::hash top_block_hash;
uint64_t cumulative_difficulty; uint64_t cumulative_difficulty;
uint64_t block_size_limit; uint64_t block_size_limit;
uint64_t block_weight_limit;
uint64_t block_size_median;
uint64_t block_weight_median;
uint64_t start_time; uint64_t start_time;
}; };

View File

@ -757,6 +757,7 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::tx_in_pool& tx
INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx); INSERT_INTO_JSON_OBJECT(val, doc, tx, tx.tx);
INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash); INSERT_INTO_JSON_OBJECT(val, doc, tx_hash, tx.tx_hash);
INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size); INSERT_INTO_JSON_OBJECT(val, doc, blob_size, tx.blob_size);
INSERT_INTO_JSON_OBJECT(val, doc, weight, tx.weight);
INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee); INSERT_INTO_JSON_OBJECT(val, doc, fee, tx.fee);
INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_hash, tx.max_used_block_hash);
INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height); INSERT_INTO_JSON_OBJECT(val, doc, max_used_block_height, tx.max_used_block_height);
@ -780,6 +781,7 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::tx_in_pool& tx)
GET_FROM_JSON_OBJECT(val, tx.tx, tx); GET_FROM_JSON_OBJECT(val, tx.tx, tx);
GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size); GET_FROM_JSON_OBJECT(val, tx.blob_size, blob_size);
GET_FROM_JSON_OBJECT(val, tx.weight, weight);
GET_FROM_JSON_OBJECT(val, tx.fee, fee); GET_FROM_JSON_OBJECT(val, tx.fee, fee);
GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash); GET_FROM_JSON_OBJECT(val, tx.max_used_block_hash, max_used_block_hash);
GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height); GET_FROM_JSON_OBJECT(val, tx.max_used_block_height, max_used_block_height);
@ -1195,6 +1197,9 @@ void toJsonValue(rapidjson::Document& doc, const cryptonote::rpc::DaemonInfo& in
INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash); INSERT_INTO_JSON_OBJECT(val, doc, top_block_hash, info.top_block_hash);
INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty); INSERT_INTO_JSON_OBJECT(val, doc, cumulative_difficulty, info.cumulative_difficulty);
INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit); INSERT_INTO_JSON_OBJECT(val, doc, block_size_limit, info.block_size_limit);
INSERT_INTO_JSON_OBJECT(val, doc, block_weight_limit, info.block_weight_limit);
INSERT_INTO_JSON_OBJECT(val, doc, block_size_median, info.block_size_median);
INSERT_INTO_JSON_OBJECT(val, doc, block_weight_median, info.block_weight_median);
INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time); INSERT_INTO_JSON_OBJECT(val, doc, start_time, info.start_time);
} }
@ -1221,6 +1226,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::rpc::DaemonInfo& inf
GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash); GET_FROM_JSON_OBJECT(val, info.top_block_hash, top_block_hash);
GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty); GET_FROM_JSON_OBJECT(val, info.cumulative_difficulty, cumulative_difficulty);
GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit); GET_FROM_JSON_OBJECT(val, info.block_size_limit, block_size_limit);
GET_FROM_JSON_OBJECT(val, info.block_weight_limit, block_weight_limit);
GET_FROM_JSON_OBJECT(val, info.block_size_median, block_size_median);
GET_FROM_JSON_OBJECT(val, info.block_weight_median, block_weight_median);
GET_FROM_JSON_OBJECT(val, info.start_time, start_time); GET_FROM_JSON_OBJECT(val, info.start_time, start_time);
} }

View File

@ -86,9 +86,9 @@ typedef cryptonote::simple_wallet sw;
#define EXTENDED_LOGS_FILE "wallet_details.log" #define EXTENDED_LOGS_FILE "wallet_details.log"
#define DEFAULT_MIX 6 #define DEFAULT_MIX 10
#define MIN_RING_SIZE 7 // Used to inform user about min ring size -- does not track actual protocol #define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol
#define LOCK_IDLE_SCOPE() \ #define LOCK_IDLE_SCOPE() \
bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \
@ -829,21 +829,24 @@ bool simple_wallet::print_fee_info(const std::vector<std::string> &args/* = std:
fail_msg_writer() << tr("Cannot connect to daemon"); fail_msg_writer() << tr("Cannot connect to daemon");
return true; return true;
} }
const uint64_t per_kb_fee = m_wallet->get_per_kb_fee(); const bool per_byte = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const uint64_t typical_size_kb = 13; const uint64_t base_fee = m_wallet->get_base_fee();
message_writer() << (boost::format(tr("Current fee is %s %s per kB")) % print_money(per_kb_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point())).str(); const char *base = per_byte ? "byte" : "kB";
const uint64_t typical_size = per_byte ? 2500 : 13;
const uint64_t size_granularity = per_byte ? 1 : 1024;
message_writer() << (boost::format(tr("Current fee is %s %s per %s")) % print_money(base_fee) % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % base).str();
std::vector<uint64_t> fees; std::vector<uint64_t> fees;
for (uint32_t priority = 1; priority <= 4; ++priority) for (uint32_t priority = 1; priority <= 4; ++priority)
{ {
uint64_t mult = m_wallet->get_fee_multiplier(priority); uint64_t mult = m_wallet->get_fee_multiplier(priority);
fees.push_back(per_kb_fee * typical_size_kb * mult); fees.push_back(base_fee * typical_size * mult);
} }
std::vector<std::pair<uint64_t, uint64_t>> blocks; std::vector<std::pair<uint64_t, uint64_t>> blocks;
try try
{ {
uint64_t base_size = typical_size_kb * 1024; uint64_t base_size = typical_size * size_granularity;
blocks = m_wallet->estimate_backlog(base_size, base_size + 1023, fees); blocks = m_wallet->estimate_backlog(base_size, base_size + size_granularity - 1, fees);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -1839,6 +1842,8 @@ bool simple_wallet::set_default_ring_size(const std::vector<std::string> &args/*
if (ring_size != 0 && ring_size != DEFAULT_MIX+1) if (ring_size != 0 && ring_size != DEFAULT_MIX+1)
message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended."); message_writer() << tr("WARNING: this is a non default ring size, which may harm your privacy. Default is recommended.");
else if (ring_size == DEFAULT_MIX)
message_writer() << tr("WARNING: from v8, ring size will be fixed and this setting will be ignored.");
const auto pwd_container = get_and_verify_password(); const auto pwd_container = get_and_verify_password();
if (pwd_container) if (pwd_container)
@ -4704,6 +4709,11 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vector<std::stri
fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true; return true;
} }
if (adjusted_fake_outs_count < fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2; const size_t min_args = (transfer_type == TransferLocked) ? 3 : 2;
if(local_args.size() < min_args) if(local_args.size() < min_args)
@ -5228,6 +5238,11 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vector<st
fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str(); fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true; return true;
} }
if (adjusted_fake_outs_count < fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
uint64_t unlock_block = 0; uint64_t unlock_block = 0;
if (locked) { if (locked) {
@ -5466,12 +5481,28 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
if (fake_outs_count == 0) if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIX; fake_outs_count = DEFAULT_MIX;
} }
else if (ring_size == 0)
{
fail_msg_writer() << tr("Ring size must not be 0");
return true;
}
else else
{ {
fake_outs_count = ring_size - 1; fake_outs_count = ring_size - 1;
local_args.erase(local_args.begin()); local_args.erase(local_args.begin());
} }
} }
uint64_t adjusted_fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
if (adjusted_fake_outs_count > fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too small, minimum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
if (adjusted_fake_outs_count < fake_outs_count)
{
fail_msg_writer() << (boost::format(tr("ring size %u is too large, maximum is %u")) % (fake_outs_count+1) % (adjusted_fake_outs_count+1)).str();
return true;
}
std::vector<uint8_t> extra; std::vector<uint8_t> extra;
bool payment_id_seen = false; bool payment_id_seen = false;

View File

@ -1297,6 +1297,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const
size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin(); size_t fake_outs_count = mixin_count > 0 ? mixin_count : m_wallet->default_mixin();
if (fake_outs_count == 0) if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIXIN; fake_outs_count = DEFAULT_MIXIN;
fake_outs_count = m_wallet->adjust_mixin(fake_outs_count);
uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast<uint32_t>(priority)); uint32_t adjusted_priority = m_wallet->adjust_priority(static_cast<uint32_t>(priority));

View File

@ -50,12 +50,13 @@ void NodeRPCProxy::invalidate()
m_height = 0; m_height = 0;
for (size_t n = 0; n < 256; ++n) for (size_t n = 0; n < 256; ++n)
m_earliest_height[n] = 0; m_earliest_height[n] = 0;
m_dynamic_per_kb_fee_estimate = 0; m_dynamic_base_fee_estimate = 0;
m_dynamic_per_kb_fee_estimate_cached_height = 0; m_dynamic_base_fee_estimate_cached_height = 0;
m_dynamic_per_kb_fee_estimate_grace_blocks = 0; m_dynamic_base_fee_estimate_grace_blocks = 0;
m_fee_quantization_mask = 1;
m_rpc_version = 0; m_rpc_version = 0;
m_target_height = 0; m_target_height = 0;
m_block_size_limit = 0; m_block_weight_limit = 0;
m_get_info_time = 0; m_get_info_time = 0;
} }
@ -99,7 +100,7 @@ boost::optional<std::string> NodeRPCProxy::get_info() const
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get target blockchain height");
m_height = resp_t.height; m_height = resp_t.height;
m_target_height = resp_t.target_height; m_target_height = resp_t.target_height;
m_block_size_limit = resp_t.block_size_limit; m_block_weight_limit = resp_t.block_weight_limit ? resp_t.block_weight_limit : resp_t.block_size_limit;
m_get_info_time = now; m_get_info_time = now;
} }
return boost::optional<std::string>(); return boost::optional<std::string>();
@ -123,12 +124,12 @@ boost::optional<std::string> NodeRPCProxy::get_target_height(uint64_t &height) c
return boost::optional<std::string>(); return boost::optional<std::string>();
} }
boost::optional<std::string> NodeRPCProxy::get_block_size_limit(uint64_t &block_size_limit) const boost::optional<std::string> NodeRPCProxy::get_block_weight_limit(uint64_t &block_weight_limit) const
{ {
auto res = get_info(); auto res = get_info();
if (res) if (res)
return res; return res;
block_size_limit = m_block_size_limit; block_weight_limit = m_block_weight_limit;
return boost::optional<std::string>(); return boost::optional<std::string>();
} }
@ -153,7 +154,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
return boost::optional<std::string>(); return boost::optional<std::string>();
} }
boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const boost::optional<std::string> NodeRPCProxy::get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const
{ {
uint64_t height; uint64_t height;
@ -161,10 +162,10 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
if (result) if (result)
return result; return result;
if (m_dynamic_per_kb_fee_estimate_cached_height != height || m_dynamic_per_kb_fee_estimate_grace_blocks != grace_blocks) if (m_dynamic_base_fee_estimate_cached_height != height || m_dynamic_base_fee_estimate_grace_blocks != grace_blocks)
{ {
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t); cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock(); m_daemon_rpc_mutex.lock();
req_t.grace_blocks = grace_blocks; req_t.grace_blocks = grace_blocks;
@ -173,12 +174,47 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon"); CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate"); CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
m_dynamic_per_kb_fee_estimate = resp_t.fee; m_dynamic_base_fee_estimate = resp_t.fee;
m_dynamic_per_kb_fee_estimate_cached_height = height; m_dynamic_base_fee_estimate_cached_height = height;
m_dynamic_per_kb_fee_estimate_grace_blocks = grace_blocks; m_dynamic_base_fee_estimate_grace_blocks = grace_blocks;
m_fee_quantization_mask = resp_t.quantization_mask;
} }
fee = m_dynamic_per_kb_fee_estimate; fee = m_dynamic_base_fee_estimate;
return boost::optional<std::string>();
}
boost::optional<std::string> NodeRPCProxy::get_fee_quantization_mask(uint64_t &fee_quantization_mask) const
{
uint64_t height;
boost::optional<std::string> result = get_height(height);
if (result)
return result;
if (m_dynamic_base_fee_estimate_cached_height != height)
{
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
m_daemon_rpc_mutex.lock();
req_t.grace_blocks = m_dynamic_base_fee_estimate_grace_blocks;
bool r = net_utils::invoke_http_json_rpc("/json_rpc", "get_fee_estimate", req_t, resp_t, m_http_client, rpc_timeout);
m_daemon_rpc_mutex.unlock();
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get fee estimate");
m_dynamic_base_fee_estimate = resp_t.fee;
m_dynamic_base_fee_estimate_cached_height = height;
m_fee_quantization_mask = resp_t.quantization_mask;
}
fee_quantization_mask = m_fee_quantization_mask;
if (fee_quantization_mask == 0)
{
MERROR("Fee quantization mask is 0, forcing to 1");
fee_quantization_mask = 1;
}
return boost::optional<std::string>(); return boost::optional<std::string>();
} }

View File

@ -47,9 +47,10 @@ public:
boost::optional<std::string> get_height(uint64_t &height) const; boost::optional<std::string> get_height(uint64_t &height) const;
void set_height(uint64_t h); void set_height(uint64_t h);
boost::optional<std::string> get_target_height(uint64_t &height) const; boost::optional<std::string> get_target_height(uint64_t &height) const;
boost::optional<std::string> get_block_size_limit(uint64_t &block_size_limit) const; boost::optional<std::string> get_block_weight_limit(uint64_t &block_weight_limit) const;
boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const; boost::optional<std::string> get_earliest_height(uint8_t version, uint64_t &earliest_height) const;
boost::optional<std::string> get_dynamic_per_kb_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const; boost::optional<std::string> get_dynamic_base_fee_estimate(uint64_t grace_blocks, uint64_t &fee) const;
boost::optional<std::string> get_fee_quantization_mask(uint64_t &fee_quantization_mask) const;
private: private:
boost::optional<std::string> get_info() const; boost::optional<std::string> get_info() const;
@ -59,12 +60,13 @@ private:
mutable uint64_t m_height; mutable uint64_t m_height;
mutable uint64_t m_earliest_height[256]; mutable uint64_t m_earliest_height[256];
mutable uint64_t m_dynamic_per_kb_fee_estimate; mutable uint64_t m_dynamic_base_fee_estimate;
mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height; mutable uint64_t m_dynamic_base_fee_estimate_cached_height;
mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks; mutable uint64_t m_dynamic_base_fee_estimate_grace_blocks;
mutable uint64_t m_fee_quantization_mask;
mutable uint32_t m_rpc_version; mutable uint32_t m_rpc_version;
mutable uint64_t m_target_height; mutable uint64_t m_target_height;
mutable uint64_t m_block_size_limit; mutable uint64_t m_block_weight_limit;
mutable time_t m_get_info_time; mutable time_t m_get_info_time;
}; };

View File

@ -84,8 +84,8 @@ using namespace cryptonote;
// used to choose when to stop adding outputs to a tx // used to choose when to stop adding outputs to a tx
#define APPROXIMATE_INPUT_BYTES 80 #define APPROXIMATE_INPUT_BYTES 80
// used to target a given block size (additional outputs may be added on top to build fee) // used to target a given block weight (additional outputs may be added on top to build fee)
#define TX_SIZE_TARGET(bytes) (bytes*2/3) #define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
// arbitrary, used to generate different hashes from the same input // arbitrary, used to generate different hashes from the same input
#define CHACHA8_KEY_TAIL 0x8c #define CHACHA8_KEY_TAIL 0x8c
@ -183,19 +183,21 @@ uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplie
return kB * fee_per_kb * fee_multiplier; return kB * fee_per_kb * fee_multiplier;
} }
uint64_t calculate_fee(uint64_t fee_per_kb, const cryptonote::blobdata &blob, uint64_t fee_multiplier) uint64_t calculate_fee_from_weight(uint64_t base_fee, uint64_t weight, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{ {
return calculate_fee(fee_per_kb, blob.size(), fee_multiplier); uint64_t fee = weight * base_fee * fee_multiplier;
fee = (fee + fee_quantization_mask - 1) / fee_quantization_mask * fee_quantization_mask;
return fee;
} }
std::string get_size_string(size_t sz) std::string get_weight_string(size_t weight)
{ {
return std::to_string(sz) + " bytes (" + std::to_string((sz + 1023) / 1024) + " kB)"; return std::to_string(weight) + " weight";
} }
std::string get_size_string(const cryptonote::blobdata &tx) std::string get_weight_string(const cryptonote::transaction &tx, size_t blob_size)
{ {
return get_size_string(tx.size()); return get_weight_string(get_transaction_weight(tx, blob_size));
} }
std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variables_map& vm, bool rpc, const options& opts, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@ -576,7 +578,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// rangeSigs // rangeSigs
if (bulletproof) if (bulletproof)
size += ((2*6 + 4 + 5)*32 + 3) * n_outputs; {
size_t log_padded_outputs = 0;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
size += (2 * (6 + log_padded_outputs) + 4 + 5) * 32 + 3;
}
else else
size += (2*64*32+32+64*32) * n_outputs; size += (2*64*32+32+64*32) * n_outputs;
@ -595,23 +602,63 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// txnFee // txnFee
size += 4; size += 4;
LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " with ring size " << (mixin+1) << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)"); LOG_PRINT_L2("estimated " << (bulletproof ? "bulletproof" : "borromean") << " rct tx size for " << n_inputs << " inputs with ring size " << (mixin+1) << " and " << n_outputs << " outputs: " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)");
return size; return size;
} }
size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof) size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{ {
if (use_rct) if (use_rct)
return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size, bulletproof); return estimate_rct_tx_size(n_inputs, mixin, n_outputs, extra_size, bulletproof);
else else
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size; return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
} }
uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{
size_t size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
if (use_rct && bulletproof && n_outputs > 2)
{
const uint64_t bp_base = 368;
size_t log_padded_outputs = 2;
while ((1<<log_padded_outputs) < n_outputs)
++log_padded_outputs;
uint64_t nlr = 2 * (6 + log_padded_outputs);
const uint64_t bp_size = 32 * (9 + nlr);
const uint64_t bp_clawback = (bp_base * (1<<log_padded_outputs) - bp_size) * 4 / 5;
MDEBUG("clawback on size " << size << ": " << bp_clawback);
size += bp_clawback;
}
return size;
}
uint8_t get_bulletproof_fork() uint8_t get_bulletproof_fork()
{ {
return 8; return 8;
} }
uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
if (use_per_byte_fee)
{
const size_t estimated_tx_weight = estimate_tx_weight(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
return calculate_fee_from_weight(base_fee, estimated_tx_weight, fee_multiplier, fee_quantization_mask);
}
else
{
const size_t estimated_tx_size = estimate_tx_size(use_rct, n_inputs, mixin, n_outputs, extra_size, bulletproof);
return calculate_fee(base_fee, estimated_tx_size, fee_multiplier);
}
}
uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, size_t blob_size, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask)
{
if (use_per_byte_fee)
return calculate_fee_from_weight(base_fee, cryptonote::get_transaction_weight(tx, blob_size), fee_multiplier, fee_quantization_mask);
else
return calculate_fee(base_fee, blob_size, fee_multiplier);
}
crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev)
{ {
crypto::hash8 payment_id8 = null_hash8; crypto::hash8 payment_id8 = null_hash8;
@ -822,14 +869,14 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_size_limit, bool ssl, bool trusted_daemon) bool wallet2::init(bool rpc, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
{ {
m_rpc = rpc; m_rpc = rpc;
m_checkpoints.init_default_checkpoints(m_nettype); m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected()) if(m_http_client.is_connected())
m_http_client.disconnect(); m_http_client.disconnect();
m_is_initialized = true; m_is_initialized = true;
m_upper_transaction_size_limit = upper_transaction_size_limit; m_upper_transaction_weight_limit = upper_transaction_weight_limit;
m_daemon_address = std::move(daemon_address); m_daemon_address = std::move(daemon_address);
m_daemon_login = std::move(daemon_login); m_daemon_login = std::move(daemon_login);
m_trusted_daemon = trusted_daemon; m_trusted_daemon = trusted_daemon;
@ -5273,7 +5320,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector<wallet2::pendin
range_proof_type = rct::RangeProofBulletproof; range_proof_type = rct::RangeProofBulletproof;
for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs) for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
if (proof.V.size() > 1) if (proof.V.size() > 1)
range_proof_type = rct::RangeProofMultiOutputBulletproof; range_proof_type = rct::RangeProofPaddedBulletproof;
} }
crypto::secret_key tx_key; crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
@ -5697,7 +5744,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto
range_proof_type = rct::RangeProofBulletproof; range_proof_type = rct::RangeProofBulletproof;
for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs) for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
if (proof.V.size() > 1) if (proof.V.size() > 1)
range_proof_type = rct::RangeProofMultiOutputBulletproof; range_proof_type = rct::RangeProofPaddedBulletproof;
} }
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false); bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, range_proof_type, &msout, false);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype);
@ -5799,9 +5846,18 @@ bool wallet2::sign_multisig_tx_from_file(const std::string &filename, std::vecto
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
{ {
static const uint64_t old_multipliers[3] = {1, 2, 3}; static const struct
static const uint64_t new_multipliers[3] = {1, 20, 166}; {
static const uint64_t newer_multipliers[4] = {1, 4, 20, 166}; size_t count;
uint64_t multipliers[4];
}
multipliers[] =
{
{ 3, {1, 2, 3} },
{ 3, {1, 20, 166} },
{ 4, {1, 4, 20, 166} },
{ 4, {1, 5, 25, 1000} },
};
if (fee_algorithm == -1) if (fee_algorithm == -1)
fee_algorithm = get_fee_algorithm(); fee_algorithm = get_fee_algorithm();
@ -5817,47 +5873,68 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
priority = 1; priority = 1;
} }
THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 3, error::invalid_priority);
// 1 to 3/4 are allowed as priorities // 1 to 3/4 are allowed as priorities
uint32_t max_priority = (fee_algorithm >= 2) ? 4 : 3; const uint32_t max_priority = multipliers[fee_algorithm].count;
if (priority >= 1 && priority <= max_priority) if (priority >= 1 && priority <= max_priority)
{ {
switch (fee_algorithm) return multipliers[fee_algorithm].multipliers[priority-1];
{
case 0: return old_multipliers[priority-1];
case 1: return new_multipliers[priority-1];
case 2: return newer_multipliers[priority-1];
default: THROW_WALLET_EXCEPTION_IF (true, error::invalid_priority);
}
} }
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority); THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
return 1; return 1;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const uint64_t wallet2::get_dynamic_base_fee_estimate() const
{ {
uint64_t fee; uint64_t fee;
boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_per_kb_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee); boost::optional<std::string> result = m_node_rpc_proxy.get_dynamic_base_fee_estimate(FEE_ESTIMATE_GRACE_BLOCKS, fee);
if (!result) if (!result)
return fee; return fee;
LOG_PRINT_L1("Failed to query per kB fee, using " << print_money(FEE_PER_KB)); const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : FEE_PER_KB;
return FEE_PER_KB; LOG_PRINT_L1("Failed to query base fee, using " << print_money(base_fee));
return base_fee;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_per_kb_fee() const uint64_t wallet2::get_base_fee() const
{ {
if(m_light_wallet) if(m_light_wallet)
{
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
return m_light_wallet_per_kb_fee / 1024;
else
return m_light_wallet_per_kb_fee; return m_light_wallet_per_kb_fee;
}
bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1); bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
if (!use_dyn_fee) if (!use_dyn_fee)
return FEE_PER_KB; return FEE_PER_KB;
return get_dynamic_per_kb_fee_estimate(); return get_dynamic_base_fee_estimate();
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_fee_quantization_mask() const
{
if(m_light_wallet)
{
return 1; // TODO
}
bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
if (!use_per_byte_fee)
return 1;
uint64_t fee_quantization_mask;
boost::optional<std::string> result = m_node_rpc_proxy.get_fee_quantization_mask(fee_quantization_mask);
if (result)
return 1;
return fee_quantization_mask;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
int wallet2::get_fee_algorithm() const int wallet2::get_fee_algorithm() const
{ {
// changes at v3 and v5 // changes at v3, v5, v8
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0))
return 3;
if (use_fork_rules(5, 0)) if (use_fork_rules(5, 0))
return 2; return 2;
if (use_fork_rules(3, -720 * 14)) if (use_fork_rules(3, -720 * 14))
@ -5865,19 +5942,39 @@ int wallet2::get_fee_algorithm() const
return 0; return 0;
} }
//------------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_min_ring_size() const
{
if (use_fork_rules(8, 10))
return 11;
if (use_fork_rules(7, 10))
return 7;
if (use_fork_rules(6, 10))
return 5;
if (use_fork_rules(2, 10))
return 3;
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::get_max_ring_size() const
{
if (use_fork_rules(8, 10))
return 11;
return 0;
}
//------------------------------------------------------------------------------------------------------------------------------
uint64_t wallet2::adjust_mixin(uint64_t mixin) const uint64_t wallet2::adjust_mixin(uint64_t mixin) const
{ {
if (mixin < 6 && use_fork_rules(7, 10)) { const uint64_t min_ring_size = get_min_ring_size();
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 7, using 7"); if (mixin + 1 < min_ring_size)
mixin = 6; {
MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
mixin = min_ring_size-1;
} }
else if (mixin < 4 && use_fork_rules(6, 10)) { const uint64_t max_ring_size = get_max_ring_size();
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5"); if (max_ring_size && mixin + 1 > max_ring_size)
mixin = 4; {
} MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
else if (mixin < 2 && use_fork_rules(2, 10)) { mixin = max_ring_size-1;
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
mixin = 2;
} }
return mixin; return mixin;
} }
@ -5889,7 +5986,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
try try
{ {
// check if there's a backlog in the tx pool // check if there's a backlog in the tx pool
const double fee_level = get_fee_multiplier(1) * get_per_kb_fee() * (12/(double)13) / (double)1024; const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(1);
const double fee_level = fee_multiplier * base_fee * (use_per_byte_fee ? 1 : (12/(double)13 / (double)1024));
const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)}); const std::vector<std::pair<uint64_t, uint64_t>> blocks = estimate_backlog({std::make_pair(fee_level, fee_level)});
if (blocks.size() != 1) if (blocks.size() != 1)
{ {
@ -5903,10 +6003,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
} }
// get the current full reward zone // get the current full reward zone
uint64_t block_size_limit = 0; uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info"); throw_on_rpc_response_error(result, "get_info");
const uint64_t full_reward_zone = block_size_limit / 2; const uint64_t full_reward_zone = block_weight_limit / 2;
// get the last N block headers and sum the block sizes // get the last N block headers and sum the block sizes
const size_t N = 10; const size_t N = 10;
@ -5930,14 +6030,14 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
MERROR("Bad blockheaders size"); MERROR("Bad blockheaders size");
return priority; return priority;
} }
size_t block_size_sum = 0; size_t block_weight_sum = 0;
for (const cryptonote::block_header_response &i : getbh_res.headers) for (const cryptonote::block_header_response &i : getbh_res.headers)
{ {
block_size_sum += i.block_size; block_weight_sum += i.block_weight;
} }
// estimate how 'full' the last N blocks are // estimate how 'full' the last N blocks are
const size_t P = 100 * block_size_sum / (N * full_reward_zone); const size_t P = 100 * block_weight_sum / (N * full_reward_zone);
MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str()); MINFO((boost::format("The last %d blocks fill roughly %d%% of the full reward zone.") % N % P).str());
if (P > 80) if (P > 80)
{ {
@ -5963,8 +6063,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
{ {
const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true); const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, true, true);
const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// failsafe split attempt counter // failsafe split attempt counter
size_t attempt_count = 0; size_t attempt_count = 0;
@ -5991,13 +6093,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
pending_tx ptx; pending_tx ptx;
// loop until fee is met without increasing tx size to next KB boundary. // loop until fee is met without increasing tx size to next KB boundary.
const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false); uint64_t needed_fee = estimate_fee(use_per_byte_fee, false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size()+1, extra.size(), false, base_fee, fee_multiplier, fee_quantization_mask);
uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do do
{ {
transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx); transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx);
auto txBlob = t_serializable_object_to_blob(ptx.tx); auto txBlob = t_serializable_object_to_blob(ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
} while (ptx.fee < needed_fee); } while (ptx.fee < needed_fee);
ptx_vector.push_back(ptx); ptx_vector.push_back(ptx);
@ -6967,7 +7068,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs"); THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee; uint64_t needed_money = fee;
LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money)); LOG_PRINT_L2("transfer: starting with fee " << print_money (needed_money));
@ -7072,7 +7173,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBulletproof, m_multisig ? &msout : NULL); bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBulletproof, m_multisig ? &msout : NULL);
LOG_PRINT_L2("constructed tx, r="<<r); LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
std::string key_images; std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
@ -7124,7 +7225,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
// throw if attempting a transaction with no destinations // throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee; uint64_t needed_money = fee;
LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money)); LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money));
LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " ")); LOG_PRINT_L2("selected transfers: " << strjoin(selected_transfers, " "));
@ -7277,7 +7378,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, range_proof_type, m_multisig ? &msout : NULL); bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, range_proof_type, m_multisig ? &msout : NULL);
LOG_PRINT_L2("constructed tx, r="<<r); LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
// work out the permutation done on sources // work out the permutation done on sources
std::vector<size_t> ins_order; std::vector<size_t> ins_order;
@ -7322,7 +7423,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, range_proof_type, &msout, false); bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources_copy_copy, splitted_dsts, change_dts.addr, extra, ms_tx, unlock_time,tx_key, additional_tx_keys, true, range_proof_type, &msout, false);
LOG_PRINT_L2("constructed tx, r="<<r); LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix"); THROW_WALLET_EXCEPTION_IF(cryptonote::get_transaction_prefix_hash(ms_tx) != prefix_hash, error::wallet_internal_error, "Multisig txes do not share prefix");
multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout}); multisig_sigs.push_back({ms_tx.rct_signatures, multisig_signers[signer_index], new_used_L, std::unordered_set<crypto::public_key>(), msout});
@ -7990,11 +8091,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<cryptonote::tx_destination_entry> dsts; std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx; cryptonote::transaction tx;
pending_tx ptx; pending_tx ptx;
size_t bytes; size_t weight;
uint64_t needed_fee; uint64_t needed_fee;
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs; std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
TX() : bytes(0), needed_fee(0) {} TX() : weight(0), needed_fee(0) {}
void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) { void add(const account_public_address &addr, bool is_subaddress, uint64_t amount, unsigned int original_output_index, bool merge_destinations) {
if (merge_destinations) if (merge_destinations)
@ -8022,13 +8123,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<TX> txes; std::vector<TX> txes;
bool adding_fee; // true if new outputs go towards fee, rather than destinations bool adding_fee; // true if new outputs go towards fee, rather than destinations
uint64_t needed_fee, available_for_fee = 0; uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
const bool use_rct = use_fork_rules(4, 0); const bool use_rct = use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBorromean; const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
// throw if attempting a transaction with no destinations // throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination); THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
@ -8059,7 +8162,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// early out if we know we can't make it anyway // early out if we know we can't make it anyway
// we could also check for being within FEE_PER_KB, but if the fee calculation // we could also check for being within FEE_PER_KB, but if the fee calculation
// ever changes, this might be missed, so let this go through // ever changes, this might be missed, so let this go through
const uint64_t min_fee = (fee_multiplier * fee_per_kb * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024; const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof)) / 1024;
uint64_t balance_subtotal = 0; uint64_t balance_subtotal = 0;
uint64_t unlocked_balance_subtotal = 0; uint64_t unlocked_balance_subtotal = 0;
for (uint32_t index_minor : subaddr_indices) for (uint32_t index_minor : subaddr_indices)
@ -8077,11 +8180,11 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("Candidate subaddress index for spending: " << i); LOG_PRINT_L2("Candidate subaddress index for spending: " << i);
// determine threshold for fractional amount // determine threshold for fractional amount
const size_t tx_size_one_ring = estimate_tx_size(use_rct, 1, fake_outs_count, 2, 0, bulletproof); const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, fake_outs_count, 2, 0, bulletproof);
const size_t tx_size_two_rings = estimate_tx_size(use_rct, 2, fake_outs_count, 2, 0, bulletproof); const size_t tx_weight_two_rings = estimate_tx_weight(use_rct, 2, fake_outs_count, 2, 0, bulletproof);
THROW_WALLET_EXCEPTION_IF(tx_size_one_ring > tx_size_two_rings, error::wallet_internal_error, "Estimated tx size with 1 input is larger than with 2 inputs!"); THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring; const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024; const uint64_t fractional_threshold = (fee_multiplier * base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
// gather all dust and non-dust outputs belonging to specified subaddresses // gather all dust and non-dust outputs belonging to specified subaddresses
size_t num_nondust_outputs = 0; size_t num_nondust_outputs = 0;
@ -8174,7 +8277,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{ {
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee. // will get us a known fee.
uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size(), bulletproof), fee_multiplier); uint64_t estimated_fee = estimate_fee(use_per_byte_fee, use_rct, 2, fake_outs_count, 2, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices); preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty()) if (!preferred_inputs.empty())
{ {
@ -8216,7 +8319,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) { while ((!dsts.empty() && dsts[0].amount > 0) || adding_fee || !preferred_inputs.empty() || should_pick_a_second_output(use_rct, txes.back().selected_transfers.size(), *unused_transfers_indices, *unused_dust_indices)) {
TX &tx = txes.back(); TX &tx = txes.back();
LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size()); LOG_PRINT_L2("Start of loop with " << unused_transfers_indices->size() << " " << unused_dust_indices->size() << ", tx.dsts.size() " << tx.dsts.size());
LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " ")); LOG_PRINT_L2("unused_transfers_indices: " << strjoin(*unused_transfers_indices, " "));
LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " ")); LOG_PRINT_L2("unused_dust_indices: " << strjoin(*unused_dust_indices, " "));
LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount))); LOG_PRINT_L2("dsts size " << dsts.size() << ", first " << (dsts.empty() ? "-" : cryptonote::print_money(dsts[0].amount)));
@ -8279,7 +8382,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
} }
else else
{ {
while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit))
{ {
// we can fully pay that destination // we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
@ -8291,7 +8394,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index; ++original_output_index;
} }
if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) { if (available_amount > 0 && !dsts.empty() && estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof) < TX_WEIGHT_TARGET(upper_transaction_weight_limit)) {
// we can partially fill that destination // we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) << LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_nettype, dsts[0].is_subaddress, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount)); " for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@ -8303,7 +8406,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// here, check if we need to sent tx and start a new one // here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit); << upper_transaction_weight_limit);
bool try_tx = false; bool try_tx = false;
// if we have preferred picks, but haven't yet used all of them, continue // if we have preferred picks, but haven't yet used all of them, continue
if (preferred_inputs.empty()) if (preferred_inputs.empty())
@ -8315,8 +8418,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
} }
else else
{ {
const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
} }
} }
@ -8324,8 +8427,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx; cryptonote::transaction test_tx;
pending_tx test_ptx; pending_tx test_ptx;
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); needed_fee = estimate_fee(use_per_byte_fee, use_rct ,tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_multiplier);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
uint64_t inputs = 0, outputs = needed_fee; uint64_t inputs = 0, outputs = needed_fee;
for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount(); for (size_t idx: tx.selected_transfers) inputs += m_transfers[idx].amount();
@ -8347,9 +8449,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0); available_for_fee = test_ptx.fee + test_ptx.change_dts.amount + (!test_ptx.dust_added_to_fee ? test_ptx.dust : 0);
LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" << LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)"); print_money(needed_fee) << " needed)");
if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0) if (needed_fee > available_for_fee && !dsts.empty() && dsts[0].amount > 0)
@ -8390,17 +8492,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx); txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
} }
LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs; tx.outs = outs;
tx.needed_fee = needed_fee; tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee; accumulated_fee += test_ptx.fee;
@ -8475,7 +8577,7 @@ skip_tx:
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
} }
std::vector<wallet2::pending_tx> ptx_vector; std::vector<wallet2::pending_tx> ptx_vector;
@ -8486,7 +8588,7 @@ skip_tx:
for (size_t idx: tx.selected_transfers) for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount(); tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
" " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " << " outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change"); print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx); ptx_vector.push_back(tx.ptx);
@ -8584,22 +8686,24 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<cryptonote::tx_destination_entry> dsts; std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::transaction tx; cryptonote::transaction tx;
pending_tx ptx; pending_tx ptx;
size_t bytes; size_t weight;
uint64_t needed_fee; uint64_t needed_fee;
std::vector<std::vector<get_outs_entry>> outs; std::vector<std::vector<get_outs_entry>> outs;
TX() : bytes(0), needed_fee(0) {} TX() : weight(0), needed_fee(0) {}
}; };
std::vector<TX> txes; std::vector<TX> txes;
uint64_t needed_fee, available_for_fee = 0; uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
std::vector<std::vector<get_outs_entry>> outs; std::vector<std::vector<get_outs_entry>> outs;
const bool use_per_byte_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0); const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean; const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t base_fee = get_base_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs"); LOG_PRINT_L2("Starting with " << unused_transfers_indices.size() << " non-dust outputs and " << unused_dust_indices.size() << " dust outputs");
@ -8621,7 +8725,25 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// get a random unspent output and use it to pay next chunk. We try to alternate // get a random unspent output and use it to pay next chunk. We try to alternate
// dust and non dust to ensure we never get with only dust, from which we might // dust and non dust to ensure we never get with only dust, from which we might
// get a tx that can't pay for itself // get a tx that can't pay for itself
size_t idx = unused_transfers_indices.empty() ? pop_best_value(unused_dust_indices, tx.selected_transfers) : unused_dust_indices.empty() ? pop_best_value(unused_transfers_indices, tx.selected_transfers) : ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_per_kb * fee_multiplier * (upper_transaction_size_limit + 1023) / 1024) ? pop_best_value(unused_dust_indices, tx.selected_transfers) : pop_best_value(unused_transfers_indices, tx.selected_transfers); uint64_t fee_dust_threshold;
if (use_fork_rules(HF_VERSION_PER_BYTE_FEE))
{
const uint64_t estimated_tx_weight_with_one_extra_output = estimate_tx_weight(use_rct, tx.selected_transfers.size() + 1, fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof);
fee_dust_threshold = calculate_fee_from_weight(base_fee, estimated_tx_weight_with_one_extra_output, fee_multiplier, fee_quantization_mask);
}
else
{
fee_dust_threshold = base_fee * fee_multiplier * (upper_transaction_weight_limit + 1023) / 1024;
}
size_t idx =
unused_transfers_indices.empty()
? pop_best_value(unused_dust_indices, tx.selected_transfers)
: unused_dust_indices.empty()
? pop_best_value(unused_transfers_indices, tx.selected_transfers)
: ((tx.selected_transfers.size() & 1) || accumulated_outputs > fee_dust_threshold)
? pop_best_value(unused_dust_indices, tx.selected_transfers)
: pop_best_value(unused_transfers_indices, tx.selected_transfers);
const transfer_details &td = m_transfers[idx]; const transfer_details &td = m_transfers[idx];
LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount())); LOG_PRINT_L2("Picking output " << idx << ", amount " << print_money(td.amount()));
@ -8636,16 +8758,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one // here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit " LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit); << upper_transaction_weight_limit);
const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof); const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 2, extra.size(), bulletproof);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit)); bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
if (try_tx) { if (try_tx) {
cryptonote::transaction test_tx; cryptonote::transaction test_tx;
pending_tx test_ptx; pending_tx test_ptx;
const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof); needed_fee = estimate_fee(use_per_byte_fee, use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof, base_fee, fee_multiplier, fee_quantization_mask);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress)); tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
@ -8658,9 +8779,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount; available_for_fee = test_ptx.fee + test_ptx.dests[0].amount + test_ptx.change_dts.amount;
LOG_PRINT_L2("Made a " << get_size_string(txBlob) << " tx, with " << print_money(available_for_fee) << " available for fee (" << LOG_PRINT_L2("Made a " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(available_for_fee) << " available for fee (" <<
print_money(needed_fee) << " needed)"); print_money(needed_fee) << " needed)");
THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself"); THROW_WALLET_EXCEPTION_IF(needed_fee > available_for_fee, error::wallet_internal_error, "Transaction cannot pay for itself");
@ -8675,17 +8796,17 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx); detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx); txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_multiplier, fee_quantization_mask);
LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made an attempt at a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
} while (needed_fee > test_ptx.fee); } while (needed_fee > test_ptx.fee);
LOG_PRINT_L2("Made a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) << LOG_PRINT_L2("Made a final " << get_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change"); " fee and " << print_money(test_ptx.change_dts.amount) << " change");
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs; tx.outs = outs;
tx.needed_fee = needed_fee; tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee; accumulated_fee += test_ptx.fee;
@ -8717,7 +8838,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
auto txBlob = t_serializable_object_to_blob(test_ptx.tx); auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx; tx.tx = test_tx;
tx.ptx = test_ptx; tx.ptx = test_ptx;
tx.bytes = txBlob.size(); tx.weight = get_transaction_weight(test_tx, txBlob.size());
} }
std::vector<wallet2::pending_tx> ptx_vector; std::vector<wallet2::pending_tx> ptx_vector;
@ -8728,7 +8849,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
for (size_t idx: tx.selected_transfers) for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount(); tx_money += m_transfers[idx].amount();
LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() << LOG_PRINT_L1(" Transaction " << (1+std::distance(txes.begin(), i)) << "/" << txes.size() <<
" " << get_transaction_hash(tx.ptx.tx) << ": " << get_size_string(tx.bytes) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() << " " << get_transaction_hash(tx.ptx.tx) << ": " << get_weight_string(tx.weight) << ", sending " << print_money(tx_money) << " in " << tx.selected_transfers.size() <<
" outputs to " << tx.dsts.size() << " destination(s), including " << " outputs to " << tx.dsts.size() << " destination(s), including " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change"); print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx); ptx_vector.push_back(tx.ptx);
@ -8763,11 +8884,14 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
return close_enough; return close_enough;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_upper_transaction_size_limit() const uint64_t wallet2::get_upper_transaction_weight_limit() const
{ {
if (m_upper_transaction_size_limit > 0) if (m_upper_transaction_weight_limit > 0)
return m_upper_transaction_size_limit; return m_upper_transaction_weight_limit;
uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; uint64_t full_reward_zone = use_fork_rules(5, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V5 : use_fork_rules(2, 10) ? CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 : CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
if (use_fork_rules(8, 10))
return full_reward_zone / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
else
return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
@ -8874,16 +8998,14 @@ const wallet2::transfer_details &wallet2::get_transfer_details(size_t idx) const
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_unmixable_outputs() std::vector<size_t> wallet2::select_available_unmixable_outputs()
{ {
// request all outputs with less than 3 instances // request all outputs with less instances than the min ring size
const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
return select_available_outputs_from_histogram(min_mixin + 1, false, true, false);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_mixable_outputs() std::vector<size_t> wallet2::select_available_mixable_outputs()
{ {
// request all outputs with at least 3 instances, so we can use mixin 2 with // request all outputs with at least as many instances as the min ring size
const size_t min_mixin = use_fork_rules(7, 10) ? 6 : use_fork_rules(6, 10) ? 4 : 2; // v6 increases min mixin from 2 to 4, v7 to 6 return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true);
return select_available_outputs_from_histogram(min_mixin + 1, true, true, true);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions() std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
@ -8892,7 +9014,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2 const bool hf1_rules = use_fork_rules(2, 10); // first hard fork has version 2
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD); tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
const uint64_t fee_per_kb = get_per_kb_fee(); const uint64_t base_fee = get_base_fee();
// may throw // may throw
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(); std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs();
@ -8907,7 +9029,7 @@ std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions()
std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs; std::vector<size_t> unmixable_transfer_outputs, unmixable_dust_outputs;
for (auto n: unmixable_outputs) for (auto n: unmixable_outputs)
{ {
if (m_transfers[n].amount() < fee_per_kb) if (m_transfers[n].amount() < base_fee)
unmixable_dust_outputs.push_back(n); unmixable_dust_outputs.push_back(n);
else else
unmixable_transfer_outputs.push_back(n); unmixable_transfer_outputs.push_back(n);
@ -11228,46 +11350,46 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(const std::
THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_txpool_backlog");
THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_tx_pool_error);
uint64_t block_size_limit = 0; uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit); const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info"); throw_on_rpc_response_error(result, "get_info");
uint64_t full_reward_zone = block_size_limit / 2; uint64_t full_reward_zone = block_weight_limit / 2;
THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon"); THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block weight limit from daemon");
std::vector<std::pair<uint64_t, uint64_t>> blocks; std::vector<std::pair<uint64_t, uint64_t>> blocks;
for (const auto &fee_level: fee_levels) for (const auto &fee_level: fee_levels)
{ {
const double our_fee_byte_min = fee_level.first; const double our_fee_byte_min = fee_level.first;
const double our_fee_byte_max = fee_level.second; const double our_fee_byte_max = fee_level.second;
uint64_t priority_size_min = 0, priority_size_max = 0; uint64_t priority_weight_min = 0, priority_weight_max = 0;
for (const auto &i: res.backlog) for (const auto &i: res.backlog)
{ {
if (i.blob_size == 0) if (i.weight == 0)
{ {
MWARNING("Got 0 sized blob from txpool, ignored"); MWARNING("Got 0 weight tx from txpool, ignored");
continue; continue;
} }
double this_fee_byte = i.fee / (double)i.blob_size; double this_fee_byte = i.fee / (double)i.weight;
if (this_fee_byte >= our_fee_byte_min) if (this_fee_byte >= our_fee_byte_min)
priority_size_min += i.blob_size; priority_weight_min += i.weight;
if (this_fee_byte >= our_fee_byte_max) if (this_fee_byte >= our_fee_byte_max)
priority_size_max += i.blob_size; priority_weight_max += i.weight;
} }
uint64_t nblocks_min = priority_size_min / full_reward_zone; uint64_t nblocks_min = priority_weight_min / full_reward_zone;
uint64_t nblocks_max = priority_size_max / full_reward_zone; uint64_t nblocks_max = priority_weight_max / full_reward_zone;
MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for " MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
<< our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, " << our_fee_byte_min << " - " << our_fee_byte_max << " piconero byte fee, "
<< nblocks_min << " - " << nblocks_max << " blocks at block size " << full_reward_zone); << nblocks_min << " - " << nblocks_max << " blocks at block weight " << full_reward_zone);
blocks.push_back(std::make_pair(nblocks_min, nblocks_max)); blocks.push_back(std::make_pair(nblocks_min, nblocks_max));
} }
return blocks; return blocks;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees) std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees)
{ {
THROW_WALLET_EXCEPTION_IF(min_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee"); THROW_WALLET_EXCEPTION_IF(min_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
THROW_WALLET_EXCEPTION_IF(max_blob_size == 0, error::wallet_internal_error, "Invalid 0 fee"); THROW_WALLET_EXCEPTION_IF(max_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
for (uint64_t fee: fees) for (uint64_t fee: fees)
{ {
THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee"); THROW_WALLET_EXCEPTION_IF(fee == 0, error::wallet_internal_error, "Invalid 0 fee");
@ -11275,7 +11397,7 @@ std::vector<std::pair<uint64_t, uint64_t>> wallet2::estimate_backlog(uint64_t mi
std::vector<std::pair<double, double>> fee_levels; std::vector<std::pair<double, double>> fee_levels;
for (uint64_t fee: fees) for (uint64_t fee: fees)
{ {
double our_fee_byte_min = fee / (double)min_blob_size, our_fee_byte_max = fee / (double)max_blob_size; double our_fee_byte_min = fee / (double)min_tx_weight, our_fee_byte_max = fee / (double)max_tx_weight;
fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max); fee_levels.emplace_back(our_fee_byte_min, our_fee_byte_max);
} }
return estimate_backlog(fee_levels); return estimate_backlog(fee_levels);

View File

@ -630,14 +630,9 @@ namespace tools
void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;} void explicit_refresh_from_block_height(bool expl) {m_explicit_refresh_from_block_height = expl;}
bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;}
// upper_transaction_size_limit as defined below is set to
// approximately 125% of the fixed minimum allowable penalty
// free block size. TODO: fix this so that it actually takes
// into account the current median block size rather than
// the minimum block size.
bool deinit(); bool deinit();
bool init(bool rpc, std::string daemon_address = "http://localhost:8080", bool init(bool rpc, std::string daemon_address = "http://localhost:8080",
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false, bool trusted_daemon = false); boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false);
void stop() { m_run.store(false, std::memory_order_relaxed); } void stop() { m_run.store(false, std::memory_order_relaxed); }
@ -1084,10 +1079,13 @@ namespace tools
bool is_synced() const; bool is_synced() const;
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels); std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(const std::vector<std::pair<double, double>> &fee_levels);
std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_blob_size, uint64_t max_blob_size, const std::vector<uint64_t> &fees); std::vector<std::pair<uint64_t, uint64_t>> estimate_backlog(uint64_t min_tx_weight, uint64_t max_tx_weight, const std::vector<uint64_t> &fees);
uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const; uint64_t get_fee_multiplier(uint32_t priority, int fee_algorithm = -1) const;
uint64_t get_per_kb_fee() const; uint64_t get_base_fee() const;
uint64_t get_fee_quantization_mask() const;
uint64_t get_min_ring_size() const;
uint64_t get_max_ring_size() const;
uint64_t adjust_mixin(uint64_t mixin) const; uint64_t adjust_mixin(uint64_t mixin) const;
uint32_t adjust_priority(uint32_t priority); uint32_t adjust_priority(uint32_t priority);
@ -1212,9 +1210,9 @@ namespace tools
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const; void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_size_limit() const; uint64_t get_upper_transaction_weight_limit() const;
std::vector<uint64_t> get_unspent_amounts_vector() const; std::vector<uint64_t> get_unspent_amounts_vector() const;
uint64_t get_dynamic_per_kb_fee_estimate() const; uint64_t get_dynamic_base_fee_estimate() const;
float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const; float get_output_relatedness(const transfer_details &td0, const transfer_details &td1) const;
std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const; std::vector<size_t> pick_preferred_rct_inputs(uint64_t needed_money, uint32_t subaddr_account, const std::set<uint32_t> &subaddr_indices) const;
void set_spent(size_t idx, uint64_t height); void set_spent(size_t idx, uint64_t height);
@ -1269,7 +1267,7 @@ namespace tools
std::unordered_map<std::string, std::string> m_attributes; std::unordered_map<std::string, std::string> m_attributes;
std::vector<tools::wallet2::address_book_row> m_address_book; std::vector<tools::wallet2::address_book_row> m_address_book;
std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags; std::pair<std::map<std::string, std::string>, std::vector<std::string>> m_account_tags;
uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value uint64_t m_upper_transaction_weight_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value
const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info; const std::vector<std::vector<tools::wallet2::multisig_info>> *m_multisig_rescan_info;
const std::vector<std::vector<rct::key>> *m_multisig_rescan_k; const std::vector<std::vector<rct::key>> *m_multisig_rescan_k;
@ -1838,7 +1836,7 @@ namespace tools
THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs"); THROW_WALLET_EXCEPTION_IF(m_multisig, error::wallet_internal_error, "Multisig wallets cannot spend non rct outputs");
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit(); uint64_t upper_transaction_weight_limit = get_upper_transaction_weight_limit();
uint64_t needed_money = fee; uint64_t needed_money = fee;
// calculate total amount being sent to all destinations // calculate total amount being sent to all destinations
@ -1970,7 +1968,7 @@ namespace tools
rct::multisig_out msout; rct::multisig_out msout;
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL); bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, false, rct::RangeProofBorromean, m_multisig ? &msout : NULL);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_nettype);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit); THROW_WALLET_EXCEPTION_IF(upper_transaction_weight_limit <= get_transaction_weight(tx), error::tx_too_big, tx, upper_transaction_weight_limit);
std::string key_images; std::string key_images;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool

View File

@ -679,30 +679,30 @@ namespace tools
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct tx_too_big : public transfer_error struct tx_too_big : public transfer_error
{ {
explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_size_limit) explicit tx_too_big(std::string&& loc, const cryptonote::transaction& tx, uint64_t tx_weight_limit)
: transfer_error(std::move(loc), "transaction is too big") : transfer_error(std::move(loc), "transaction is too big")
, m_tx(tx) , m_tx(tx)
, m_tx_size_limit(tx_size_limit) , m_tx_weight_limit(tx_weight_limit)
{ {
} }
const cryptonote::transaction& tx() const { return m_tx; } const cryptonote::transaction& tx() const { return m_tx; }
uint64_t tx_size_limit() const { return m_tx_size_limit; } uint64_t tx_weight_limit() const { return m_tx_weight_limit; }
std::string to_string() const std::string to_string() const
{ {
std::ostringstream ss; std::ostringstream ss;
cryptonote::transaction tx = m_tx; cryptonote::transaction tx = m_tx;
ss << transfer_error::to_string() << ss << transfer_error::to_string() <<
", tx_size_limit = " << m_tx_size_limit << ", tx_weight_limit = " << m_tx_weight_limit <<
", tx size = " << get_object_blobsize(m_tx) << ", tx weight = " << get_transaction_weight(m_tx) <<
", tx:\n" << cryptonote::obj_to_json_str(tx); ", tx:\n" << cryptonote::obj_to_json_str(tx);
return ss.str(); return ss.str();
} }
private: private:
cryptonote::transaction m_tx; cryptonote::transaction m_tx;
uint64_t m_tx_size_limit; uint64_t m_tx_weight_limit;
}; };
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
struct zero_destination : public transfer_error struct zero_destination : public transfer_error

View File

@ -1542,7 +1542,6 @@ namespace tools
rpc_transfers.spent = td.m_spent; rpc_transfers.spent = td.m_spent;
rpc_transfers.global_index = td.m_global_output_index; rpc_transfers.global_index = td.m_global_output_index;
rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid); rpc_transfers.tx_hash = epee::string_tools::pod_to_hex(td.m_txid);
rpc_transfers.tx_size = txBlob.size();
rpc_transfers.subaddr_index = td.m_subaddr_index.minor; rpc_transfers.subaddr_index = td.m_subaddr_index.minor;
rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : ""; rpc_transfers.key_image = req.verbose && td.m_key_image_known ? epee::string_tools::pod_to_hex(td.m_key_image) : "";
res.transfers.push_back(rpc_transfers); res.transfers.push_back(rpc_transfers);

View File

@ -865,7 +865,6 @@ namespace wallet_rpc
bool spent; bool spent;
uint64_t global_index; uint64_t global_index;
std::string tx_hash; std::string tx_hash;
uint64_t tx_size;
uint32_t subaddr_index; uint32_t subaddr_index;
std::string key_image; std::string key_image;
@ -874,7 +873,6 @@ namespace wallet_rpc
KV_SERIALIZE(spent) KV_SERIALIZE(spent)
KV_SERIALIZE(global_index) KV_SERIALIZE(global_index)
KV_SERIALIZE(tx_hash) KV_SERIALIZE(tx_hash)
KV_SERIALIZE(tx_size)
KV_SERIALIZE(subaddr_index) KV_SERIALIZE(subaddr_index)
KV_SERIALIZE(key_image) KV_SERIALIZE(key_image)
END_KV_SERIALIZE_MAP() END_KV_SERIALIZE_MAP()

View File

@ -36,24 +36,24 @@ using namespace cryptonote;
namespace namespace
{ {
bool construct_miner_tx_by_size(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins, bool construct_miner_tx_by_weight(transaction& miner_tx, uint64_t height, uint64_t already_generated_coins,
const account_public_address& miner_address, std::vector<size_t>& block_sizes, size_t target_tx_size, const account_public_address& miner_address, std::vector<size_t>& block_weights, size_t target_tx_weight,
size_t target_block_size, uint64_t fee = 0) size_t target_block_weight, uint64_t fee = 0)
{ {
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, fee, miner_address, miner_tx)) if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, fee, miner_address, miner_tx))
return false; return false;
size_t current_size = get_object_blobsize(miner_tx); size_t current_weight = get_transaction_weight(miner_tx);
size_t try_count = 0; size_t try_count = 0;
while (target_tx_size != current_size) while (target_tx_weight != current_weight)
{ {
++try_count; ++try_count;
if (10 < try_count) if (10 < try_count)
return false; return false;
if (target_tx_size < current_size) if (target_tx_weight < current_weight)
{ {
size_t diff = current_size - target_tx_size; size_t diff = current_weight - target_tx_weight;
if (diff <= miner_tx.extra.size()) if (diff <= miner_tx.extra.size())
miner_tx.extra.resize(miner_tx.extra.size() - diff); miner_tx.extra.resize(miner_tx.extra.size() - diff);
else else
@ -61,28 +61,28 @@ namespace
} }
else else
{ {
size_t diff = target_tx_size - current_size; size_t diff = target_tx_weight - current_weight;
miner_tx.extra.resize(miner_tx.extra.size() + diff); miner_tx.extra.resize(miner_tx.extra.size() + diff);
} }
current_size = get_object_blobsize(miner_tx); current_weight = get_transaction_weight(miner_tx);
} }
return true; return true;
} }
bool construct_max_size_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account, bool construct_max_weight_block(test_generator& generator, block& blk, const block& blk_prev, const account_base& miner_account,
size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW) size_t median_block_count = CRYPTONOTE_REWARD_BLOCKS_WINDOW)
{ {
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_prev), median_block_count); generator.get_last_n_block_weights(block_weights, get_block_hash(blk_prev), median_block_count);
size_t median = misc_utils::median(block_sizes); size_t median = misc_utils::median(block_weights);
median = std::max(median, static_cast<size_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1)); median = std::max(median, static_cast<size_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1));
transaction miner_tx; transaction miner_tx;
bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev), bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_prev) + 1, generator.get_already_generated_coins(blk_prev),
miner_account.get_keys().m_account_address, block_sizes, 2 * median, 2 * median); miner_account.get_keys().m_account_address, block_weights, 2 * median, 2 * median);
if (!r) if (!r)
return false; return false;
@ -97,7 +97,7 @@ namespace
for (size_t i = 0; i < block_count; ++i) for (size_t i = 0; i < block_count; ++i)
{ {
block blk_i; block blk_i;
if (!construct_max_size_block(generator, blk_i, blk, miner_account)) if (!construct_max_weight_block(generator, blk_i, blk, miner_account))
return false; return false;
events.push_back(blk_i); events.push_back(blk_i);
@ -141,18 +141,18 @@ bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
// Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks // Test: block reward is calculated using median of the latest CRYPTONOTE_REWARD_BLOCKS_WINDOW blocks
DO_CALLBACK(events, "mark_invalid_block"); DO_CALLBACK(events, "mark_invalid_block");
block blk_1_bad_1; block blk_1_bad_1;
if (!construct_max_size_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1)) if (!construct_max_weight_block(generator, blk_1_bad_1, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW + 1))
return false; return false;
events.push_back(blk_1_bad_1); events.push_back(blk_1_bad_1);
DO_CALLBACK(events, "mark_invalid_block"); DO_CALLBACK(events, "mark_invalid_block");
block blk_1_bad_2; block blk_1_bad_2;
if (!construct_max_size_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1)) if (!construct_max_weight_block(generator, blk_1_bad_2, blk_0r, miner_account, CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1))
return false; return false;
events.push_back(blk_1_bad_2); events.push_back(blk_1_bad_2);
block blk_1; block blk_1;
if (!construct_max_size_block(generator, blk_1, blk_0r, miner_account)) if (!construct_max_weight_block(generator, blk_1, blk_0r, miner_account))
return false; return false;
events.push_back(blk_1); events.push_back(blk_1);
@ -187,16 +187,16 @@ bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
{ {
transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE); transaction tx_1 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 11 * TESTS_DEFAULT_FEE);
transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE); transaction tx_2 = construct_tx_with_fee(events, blk_5, miner_account, bob_account, MK_COINS(1), 13 * TESTS_DEFAULT_FEE);
size_t txs_1_size = get_object_blobsize(tx_1) + get_object_blobsize(tx_2); size_t txs_1_weight = get_transaction_weight(tx_1) + get_transaction_weight(tx_2);
uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2); uint64_t txs_fee = get_tx_fee(tx_1) + get_tx_fee(tx_2);
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW); generator.get_last_n_block_weights(block_weights, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
size_t median = misc_utils::median(block_sizes); size_t median = misc_utils::median(block_weights);
transaction miner_tx; transaction miner_tx;
bool r = construct_miner_tx_by_size(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7), bool r = construct_miner_tx_by_weight(miner_tx, get_block_height(blk_7) + 1, generator.get_already_generated_coins(blk_7),
miner_account.get_keys().m_account_address, block_sizes, 2 * median - txs_1_size, 2 * median, txs_fee); miner_account.get_keys().m_account_address, block_weights, 2 * median - txs_1_weight, 2 * median, txs_fee);
if (!r) if (!r)
return false; return false;
@ -206,7 +206,7 @@ bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
block blk_8; block blk_8;
generator.construct_block_manually(blk_8, blk_7, miner_account, test_generator::bf_miner_tx | test_generator::bf_tx_hashes, generator.construct_block_manually(blk_8, blk_7, miner_account, test_generator::bf_miner_tx | test_generator::bf_tx_hashes,
0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_size); 0, 0, 0, crypto::hash(), 0, miner_tx, txs_1_hashes, txs_1_weight);
events.push_back(blk_8); events.push_back(blk_8);
DO_CALLBACK(events, "mark_checked_block"); DO_CALLBACK(events, "mark_checked_block");

View File

@ -586,11 +586,11 @@ bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& ev
block blk_test; block blk_test;
std::vector<crypto::hash> tx_hashes; std::vector<crypto::hash> tx_hashes;
tx_hashes.push_back(get_transaction_hash(tx_0)); tx_hashes.push_back(get_transaction_hash(tx_0));
size_t txs_size = get_object_blobsize(tx_0); size_t txs_weight = get_transaction_weight(tx_0);
diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1); diffic = next_difficulty(timestamps, cummulative_difficulties,DIFFICULTY_TARGET_V1);
if (!generator.construct_block_manually(blk_test, blk_last, miner_account, if (!generator.construct_block_manually(blk_test, blk_last, miner_account,
test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp, test_generator::bf_diffic | test_generator::bf_timestamp | test_generator::bf_tx_hashes, 0, 0, blk_last.timestamp,
crypto::hash(), diffic, transaction(), tx_hashes, txs_size)) crypto::hash(), diffic, transaction(), tx_hashes, txs_weight))
return false; return false;
blobdata blob = t_serializable_object_to_blob(blk_test); blobdata blob = t_serializable_object_to_blob(blk_test);

View File

@ -42,7 +42,7 @@ using namespace cryptonote;
// Tests // Tests
bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& events, bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& events,
const int *out_idx, int mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const bool *multi_out, size_t mixin, size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RangeProofType *range_proof_type,
const std::function<bool(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations, size_t tx_idx)> &pre_tx, const std::function<bool(std::vector<tx_source_entry> &sources, std::vector<tx_destination_entry> &destinations, size_t tx_idx)> &pre_tx,
const std::function<bool(transaction &tx, size_t tx_idx)> &post_tx) const const std::function<bool(transaction &tx, size_t tx_idx)> &post_tx) const
{ {
@ -51,11 +51,11 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
GENERATE_ACCOUNT(miner_account); GENERATE_ACCOUNT(miner_account);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start); MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
// create 8 miner accounts, and have them mine the next 8 blocks // create 12 miner accounts, and have them mine the next 12 blocks
cryptonote::account_base miner_accounts[8]; cryptonote::account_base miner_accounts[12];
const cryptonote::block *prev_block = &blk_0; const cryptonote::block *prev_block = &blk_0;
cryptonote::block blocks[8 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW]; cryptonote::block blocks[12 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW];
for (size_t n = 0; n < 8; ++n) { for (size_t n = 0; n < 12; ++n) {
miner_accounts[n].generate(); miner_accounts[n].generate();
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n], CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[n], *prev_block, miner_accounts[n],
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
@ -70,16 +70,16 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
// rewind // rewind
cryptonote::block blk_r, blk_last; cryptonote::block blk_r, blk_last;
{ {
blk_last = blocks[7]; blk_last = blocks[11];
for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i) for (size_t i = 0; i < CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; ++i)
{ {
CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[8+i], blk_last, miner_account, CHECK_AND_ASSERT_MES(generator.construct_block_manually(blocks[12+i], blk_last, miner_account,
test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version, test_generator::bf_major_ver | test_generator::bf_minor_ver | test_generator::bf_timestamp | test_generator::bf_hf_version,
2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long 2, 2, blk_last.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN * 2, // v2 has blocks twice as long
crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2), crypto::hash(), 0, transaction(), std::vector<crypto::hash>(), 0, 0, 2),
false, "Failed to generate block"); false, "Failed to generate block");
events.push_back(blocks[8+i]); events.push_back(blocks[12+i]);
blk_last = blocks[8+i]; blk_last = blocks[12+i];
} }
blk_r = blk_last; blk_r = blk_last;
} }
@ -99,7 +99,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
const uint64_t needed_amount = input_amounts_available[n]; const uint64_t needed_amount = input_amounts_available[n];
src.amount = input_amounts_available[n]; src.amount = input_amounts_available[n];
size_t real_index_in_tx = 0; size_t real_index_in_tx = 0;
for (size_t m = 0; m < 7; ++m) { for (size_t m = 0; m <= mixin; ++m) {
size_t index_in_tx = 0; size_t index_in_tx = 0;
for (size_t i = 0; i < blocks[m].miner_tx.vout.size(); ++i) for (size_t i = 0; i < blocks[m].miner_tx.vout.size(); ++i)
if (blocks[m].miner_tx.vout[i].amount == needed_amount) if (blocks[m].miner_tx.vout[i].amount == needed_amount)
@ -136,7 +136,7 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0}; subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
rct_txes.resize(rct_txes.size() + 1); rct_txes.resize(rct_txes.size() + 1);
bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, multi_out[n] ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBulletproof); bool r = construct_tx_and_get_tx_key(miner_accounts[n].get_keys(), subaddresses, sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), rct_txes.back(), 0, tx_key, additional_tx_keys, true, range_proof_type[n]);
CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction"); CHECK_AND_ASSERT_MES(r, false, "failed to construct transaction");
if (post_tx && !post_tx(rct_txes.back(), n)) if (post_tx && !post_tx(rct_txes.back(), n))
@ -199,93 +199,100 @@ bool gen_bp_tx_validation_base::check_bp(const cryptonote::transaction &tx, size
while (sizes[n_sizes] != (size_t)-1) while (sizes[n_sizes] != (size_t)-1)
n_amounts += sizes[n_sizes++]; n_amounts += sizes[n_sizes++];
CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == n_sizes); CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == n_sizes);
CHECK_TEST_CONDITION(rct::n_bulletproof_amounts(tx.rct_signatures.p.bulletproofs) == n_amounts); CHECK_TEST_CONDITION(rct::n_bulletproof_max_amounts(tx.rct_signatures.p.bulletproofs) == n_amounts);
for (size_t n = 0; n < n_sizes; ++n) for (size_t n = 0; n < n_sizes; ++n)
CHECK_TEST_CONDITION(rct::n_bulletproof_amounts(tx.rct_signatures.p.bulletproofs[n]) == sizes[n]); CHECK_TEST_CONDITION(rct::n_bulletproof_max_amounts(tx.rct_signatures.p.bulletproofs[n]) == sizes[n]);
return true; return true;
} }
bool gen_bp_tx_valid_1::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_valid_1::generate(std::vector<test_event_entry>& events) const
{ {
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const size_t bp_sizes[] = {1, (size_t)-1}; const size_t bp_sizes[] = {1, (size_t)-1};
const bool multi_out[] = {false}; const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof};
return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); }); return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_1"); });
} }
bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) const
{ {
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {1, 1, (size_t)-1}; const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
const bool multi_out[] = {false}; return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL);
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_invalid_1_1"); });
} }
bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
{ {
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1};
const bool multi_out[] = {true}; const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof};
return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); }); return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_2"); });
} }
bool gen_bp_tx_valid_4_2_1::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_valid_3::generate(std::vector<test_event_entry>& events) const
{ {
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1}; const uint64_t amounts_paid[] = {5000, 5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {4, (size_t)-1};
const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof };
return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_3"); });
}
bool gen_bp_tx_valid_16::generate(std::vector<test_event_entry>& events) const
{
const size_t mixin = 10;
const uint64_t amounts_paid[] = {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, (uint64_t)-1};
const size_t bp_sizes[] = {16, (size_t)-1};
const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof };
return generate_with(events, mixin, 1, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16"); });
}
bool gen_bp_tx_invalid_4_2_1::generate(std::vector<test_event_entry>& events) const
{
const size_t mixin = 10;
const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
const size_t bp_sizes[] = {4, 2, 1, (size_t)-1}; const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof };
const bool multi_out[] = {true}; return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL);
return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_4_2_1"); });
} }
bool gen_bp_tx_valid_16_16::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_invalid_16_16::generate(std::vector<test_event_entry>& events) const
{ {
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; const uint64_t amounts_paid[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
const size_t bp_sizes[] = {16, 16, (size_t)-1}; const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof };
const bool multi_out[] = {true}; return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL);
return generate_with(events, out_idx, mixin, 1, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_tx_valid_16_16"); });
} }
bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const
{ {
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1}; const uint64_t amounts_paid[] = {1000, 1000, (size_t)-1, 1000, 1000, (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1}; const size_t bp_sizes[] = {2, (size_t)-1, 2, (size_t)-1};
const bool multi_out[] = {true}; const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof};
return generate_with(events, out_idx, mixin, 2, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_2"); }); return generate_with(events, mixin, 2, amounts_paid, true, range_proof_type, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_2_and_2"); });
} }
bool gen_bp_txs_valid_2_and_8_2_and_16_16_1::generate(std::vector<test_event_entry>& events) const bool gen_bp_txs_invalid_2_and_8_2_and_16_16_1::generate(std::vector<test_event_entry>& events) const
{ {
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; const uint64_t amounts_paid[] = {1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1};
const bool multi_out[] = {true, true, true}; const rct::RangeProofType range_proof_type[] = {rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof};
const size_t bp_sizes[] = {2, (size_t)-1, 8, 2, (size_t)-1, 16, 16, 1, (size_t)-1}; return generate_with(events, mixin, 3, amounts_paid, false, range_proof_type, NULL, NULL);
return generate_with(events, out_idx, mixin, 3, amounts_paid, true, multi_out, NULL, [&](const cryptonote::transaction &tx, size_t tx_idx){ return check_bp(tx, tx_idx, bp_sizes, "gen_bp_txs_valid_1_1_and_8_2_and_16_16_1"); });
} }
bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>& events) const
{ {
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs"); DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_not_enough_proofs");
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
const bool multi_out[] = {false}; return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.pop_back(); tx.rct_signatures.p.bulletproofs.pop_back();
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
return true; return true;
}); });
} }
@ -293,11 +300,10 @@ bool gen_bp_tx_invalid_not_enough_proofs::generate(std::vector<test_event_entry>
bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>& events) const
{ {
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs"); DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs");
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const bool multi_out[] = {false}; const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.push_back(tx.rct_signatures.p.bulletproofs.back()); tx.rct_signatures.p.bulletproofs.push_back(tx.rct_signatures.p.bulletproofs.back());
@ -308,11 +314,10 @@ bool gen_bp_tx_invalid_too_many_proofs::generate(std::vector<test_event_entry>&
bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& events) const
{ {
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount"); DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount");
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {10000, (uint64_t)-1}; const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const bool multi_out[] = {false}; const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){ return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof);
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty()); CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.back() = rct::bulletproof_PROVE(1000, rct::skGen()); tx.rct_signatures.p.bulletproofs.back() = rct::bulletproof_PROVE(1000, rct::skGen());
@ -320,20 +325,15 @@ bool gen_bp_tx_invalid_wrong_amount::generate(std::vector<test_event_entry>& eve
}); });
} }
bool gen_bp_tx_invalid_switched::generate(std::vector<test_event_entry>& events) const bool gen_bp_tx_invalid_borromean_type::generate(std::vector<test_event_entry>& events) const
{ {
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_switched"); DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_borromean_type");
const int mixin = 6; const size_t mixin = 10;
const int out_idx[] = {1, -1}; const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const uint64_t amounts_paid[] = {1001, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, (uint64_t)-1}; const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof};
const bool multi_out[] = {true}; return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof); CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof);
CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == 2); tx.rct_signatures.type = rct::RCTTypeSimple;
rct::Bulletproof proof = tx.rct_signatures.p.bulletproofs[0];
tx.rct_signatures.p.bulletproofs[0] = tx.rct_signatures.p.bulletproofs[1];
tx.rct_signatures.p.bulletproofs[1] = proof;
return true; return true;
}); });
} }

View File

@ -81,8 +81,8 @@ struct gen_bp_tx_validation_base : public test_chain_unit_base
return true; return true;
} }
bool generate_with(std::vector<test_event_entry>& events, const int *out_idx, int mixin, bool generate_with(std::vector<test_event_entry>& events, size_t mixin,
size_t n_txes, const uint64_t *amounts_paid, bool valid, const bool *multi_out, size_t n_txes, const uint64_t *amounts_paid, bool valid, const rct::RangeProofType *range_proof_type,
const std::function<bool(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations, size_t)> &pre_tx, const std::function<bool(std::vector<cryptonote::tx_source_entry> &sources, std::vector<cryptonote::tx_destination_entry> &destinations, size_t)> &pre_tx,
const std::function<bool(cryptonote::transaction &tx, size_t)> &post_tx) const; const std::function<bool(cryptonote::transaction &tx, size_t)> &post_tx) const;
@ -95,7 +95,7 @@ private:
template<> template<>
struct get_test_options<gen_bp_tx_validation_base> { struct get_test_options<gen_bp_tx_validation_base> {
const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(8, 69), std::make_pair(0, 0)}; const std::pair<uint8_t, uint64_t> hard_forks[4] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(8, 73), std::make_pair(0, 0)};
const cryptonote::test_options test_options = { const cryptonote::test_options test_options = {
hard_forks hard_forks
}; };
@ -120,17 +120,29 @@ struct gen_bp_tx_valid_2 : public gen_bp_tx_validation_base
}; };
template<> struct get_test_options<gen_bp_tx_valid_2>: public get_test_options<gen_bp_tx_validation_base> {}; template<> struct get_test_options<gen_bp_tx_valid_2>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_valid_4_2_1 : public gen_bp_tx_validation_base struct gen_bp_tx_valid_3 : public gen_bp_tx_validation_base
{ {
bool generate(std::vector<test_event_entry>& events) const; bool generate(std::vector<test_event_entry>& events) const;
}; };
template<> struct get_test_options<gen_bp_tx_valid_4_2_1>: public get_test_options<gen_bp_tx_validation_base> {}; template<> struct get_test_options<gen_bp_tx_valid_3>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_valid_16_16 : public gen_bp_tx_validation_base struct gen_bp_tx_valid_16 : public gen_bp_tx_validation_base
{ {
bool generate(std::vector<test_event_entry>& events) const; bool generate(std::vector<test_event_entry>& events) const;
}; };
template<> struct get_test_options<gen_bp_tx_valid_16_16>: public get_test_options<gen_bp_tx_validation_base> {}; template<> struct get_test_options<gen_bp_tx_valid_16>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_4_2_1 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_4_2_1>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_16_16 : public gen_bp_tx_validation_base
{
bool generate(std::vector<test_event_entry>& events) const;
};
template<> struct get_test_options<gen_bp_tx_invalid_16_16>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base
{ {
@ -138,11 +150,11 @@ struct gen_bp_txs_valid_2_and_2 : public gen_bp_tx_validation_base
}; };
template<> struct get_test_options<gen_bp_txs_valid_2_and_2>: public get_test_options<gen_bp_tx_validation_base> {}; template<> struct get_test_options<gen_bp_txs_valid_2_and_2>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_txs_valid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base struct gen_bp_txs_invalid_2_and_8_2_and_16_16_1 : public gen_bp_tx_validation_base
{ {
bool generate(std::vector<test_event_entry>& events) const; bool generate(std::vector<test_event_entry>& events) const;
}; };
template<> struct get_test_options<gen_bp_txs_valid_2_and_8_2_and_16_16_1>: public get_test_options<gen_bp_tx_validation_base> {}; template<> struct get_test_options<gen_bp_txs_invalid_2_and_8_2_and_16_16_1>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base struct gen_bp_tx_invalid_not_enough_proofs : public gen_bp_tx_validation_base
{ {
@ -162,9 +174,8 @@ struct gen_bp_tx_invalid_wrong_amount : public gen_bp_tx_validation_base
}; };
template<> struct get_test_options<gen_bp_tx_invalid_wrong_amount>: public get_test_options<gen_bp_tx_validation_base> {}; template<> struct get_test_options<gen_bp_tx_invalid_wrong_amount>: public get_test_options<gen_bp_tx_validation_base> {};
struct gen_bp_tx_invalid_switched : public gen_bp_tx_validation_base struct gen_bp_tx_invalid_borromean_type : public gen_bp_tx_validation_base
{ {
bool generate(std::vector<test_event_entry>& events) const; bool generate(std::vector<test_event_entry>& events) const;
}; };
template<> struct get_test_options<gen_bp_tx_invalid_switched>: public get_test_options<gen_bp_tx_validation_base> {}; template<> struct get_test_options<gen_bp_tx_invalid_borromean_type>: public get_test_options<gen_bp_tx_validation_base> {};

View File

@ -69,13 +69,13 @@ void test_generator::get_block_chain(std::vector<block_info>& blockchain, const
std::reverse(blockchain.begin(), blockchain.end()); std::reverse(blockchain.begin(), blockchain.end());
} }
void test_generator::get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const void test_generator::get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const
{ {
std::vector<block_info> blockchain; std::vector<block_info> blockchain;
get_block_chain(blockchain, head, n); get_block_chain(blockchain, head, n);
BOOST_FOREACH(auto& bi, blockchain) BOOST_FOREACH(auto& bi, blockchain)
{ {
block_sizes.push_back(bi.block_size); block_weights.push_back(bi.block_weight);
} }
} }
@ -95,17 +95,17 @@ uint64_t test_generator::get_already_generated_coins(const cryptonote::block& bl
return get_already_generated_coins(blk_hash); return get_already_generated_coins(blk_hash);
} }
void test_generator::add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins, uint8_t hf_version) void test_generator::add_block(const cryptonote::block& blk, size_t txs_weight, std::vector<size_t>& block_weights, uint64_t already_generated_coins, uint8_t hf_version)
{ {
const size_t block_size = tsx_size + get_object_blobsize(blk.miner_tx); const size_t block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
uint64_t block_reward; uint64_t block_reward;
get_block_reward(misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, hf_version); get_block_reward(misc_utils::median(block_weights), block_weight, already_generated_coins, block_reward, hf_version);
m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_size); m_blocks_info[get_block_hash(blk)] = block_info(blk.prev_id, already_generated_coins + block_reward, block_weight);
} }
bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list) std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list)
{ {
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION; blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
blk.minor_version = CURRENT_BLOCK_MINOR_VERSION; blk.minor_version = CURRENT_BLOCK_MINOR_VERSION;
@ -121,52 +121,52 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
} }
uint64_t total_fee = 0; uint64_t total_fee = 0;
size_t txs_size = 0; size_t txs_weight = 0;
BOOST_FOREACH(auto& tx, tx_list) BOOST_FOREACH(auto& tx, tx_list)
{ {
uint64_t fee = 0; uint64_t fee = 0;
bool r = get_tx_fee(tx, fee); bool r = get_tx_fee(tx, fee);
CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block"); CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block");
total_fee += fee; total_fee += fee;
txs_size += get_object_blobsize(tx); txs_weight += get_transaction_weight(tx);
} }
blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx); blk.miner_tx = AUTO_VAL_INIT(blk.miner_tx);
size_t target_block_size = txs_size + get_object_blobsize(blk.miner_tx); size_t target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
while (true) while (true)
{ {
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, target_block_size, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10)) if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, target_block_weight, total_fee, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), 10))
return false; return false;
size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (target_block_size < actual_block_size) if (target_block_weight < actual_block_weight)
{ {
target_block_size = actual_block_size; target_block_weight = actual_block_weight;
} }
else if (actual_block_size < target_block_size) else if (actual_block_weight < target_block_weight)
{ {
size_t delta = target_block_size - actual_block_size; size_t delta = target_block_weight - actual_block_weight;
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0); blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (actual_block_size == target_block_size) if (actual_block_weight == target_block_weight)
{ {
break; break;
} }
else else
{ {
CHECK_AND_ASSERT_MES(target_block_size < actual_block_size, false, "Unexpected block size"); CHECK_AND_ASSERT_MES(target_block_weight < actual_block_weight, false, "Unexpected block size");
delta = actual_block_size - target_block_size; delta = actual_block_weight - target_block_weight;
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta); blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta);
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx); actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (actual_block_size == target_block_size) if (actual_block_weight == target_block_weight)
{ {
break; break;
} }
else else
{ {
CHECK_AND_ASSERT_MES(actual_block_size < target_block_size, false, "Unexpected block size"); CHECK_AND_ASSERT_MES(actual_block_weight < target_block_weight, false, "Unexpected block size");
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0); blk.miner_tx.extra.resize(blk.miner_tx.extra.size() + delta, 0);
target_block_size = txs_size + get_object_blobsize(blk.miner_tx); target_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
} }
} }
} }
@ -183,16 +183,16 @@ bool test_generator::construct_block(cryptonote::block& blk, uint64_t height, co
while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height)) while (!miner::find_nonce_for_given_block(blk, get_test_difficulty(), height))
blk.timestamp++; blk.timestamp++;
add_block(blk, txs_size, block_sizes, already_generated_coins); add_block(blk, txs_weight, block_weights, already_generated_coins);
return true; return true;
} }
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp) bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp)
{ {
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
std::list<cryptonote::transaction> tx_list; std::list<cryptonote::transaction> tx_list;
return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_sizes, tx_list); return construct_block(blk, 0, null_hash, miner_acc, timestamp, 0, block_weights, tx_list);
} }
bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev,
@ -204,10 +204,10 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::b
// Keep difficulty unchanged // Keep difficulty unchanged
uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN; uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
uint64_t already_generated_coins = get_already_generated_coins(prev_id); uint64_t already_generated_coins = get_already_generated_coins(prev_id);
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
get_last_n_block_sizes(block_sizes, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_block_weights(block_weights, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_sizes, tx_list); return construct_block(blk, height, prev_id, miner_acc, timestamp, already_generated_coins, block_weights, tx_list);
} }
bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc, bool test_generator::construct_block_manually(block& blk, const block& prev_block, const account_base& miner_acc,
@ -216,7 +216,7 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/, const crypto::hash& prev_id/* = crypto::hash()*/, const difficulty_type& diffic/* = 1*/,
const transaction& miner_tx/* = transaction()*/, const transaction& miner_tx/* = transaction()*/,
const std::vector<crypto::hash>& tx_hashes/* = std::vector<crypto::hash>()*/, const std::vector<crypto::hash>& tx_hashes/* = std::vector<crypto::hash>()*/,
size_t txs_sizes/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/) size_t txs_weight/* = 0*/, size_t max_outs/* = 0*/, uint8_t hf_version/* = 1*/)
{ {
blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION; blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION;
blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION; blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION;
@ -228,17 +228,17 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
size_t height = get_block_height(prev_block) + 1; size_t height = get_block_height(prev_block) + 1;
uint64_t already_generated_coins = get_already_generated_coins(prev_block); uint64_t already_generated_coins = get_already_generated_coins(prev_block);
std::vector<size_t> block_sizes; std::vector<size_t> block_weights;
get_last_n_block_sizes(block_sizes, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW); get_last_n_block_weights(block_weights, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (actual_params & bf_miner_tx) if (actual_params & bf_miner_tx)
{ {
blk.miner_tx = miner_tx; blk.miner_tx = miner_tx;
} }
else else
{ {
size_t current_block_size = txs_sizes + get_object_blobsize(blk.miner_tx); size_t current_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
// TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE // TODO: This will work, until size of constructed block is less then CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE
if (!construct_miner_tx(height, misc_utils::median(block_sizes), already_generated_coins, current_block_size, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version)) if (!construct_miner_tx(height, misc_utils::median(block_weights), already_generated_coins, current_block_weight, 0, miner_acc.get_keys().m_account_address, blk.miner_tx, blobdata(), max_outs, hf_version))
return false; return false;
} }
@ -247,16 +247,16 @@ bool test_generator::construct_block_manually(block& blk, const block& prev_bloc
difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty(); difficulty_type a_diffic = actual_params & bf_diffic ? diffic : get_test_difficulty();
fill_nonce(blk, a_diffic, height); fill_nonce(blk, a_diffic, height);
add_block(blk, txs_sizes, block_sizes, already_generated_coins, hf_version); add_block(blk, txs_weight, block_weights, already_generated_coins, hf_version);
return true; return true;
} }
bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block, bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
const cryptonote::account_base& miner_acc, const cryptonote::account_base& miner_acc,
const std::vector<crypto::hash>& tx_hashes, size_t txs_size) const std::vector<crypto::hash>& tx_hashes, size_t txs_weight)
{ {
return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_size); return construct_block_manually(blk, prev_block, miner_acc, bf_tx_hashes, 0, 0, 0, crypto::hash(), 0, transaction(), tx_hashes, txs_weight);
} }

View File

@ -159,20 +159,20 @@ public:
block_info() block_info()
: prev_id() : prev_id()
, already_generated_coins(0) , already_generated_coins(0)
, block_size(0) , block_weight(0)
{ {
} }
block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_size) block_info(crypto::hash a_prev_id, uint64_t an_already_generated_coins, size_t a_block_weight)
: prev_id(a_prev_id) : prev_id(a_prev_id)
, already_generated_coins(an_already_generated_coins) , already_generated_coins(an_already_generated_coins)
, block_size(a_block_size) , block_weight(a_block_weight)
{ {
} }
crypto::hash prev_id; crypto::hash prev_id;
uint64_t already_generated_coins; uint64_t already_generated_coins;
size_t block_size; size_t block_weight;
}; };
enum block_fields enum block_fields
@ -190,15 +190,15 @@ public:
}; };
void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const; void get_block_chain(std::vector<block_info>& blockchain, const crypto::hash& head, size_t n) const;
void get_last_n_block_sizes(std::vector<size_t>& block_sizes, const crypto::hash& head, size_t n) const; void get_last_n_block_weights(std::vector<size_t>& block_weights, const crypto::hash& head, size_t n) const;
uint64_t get_already_generated_coins(const crypto::hash& blk_id) const; uint64_t get_already_generated_coins(const crypto::hash& blk_id) const;
uint64_t get_already_generated_coins(const cryptonote::block& blk) const; uint64_t get_already_generated_coins(const cryptonote::block& blk) const;
void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_sizes, uint64_t already_generated_coins, void add_block(const cryptonote::block& blk, size_t tsx_size, std::vector<size_t>& block_weights, uint64_t already_generated_coins,
uint8_t hf_version = 1); uint8_t hf_version = 1);
bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id, bool construct_block(cryptonote::block& blk, uint64_t height, const crypto::hash& prev_id,
const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins, const cryptonote::account_base& miner_acc, uint64_t timestamp, uint64_t already_generated_coins,
std::vector<size_t>& block_sizes, const std::list<cryptonote::transaction>& tx_list); std::vector<size_t>& block_weights, const std::list<cryptonote::transaction>& tx_list);
bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp); bool construct_block(cryptonote::block& blk, const cryptonote::account_base& miner_acc, uint64_t timestamp);
bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc, bool construct_block(cryptonote::block& blk, const cryptonote::block& blk_prev, const cryptonote::account_base& miner_acc,
const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>()); const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>());

View File

@ -227,14 +227,16 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_bp_tx_valid_1); GENERATE_AND_PLAY(gen_bp_tx_valid_1);
GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1); GENERATE_AND_PLAY(gen_bp_tx_invalid_1_1);
GENERATE_AND_PLAY(gen_bp_tx_valid_2); GENERATE_AND_PLAY(gen_bp_tx_valid_2);
GENERATE_AND_PLAY(gen_bp_tx_valid_4_2_1); GENERATE_AND_PLAY(gen_bp_tx_valid_3);
GENERATE_AND_PLAY(gen_bp_tx_valid_16_16); GENERATE_AND_PLAY(gen_bp_tx_valid_16);
GENERATE_AND_PLAY(gen_bp_tx_invalid_4_2_1);
GENERATE_AND_PLAY(gen_bp_tx_invalid_16_16);
GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2); GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_2);
GENERATE_AND_PLAY(gen_bp_txs_valid_2_and_8_2_and_16_16_1); GENERATE_AND_PLAY(gen_bp_txs_invalid_2_and_8_2_and_16_16_1);
GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_not_enough_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs); GENERATE_AND_PLAY(gen_bp_tx_invalid_too_many_proofs);
GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount); GENERATE_AND_PLAY(gen_bp_tx_invalid_wrong_amount);
GENERATE_AND_PLAY(gen_bp_tx_invalid_switched); GENERATE_AND_PLAY(gen_bp_tx_invalid_borromean_type);
el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error); el::Level level = (failed_tests.empty() ? el::Level::Info : el::Level::Error);
MLOG(level, "\nREPORT:"); MLOG(level, "\nREPORT:");

View File

@ -40,7 +40,7 @@
#include "multi_tx_test_base.h" #include "multi_tx_test_base.h"
template<size_t a_ring_size, size_t a_outputs, bool a_rct, bool a_bulletproof> template<size_t a_ring_size, size_t a_outputs, bool a_rct, rct::RangeProofType range_proof_type = rct::RangeProofBorromean>
class test_check_tx_signature : private multi_tx_test_base<a_ring_size> class test_check_tx_signature : private multi_tx_test_base<a_ring_size>
{ {
static_assert(0 < a_ring_size, "ring_size must be greater than 0"); static_assert(0 < a_ring_size, "ring_size must be greater than 0");
@ -50,7 +50,6 @@ public:
static const size_t ring_size = a_ring_size; static const size_t ring_size = a_ring_size;
static const size_t outputs = a_outputs; static const size_t outputs = a_outputs;
static const bool rct = a_rct; static const bool rct = a_rct;
static const bool bulletproof = a_bulletproof;
typedef multi_tx_test_base<a_ring_size> base_class; typedef multi_tx_test_base<a_ring_size> base_class;
@ -72,7 +71,7 @@ public:
std::vector<crypto::secret_key> additional_tx_keys; std::vector<crypto::secret_key> additional_tx_keys;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses; std::unordered_map<crypto::public_key, cryptonote::subaddress_index> subaddresses;
subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0}; subaddresses[this->m_miners[this->real_source_idx].get_keys().m_account_address.m_spend_public_key] = {0,0};
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, bulletproof ? rct::RangeProofMultiOutputBulletproof : rct::RangeProofBorromean)) if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_tx, 0, tx_key, additional_tx_keys, rct, range_proof_type))
return false; return false;
get_transaction_prefix_hash(m_tx, m_tx_prefix_hash); get_transaction_prefix_hash(m_tx, m_tx_prefix_hash);
@ -136,7 +135,7 @@ public:
m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0)); m_txes.resize(a_num_txes + (extra_outs > 0 ? 1 : 0));
for (size_t n = 0; n < a_num_txes; ++n) for (size_t n = 0; n < a_num_txes; ++n)
{ {
if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes[n], 0, tx_key, additional_tx_keys, true, rct::RangeProofMultiOutputBulletproof)) if (!construct_tx_and_get_tx_key(this->m_miners[this->real_source_idx].get_keys(), subaddresses, this->m_sources, destinations, cryptonote::account_public_address{}, std::vector<uint8_t>(), m_txes[n], 0, tx_key, additional_tx_keys, true, rct::RangeProofPaddedBulletproof))
return false; return false;
} }

View File

@ -132,21 +132,25 @@ int main(int argc, char** argv)
TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true);
TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true); TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 1, 2, false, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, false, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, false, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 10, 2, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, false, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 100, 2, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, false, false); TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 10, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, false); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, true); TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64); TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64);

View File

@ -40,14 +40,14 @@ namespace
class block_reward_and_already_generated_coins : public ::testing::Test class block_reward_and_already_generated_coins : public ::testing::Test
{ {
protected: protected:
static const size_t current_block_size = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2; static const size_t current_block_weight = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 / 2;
bool m_block_not_too_big; bool m_block_not_too_big;
uint64_t m_block_reward; uint64_t m_block_reward;
}; };
#define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \ #define TEST_ALREADY_GENERATED_COINS(already_generated_coins, expected_reward) \
m_block_not_too_big = get_block_reward(0, current_block_size, already_generated_coins, m_block_reward,1); \ m_block_not_too_big = get_block_reward(0, current_block_weight, already_generated_coins, m_block_reward,1); \
ASSERT_TRUE(m_block_not_too_big); \ ASSERT_TRUE(m_block_not_too_big); \
ASSERT_EQ(m_block_reward, expected_reward); ASSERT_EQ(m_block_reward, expected_reward);
@ -74,7 +74,7 @@ namespace
} }
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
class block_reward_and_current_block_size : public ::testing::Test class block_reward_and_current_block_weight : public ::testing::Test
{ {
protected: protected:
virtual void SetUp() virtual void SetUp()
@ -84,9 +84,9 @@ namespace
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward);
} }
void do_test(size_t median_block_size, size_t current_block_size) void do_test(size_t median_block_weight, size_t current_block_weight)
{ {
m_block_not_too_big = get_block_reward(median_block_size, current_block_size, already_generated_coins, m_block_reward, 1); m_block_not_too_big = get_block_reward(median_block_weight, current_block_weight, already_generated_coins, m_block_reward, 1);
} }
static const uint64_t already_generated_coins = 0; static const uint64_t already_generated_coins = 0;
@ -96,28 +96,28 @@ namespace
uint64_t m_standard_block_reward; uint64_t m_standard_block_reward;
}; };
TEST_F(block_reward_and_current_block_size, handles_block_size_less_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_less_relevance_level)
{ {
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_eq_relevance_level)
{ {
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_gt_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_gt_relevance_level)
{ {
do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1); do_test(0, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_less_2_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_less_2_relevance_level)
{ {
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
@ -125,21 +125,21 @@ namespace
ASSERT_LT(0, m_block_reward); ASSERT_LT(0, m_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_eq_2_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_eq_2_relevance_level)
{ {
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(0, m_block_reward); ASSERT_EQ(0, m_block_reward);
} }
TEST_F(block_reward_and_current_block_size, handles_block_size_gt_2_relevance_level) TEST_F(block_reward_and_current_block_weight, handles_block_weight_gt_2_relevance_level)
{ {
do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1); do_test(0, 2 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1 + 1);
ASSERT_FALSE(m_block_not_too_big); ASSERT_FALSE(m_block_not_too_big);
} }
#ifdef __x86_64__ // For 64-bit systems only, because block size is limited to size_t. #ifdef __x86_64__ // For 64-bit systems only, because block size is limited to size_t.
TEST_F(block_reward_and_current_block_size, fails_on_huge_median_size) TEST_F(block_reward_and_current_block_weight, fails_on_huge_median_size)
{ {
#if !defined(NDEBUG) #if !defined(NDEBUG)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2); size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
@ -147,7 +147,7 @@ namespace
#endif #endif
} }
TEST_F(block_reward_and_current_block_size, fails_on_huge_block_size) TEST_F(block_reward_and_current_block_weight, fails_on_huge_block_weight)
{ {
#if !defined(NDEBUG) #if !defined(NDEBUG)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2); size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
@ -157,94 +157,94 @@ namespace
#endif // __x86_64__ #endif // __x86_64__
//-------------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------
class block_reward_and_last_block_sizes : public ::testing::Test class block_reward_and_last_block_weights : public ::testing::Test
{ {
protected: protected:
virtual void SetUp() virtual void SetUp()
{ {
m_last_block_sizes.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(11 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(11 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1); m_last_block_weights.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_sizes_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1; m_last_block_weights_median = 7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1;
m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), 0, already_generated_coins, m_standard_block_reward, 1); m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), 0, already_generated_coins, m_standard_block_reward, 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward); ASSERT_LT(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, m_standard_block_reward);
} }
void do_test(size_t current_block_size) void do_test(size_t current_block_weight)
{ {
m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_sizes), current_block_size, already_generated_coins, m_block_reward, 1); m_block_not_too_big = get_block_reward(epee::misc_utils::median(m_last_block_weights), current_block_weight, already_generated_coins, m_block_reward, 1);
} }
static const uint64_t already_generated_coins = 0; static const uint64_t already_generated_coins = 0;
std::vector<size_t> m_last_block_sizes; std::vector<size_t> m_last_block_weights;
uint64_t m_last_block_sizes_median; uint64_t m_last_block_weights_median;
bool m_block_not_too_big; bool m_block_not_too_big;
uint64_t m_block_reward; uint64_t m_block_reward;
uint64_t m_standard_block_reward; uint64_t m_standard_block_reward;
}; };
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_median) TEST_F(block_reward_and_last_block_weights, handles_block_weight_less_median)
{ {
do_test(m_last_block_sizes_median - 1); do_test(m_last_block_weights_median - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_median) TEST_F(block_reward_and_last_block_weights, handles_block_weight_eq_median)
{ {
do_test(m_last_block_sizes_median); do_test(m_last_block_weights_median);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward); ASSERT_EQ(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_median) TEST_F(block_reward_and_last_block_weights, handles_block_weight_gt_median)
{ {
do_test(m_last_block_sizes_median + 1); do_test(m_last_block_weights_median + 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_less_2_medians) TEST_F(block_reward_and_last_block_weights, handles_block_weight_less_2_medians)
{ {
do_test(2 * m_last_block_sizes_median - 1); do_test(2 * m_last_block_weights_median - 1);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_LT(m_block_reward, m_standard_block_reward); ASSERT_LT(m_block_reward, m_standard_block_reward);
ASSERT_LT(0, m_block_reward); ASSERT_LT(0, m_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_eq_2_medians) TEST_F(block_reward_and_last_block_weights, handles_block_weight_eq_2_medians)
{ {
do_test(2 * m_last_block_sizes_median); do_test(2 * m_last_block_weights_median);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(0, m_block_reward); ASSERT_EQ(0, m_block_reward);
} }
TEST_F(block_reward_and_last_block_sizes, handles_block_size_gt_2_medians) TEST_F(block_reward_and_last_block_weights, handles_block_weight_gt_2_medians)
{ {
do_test(2 * m_last_block_sizes_median + 1); do_test(2 * m_last_block_weights_median + 1);
ASSERT_FALSE(m_block_not_too_big); ASSERT_FALSE(m_block_not_too_big);
} }
TEST_F(block_reward_and_last_block_sizes, calculates_correctly) TEST_F(block_reward_and_last_block_weights, calculates_correctly)
{ {
ASSERT_EQ(0, m_last_block_sizes_median % 8); ASSERT_EQ(0, m_last_block_weights_median % 8);
do_test(m_last_block_sizes_median * 9 / 8); do_test(m_last_block_weights_median * 9 / 8);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64); ASSERT_EQ(m_block_reward, m_standard_block_reward * 63 / 64);
// 3/2 = 12/8 // 3/2 = 12/8
do_test(m_last_block_sizes_median * 3 / 2); do_test(m_last_block_weights_median * 3 / 2);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4); ASSERT_EQ(m_block_reward, m_standard_block_reward * 3 / 4);
do_test(m_last_block_sizes_median * 15 / 8); do_test(m_last_block_weights_median * 15 / 8);
ASSERT_TRUE(m_block_not_too_big); ASSERT_TRUE(m_block_not_too_big);
ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64); ASSERT_EQ(m_block_reward, m_standard_block_reward * 15 / 64);
} }

View File

@ -319,7 +319,7 @@ TYPED_TEST(BlockchainDBTest, RetrieveBlockData)
ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0])); ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]));
ASSERT_EQ(t_sizes[0], this->m_db->get_block_size(0)); ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0));
ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0));
ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0)); ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0));
ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0)); ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0));

View File

@ -129,7 +129,7 @@ TEST(bulletproofs, multi_splitting)
} }
rct::ctkeyV outSk; rct::ctkeyV outSk;
rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct::RangeProofMultiOutputBulletproof, hw::get_device("default")); rct::rctSig s = rct::genRctSimple(rct::zero(), sc, destinations, inamounts, outamounts, available, mixRing, amount_keys, NULL, NULL, index, outSk, rct::RangeProofPaddedBulletproof, hw::get_device("default"));
ASSERT_TRUE(rct::verRctSimple(s)); ASSERT_TRUE(rct::verRctSimple(s));
for (size_t i = 0; i < n_outputs; ++i) for (size_t i = 0; i < n_outputs; ++i)
{ {

View File

@ -58,46 +58,46 @@ namespace
TEST_F(fee, 10xmr) TEST_F(fee, 10xmr)
{ {
// CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(2000000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(2000000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(2000000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, 1, 3), 2000000000); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, 1, 3), 2000000000);
// higher is inverse proportional // higher is inverse proportional
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(2000000000 / 2));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(2000000000 / 10));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(2000000000 / 1000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(2000000000 / 20000));
} }
TEST_F(fee, 1xmr) TEST_F(fee, 1xmr)
{ {
// CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(200000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(200000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(200000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, 1, 3), 200000000); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, 1, 3), 200000000);
// higher is inverse proportional // higher is inverse proportional
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(200000000 / 2));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(200000000 / 10));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(200000000 / 1000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(200000000 / 20000));
} }
TEST_F(fee, dot3xmr) TEST_F(fee, dot3xmr)
{ {
// CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped // CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 and lower are clamped
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, 3), clamp_fee(60000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 2, 3), clamp_fee(60000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 / 100, 3), clamp_fee(60000000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, 1, 3), 60000000); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, 1, 3), 60000000);
// higher is inverse proportional // higher is inverse proportional
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, 3), clamp_fee(60000000 / 2));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, 3), clamp_fee(60000000 / 10));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 1000, 3), clamp_fee(60000000 / 1000));
ASSERT_EQ(Blockchain::get_dynamic_per_kb_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000)); ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 20000ull, 3), clamp_fee(60000000 / 20000));
} }
static bool is_more_or_less(double x, double y) static bool is_more_or_less(double x, double y)
@ -116,7 +116,7 @@ namespace
600000000000ull, // .6 monero, minimum reward per block at 2min 600000000000ull, // .6 monero, minimum reward per block at 2min
300000000000ull, // .3 monero, minimum reward per block at 1min 300000000000ull, // .3 monero, minimum reward per block at 1min
}; };
static const uint64_t median_block_sizes[] = { static const uint64_t median_block_weights[] = {
CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2,
CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 2,
CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10, CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10,
@ -127,9 +127,9 @@ namespace
for (uint64_t block_reward: block_rewards) for (uint64_t block_reward: block_rewards)
{ {
for (uint64_t median_block_size: median_block_sizes) for (uint64_t median_block_weight: median_block_weights)
{ {
ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_per_kb_fee(block_reward, median_block_size, 3) * (median_block_size / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024)); ASSERT_TRUE(is_more_or_less(Blockchain::get_dynamic_base_fee(block_reward, median_block_weight, 3) * (median_block_weight / 1024.) * MAX_MULTIPLIER / (double)block_reward, 1.992 * 1000 / 1024));
} }
} }
} }

View File

@ -72,7 +72,7 @@ public:
virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; } virtual uint64_t get_block_timestamp(const uint64_t& height) const { return 0; }
virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; } virtual std::vector<uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights) const { return {}; }
virtual uint64_t get_top_block_timestamp() const { return 0; } virtual uint64_t get_top_block_timestamp() const { return 0; }
virtual size_t get_block_size(const uint64_t& height) const { return 128; } virtual size_t get_block_weight(const uint64_t& height) const { return 128; }
virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; } virtual difficulty_type get_block_cumulative_difficulty(const uint64_t& height) const { return 10; }
virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; } virtual difficulty_type get_block_difficulty(const uint64_t& height) const { return 0; }
virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; } virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const { return 10000000000; }
@ -130,7 +130,7 @@ public:
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; } virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)>, bool include_blob = false, bool include_unrelayed_txes = false) const { return false; }
virtual void add_block( const block& blk virtual void add_block( const block& blk
, const size_t& block_size , size_t block_weight
, const difficulty_type& cumulative_difficulty , const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated , const uint64_t& coins_generated
, uint64_t num_rct_outs , uint64_t num_rct_outs