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

release-v0.5.1
moneromooo-monero 6 years ago
parent 869b3bf824
commit 5ffb2ff9b7
No known key found for this signature in database
GPG Key ID: 686F07454D6CEFC3

@ -224,7 +224,7 @@ struct Dbt_safe : public Dbt
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__);
check_open();
@ -255,7 +255,7 @@ void BlockchainBDB::add_block(const block& blk, const size_t& block_size, const
if (res)
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))
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);
}
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__);
check_open();
@ -1861,7 +1861,7 @@ void BlockchainBDB::block_txn_abort()
// 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__);
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;
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;
TIME_MEASURE_START(time1);

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

@ -196,7 +196,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const transacti
}
uint64_t BlockchainDB::add_block( const block& blk
, const size_t& block_size
, size_t block_weight
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, 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
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_add_block1 += time1;

@ -137,7 +137,7 @@ struct txpool_tx_meta_t
{
crypto::hash max_used_block_id;
crypto::hash last_failed_id;
uint64_t blob_size;
uint64_t weight;
uint64_t fee;
uint64_t max_used_block_height;
uint64_t last_failed_height;
@ -358,13 +358,13 @@ private:
* subclass of DB_EXCEPTION
*
* @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 coins_generated the number of coins generated total after this block
* @param blk_hash the hash of the block
*/
virtual void add_block( const block& blk
, const size_t& block_size
, size_t block_weight
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, uint64_t num_rct_outs
@ -376,7 +376,7 @@ private:
*
* 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
* 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
* subclass of DB_EXCEPTION
@ -789,7 +789,7 @@ public:
* subclass of DB_EXCEPTION
*
* @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 coins_generated the number of coins generated total after this block
* @param txs the transactions in the block
@ -797,7 +797,7 @@ public:
* @return the height of the chain post-addition
*/
virtual uint64_t add_block( const block& blk
, const size_t& block_size
, size_t block_weight
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, const std::vector<transaction>& txs
@ -930,18 +930,18 @@ public:
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.
*
* If the block does not exist, the subclass should throw BLOCK_DNE
*
* @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

@ -256,7 +256,7 @@ typedef struct mdb_block_info_old
uint64_t bi_height;
uint64_t bi_timestamp;
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;
crypto::hash bi_hash;
} mdb_block_info_old;
@ -266,7 +266,7 @@ typedef struct mdb_block_info
uint64_t bi_height;
uint64_t bi_timestamp;
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;
crypto::hash bi_hash;
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);
for (uint64_t block_num = block_start; block_num <= block_stop; ++block_num)
{
uint32_t block_size = get_block_size(block_num);
total_block_size += block_size;
// we have access to block weight, which will be greater or equal to 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
// some blocks were to be skipped for being outliers.
++num_blocks_used;
@ -674,7 +677,7 @@ estim:
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)
{
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_timestamp = blk.timestamp;
bi.bi_coins = coins_generated;
bi.bi_size = block_size;
bi.bi_weight = block_weight;
bi.bi_diff = cumulative_difficulty;
bi.bi_hash = blk_hash;
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)
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++;
}
@ -2011,7 +2016,7 @@ uint64_t BlockchainLMDB::get_top_block_timestamp() const
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__);
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"));
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();
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)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@ -3109,7 +3114,7 @@ uint64_t BlockchainLMDB::add_block(const block& blk, const size_t& block_size, c
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)
{
@ -3698,9 +3703,9 @@ void BlockchainLMDB::migrate_0_1()
if (result)
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_sizes: ", result).c_str()));
if (v.mv_size == sizeof(uint32_t))
bi.bi_size = *(uint32_t *)v.mv_data;
bi.bi_weight = *(uint32_t *)v.mv_data;
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);
if (result)
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_timestamp = bi_old->bi_timestamp;
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_hash = bi_old->bi_hash;
if (bi_old->bi_height >= distribution.size())

@ -205,7 +205,7 @@ public:
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;
@ -273,7 +273,7 @@ public:
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
, const size_t& block_size
, size_t block_weight
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, 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;
virtual void add_block( const block& blk
, const size_t& block_size
, size_t block_weight
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, uint64_t num_rct_outs

@ -474,17 +474,17 @@ int import_from_file(cryptonote::core& core, const std::string& import_file_path
txs.push_back(tx);
}
size_t block_size;
size_t block_weight;
difficulty_type cumulative_difficulty;
uint64_t coins_generated;
block_size = bp.block_size;
block_weight = bp.block_weight;
cumulative_difficulty = bp.cumulative_difficulty;
coins_generated = bp.coins_generated;
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)
{

@ -236,11 +236,11 @@ void BootstrapFile::write_block(block& block)
bool include_extra_block_data = true;
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);
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.coins_generated = coins_generated;
}

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

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

@ -86,10 +86,10 @@ namespace cryptonote {
/************************************************************************/
/* 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_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_integrated_address_checksum(const public_integrated_address_outer_blob& bl);

@ -142,24 +142,27 @@ namespace cryptonote
const bool bulletproof = rct::is_rct_bulletproof(rv.type);
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));
return false;
}
size_t idx = 0;
for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n)
if (rv.p.bulletproofs[0].L.size() < 6)
{
//rv.p.bulletproofs[n].V.resize(1);
//rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask;
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;
LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs L size in tx " << get_transaction_hash(tx));
return false;
}
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);
}
//---------------------------------------------------------------
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)
{
if (tx.version > 1)

@ -117,6 +117,8 @@ namespace cryptonote
bool check_inputs_types_supported(const transaction& tx);
bool check_outs_valid(const transaction& tx);
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_outs_overflow(const transaction& tx);

@ -65,9 +65,11 @@
#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_BYTE ((uint64_t)300000)
#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_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
@ -133,13 +135,15 @@
#define HF_VERSION_DYNAMIC_FEE 4
#define HF_VERSION_MIN_MIXIN_4 6
#define HF_VERSION_MIN_MIXIN_6 7
#define HF_VERSION_MIN_MIXIN_10 8
#define HF_VERSION_ENFORCE_RCT 6
#define HF_VERSION_PER_BYTE_FEE 8
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
#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

@ -155,7 +155,7 @@ static const struct {
//------------------------------------------------------------------
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_difficulty_for_next_block_top_hash(crypto::null_hash),
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());
}
update_next_cumulative_size_limit();
update_next_cumulative_weight_limit();
return true;
}
//------------------------------------------------------------------
@ -631,7 +631,7 @@ block Blockchain::pop_block_from_blockchain()
m_blocks_txs_check.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());
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>();
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;
}
//------------------------------------------------------------------
@ -1113,7 +1113,7 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height)
}
//------------------------------------------------------------------
// 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__);
//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;
get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (!get_block_reward(epee::misc_utils::median(last_blocks_sizes), cumulative_block_size, already_generated_coins, base_reward, version))
std::vector<size_t> last_blocks_weights;
get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
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;
}
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;
}
//------------------------------------------------------------------
// get the block sizes 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
// get the block weights of the last <count> blocks, and return by reference <sz>.
void Blockchain::get_last_n_blocks_weights(std::vector<size_t>& weights, size_t count) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
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;
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);
sz.reserve(sz.size() + h - start_offset);
weights.reserve(weights.size() + h - start_offset);
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();
}
//------------------------------------------------------------------
uint64_t Blockchain::get_current_cumulative_blocksize_limit() const
uint64_t Blockchain::get_current_cumulative_block_weight_limit() const
{
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__);
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,
@ -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)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_size;
size_t median_weight;
uint64_t already_generated_coins;
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();
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);
CRITICAL_REGION_END();
size_t txs_size;
size_t txs_weight;
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;
}
pool_cookie = m_tx_pool.cookie();
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
size_t real_txs_size = 0;
size_t real_txs_weight = 0;
uint64_t real_fee = 0;
CRITICAL_REGION_BEGIN(m_tx_pool.m_transactions_lock);
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;
}
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;
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)
{
@ -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)
{
@ -1311,70 +1311,70 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
}
CRITICAL_REGION_END();
MDEBUG("Creating block template: height " << height <<
", median size " << median_size <<
", median weight " << median_weight <<
", already generated coins " << already_generated_coins <<
", transaction size " << txs_size <<
", transaction weight " << txs_weight <<
", fee " << fee);
#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
block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size
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 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();
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");
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)
MDEBUG("Creating block template: miner tx size " << get_object_blobsize(b.miner_tx) <<
", cumulative size " << cumulative_size);
MDEBUG("Creating block template: miner tx weight " << get_transaction_weight(b.miner_tx) <<
", cumulative weight " << cumulative_weight);
#endif
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");
size_t coinbase_blob_size = get_object_blobsize(b.miner_tx);
if (coinbase_blob_size > cumulative_size - txs_size)
size_t coinbase_weight = get_transaction_weight(b.miner_tx);
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)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << cumulative_size << " is greater than before");
MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative weight " << cumulative_weight << " is greater than before");
#endif
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)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << txs_size + coinbase_blob_size <<
MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative weight " << txs_weight + coinbase_weight <<
" is less than before, adding " << delta << " zero bytes");
#endif
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.
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);
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);
cumulative_size += delta - 1;
cumulative_weight += delta - 1;
continue;
}
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)
MDEBUG("Creating block template: miner tx size " << coinbase_blob_size <<
", cumulative size " << cumulative_size << " is now good");
MDEBUG("Creating block template: miner tx weight " << coinbase_weight <<
", cumulative weight " << cumulative_weight << " is now good");
#endif
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)
{
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)
return false;
@ -2597,12 +2597,27 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
// from v8, allow bulletproofs
if (hf_version < 8) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
{
MERROR("Bulletproofs are not allowed before v8");
tvc.m_invalid_output = true;
return false;
if (tx.version >= 2) {
const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type);
if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty())
{
MERROR("Bulletproofs are not allowed before v8");
tvc.m_invalid_output = true;
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;
}
}
}
@ -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 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)
{
// 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 (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;
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 fee_per_kb_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE;
const uint64_t min_block_weight = get_min_block_weight(version);
if (median_block_weight < min_block_weight)
median_block_weight = min_block_weight;
uint64_t hi, lo;
if (version >= HF_VERSION_PER_BYTE_FEE)
{
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;
}
if (median_block_size < min_block_size)
median_block_size = min_block_size;
const uint64_t fee_base = version >= 5 ? DYNAMIC_FEE_PER_KB_BASE_FEE_V5 : DYNAMIC_FEE_PER_KB_BASE_FEE;
uint64_t unscaled_fee_per_kb = (fee_per_kb_base * min_block_size / median_block_size);
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 <= std::numeric_limits<uint32_t>::max(), "DYNAMIC_FEE_PER_KB_BASE_BLOCK_REWARD is too large");
@ -3133,29 +3166,48 @@ 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();
uint64_t fee_per_kb;
if (version < HF_VERSION_DYNAMIC_FEE)
{
fee_per_kb = FEE_PER_KB;
}
else
uint64_t median = 0;
uint64_t already_generated_coins = 0;
uint64_t base_reward = 0;
if (version >= HF_VERSION_DYNAMIC_FEE)
{
uint64_t median = m_current_block_cumul_sz_limit / 2;
uint64_t already_generated_coins = m_db->height() ? m_db->get_block_already_generated_coins(m_db->height() - 1) : 0;
uint64_t base_reward;
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;
fee_per_kb = get_dynamic_per_kb_fee(base_reward, median, version);
}
MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee");
uint64_t needed_fee = blob_size / 1024;
needed_fee += (blob_size % 1024) ? 1 : 0;
needed_fee *= fee_per_kb;
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;
if (version < HF_VERSION_DYNAMIC_FEE)
{
fee_per_kb = FEE_PER_KB;
}
else
{
fee_per_kb = get_dynamic_base_fee(base_reward, median, version);
}
MDEBUG("Using " << print_money(fee_per_kb) << "/kB fee");
needed_fee = tx_weight / 1024;
needed_fee += (tx_weight % 1024) ? 1 : 0;
needed_fee *= fee_per_kb;
}
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();
@ -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)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
const uint64_t min_block_size = get_min_block_size(version);
std::vector<size_t> sz;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
sz.reserve(grace_blocks);
const uint64_t min_block_weight = get_min_block_weight(version);
std::vector<size_t> weights;
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks);
weights.reserve(grace_blocks);
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);
if(median <= min_block_size)
median = min_block_size;
uint64_t median = epee::misc_utils::median(weights);
if(median <= min_block_weight)
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 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;
}
uint64_t fee = get_dynamic_per_kb_fee(base_reward, median, version);
MDEBUG("Estimating " << grace_blocks << "-block fee at " << print_money(fee) << "/kB");
uint64_t fee = get_dynamic_base_fee(base_reward, median, version);
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;
}
@ -3372,11 +3425,11 @@ bool Blockchain::flush_txes_from_pool(const std::vector<crypto::hash> &txids)
for (const auto &txid: txids)
{
cryptonote::transaction tx;
size_t blob_size;
size_t tx_weight;
uint64_t fee;
bool relayed, do_not_relay, double_spend_seen;
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");
res = false;
@ -3536,8 +3589,8 @@ leave:
goto leave;
}
size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx);
size_t cumulative_block_size = coinbase_blob_size;
size_t coinbase_weight = get_transaction_weight(bl.miner_tx);
size_t cumulative_block_weight = coinbase_weight;
std::vector<transaction> txs;
key_images_container keys;
@ -3559,7 +3612,7 @@ leave:
for (const crypto::hash& tx_id : bl.tx_hashes)
{
transaction tx;
size_t blob_size = 0;
size_t tx_weight = 0;
uint64_t fee = 0;
bool relayed = false, do_not_relay = false, double_spend_seen = false;
TIME_MEASURE_START(aa);
@ -3578,7 +3631,7 @@ leave:
TIME_MEASURE_START(bb);
// 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);
bvc.m_verifivation_failed = true;
@ -3649,7 +3702,7 @@ leave:
TIME_MEASURE_FINISH(cc);
t_checktx += cc;
fee_summary += fee;
cumulative_block_size += blob_size;
cumulative_block_weight += tx_weight;
}
m_blocks_txs_check.clear();
@ -3657,7 +3710,7 @@ leave:
TIME_MEASURE_START(vmt);
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;
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");
bvc.m_verifivation_failed = true;
@ -3666,11 +3719,11 @@ leave:
}
TIME_MEASURE_FINISH(vmt);
size_t block_size;
size_t block_weight;
difficulty_type cumulative_difficulty;
// 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;
// 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
@ -3691,7 +3744,7 @@ leave:
{
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)
{
@ -3716,14 +3769,14 @@ leave:
TIME_MEASURE_FINISH(addblock);
// do this after updating the hard fork state since the size limit may change due to fork
update_next_cumulative_size_limit();
// do this after updating the hard fork state since the weight limit may change due to fork
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)
{
MINFO("Height: " << new_height << " blob: " << coinbase_blob_size << " cumm: "
<< cumulative_block_size << " p/t: " << block_processing_time << " ("
MINFO("Height: " << new_height << " coinbase weight: " << coinbase_weight << " cumm: "
<< cumulative_block_weight << " p/t: " << block_processing_time << " ("
<< target_calculating_time << "/" << longhash_calculating_time << "/"
<< t1 << "/" << t2 << "/" << t3 << "/" << t_exists << "/" << t_pool
<< "/" << t_checktx << "/" << t_dblspnd << "/" << vmt << "/" << addblock << ")ms");
@ -3740,20 +3793,20 @@ leave:
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__);
std::vector<size_t> sz;
get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
std::vector<size_t> weights;
get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
uint64_t median = epee::misc_utils::median(sz);
m_current_block_cumul_sz_median = median;
uint64_t median = epee::misc_utils::median(weights);
m_current_block_cumul_weight_median = median;
if(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;
}
//------------------------------------------------------------------
@ -4650,14 +4703,14 @@ void Blockchain::load_compiled_in_block_hashes()
std::vector<transaction> txs;
m_tx_pool.get_transactions(txs);
size_t blob_size;
size_t tx_weight;
uint64_t fee;
bool relayed, do_not_relay, double_spend_seen;
transaction pool_tx;
for(const transaction &tx : txs)
{
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);
}
}
}

@ -95,7 +95,7 @@ namespace cryptonote
{
block bl; //!< the block
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
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);
/**
* @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 current block reward. It is expressed by kB.
* The dynamic fee may be quantized, to mask out the last decimal places
*
* @return the fee quantized mask
*/
static uint64_t get_fee_quantization_mask();
/**
* @brief get dynamic per kB or byte fee for a given block weight
*
* 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.
*
* @param block_reward the current block reward
* @param median_block_size the median blob's size in the past window
* @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 per kB fee
* @return the 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_dynamic_base_fee(uint64_t block_reward, size_t median_block_weight, uint8_t version);
/**
* @brief get dynamic per kB fee estimate for the next few blocks
* @brief get dynamic per kB or byte fee estimate for the next few blocks
*
* The dynamic fee is based on the block size in a past window, and
* the current block reward. It is expressed by kB. This function
* calculates an estimate for a dynamic fee which will be valid for
* the next grace_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
*
* @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
*
* This function validates the fee is enough for the transaction.
* This is based on the size of the transaction blob, and, after a
* height threshold, on the average size of transaction in a past window
* This is based on the weight of the transaction, and, after a
* 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
*
* @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
@ -635,18 +646,18 @@ namespace cryptonote
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
*/
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
*/
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
@ -1001,8 +1012,8 @@ namespace cryptonote
// main chain
transactions_container m_transactions;
size_t m_current_block_cumul_sz_limit;
size_t m_current_block_cumul_sz_median;
size_t m_current_block_cumul_weight_limit;
size_t m_current_block_cumul_weight_median;
// metadata containers
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.
*
* @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 base_reward return-by-reference the new block's generated coins
* @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
*/
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
@ -1251,14 +1262,14 @@ namespace cryptonote
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 count the number of blocks to get sizes for
* @param sz return-by-reference the list of weights
* @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
@ -1373,11 +1384,11 @@ namespace cryptonote
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
*/
bool update_next_cumulative_size_limit();
bool update_next_cumulative_weight_limit();
void return_tx_to_pool(std::vector<transaction> &txs);
/**

@ -162,10 +162,10 @@ namespace cryptonote
, "Relay blocks as normal blocks"
, false
};
static const command_line::arg_descriptor<size_t> arg_max_txpool_size = {
"max-txpool-size"
, "Set maximum txpool size in bytes."
, DEFAULT_TXPOOL_MAX_SIZE
static const command_line::arg_descriptor<size_t> arg_max_txpool_weight = {
"max-txpool-weight"
, "Set maximum txpool weight in bytes."
, 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_offline);
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);
BlockchainDB::init_options(desc);
@ -402,7 +402,7 @@ namespace cryptonote
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);
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);
if (m_nettype == FAKECHAIN)
@ -551,7 +551,7 @@ namespace cryptonote
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_mempool.init(max_txpool_size);
r = m_mempool.init(max_txpool_weight);
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
@ -692,43 +692,6 @@ namespace cryptonote
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;
}
//-----------------------------------------------------------------------------------------------
@ -747,20 +710,11 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
static bool is_canonical_bulletproof_layout(const std::vector<rct::Bulletproof> &proofs)
{
size_t n_amounts = rct::n_bulletproof_amounts(proofs), amounts_proved = 0;
size_t n = 0;
while (amounts_proved < n_amounts)
{
if (n >= proofs.size())
return false;
size_t batch_size = 1;
while (batch_size * 2 + amounts_proved <= n_amounts && batch_size * 2 <= BULLETPROOF_MAX_OUTPUTS)
batch_size *= 2;
if (rct::n_bulletproof_amounts(proofs[n]) != batch_size)
return false;
amounts_proved += batch_size;
++n;
}
if (proofs.size() != 1)
return false;
const size_t sz = proofs[0].V.size();
if (sz == 0 || sz > BULLETPROOF_MAX_OUTPUTS)
return false;
return true;
}
//-----------------------------------------------------------------------------------------------
@ -816,11 +770,9 @@ namespace cryptonote
}
break;
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))
{
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);
tx_info[n].tvc.m_verifivation_failed = true;
tx_info[n].result = false;
@ -933,7 +885,8 @@ namespace cryptonote
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)
{MERROR_VER("Transaction verification failed: " << results[i].hash);}
else if(tvc[i].m_verifivation_impossible)
@ -1016,9 +969,9 @@ namespace cryptonote
}
// 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;
}
@ -1150,7 +1103,8 @@ namespace cryptonote
crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
blobdata 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
@ -1158,7 +1112,7 @@ namespace cryptonote
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)
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();
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()

@ -788,12 +788,12 @@ namespace cryptonote
*
* @param tx_hash the transaction's 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 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

@ -74,7 +74,7 @@ namespace cryptonote
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.vout.clear();
tx.extra.clear();
@ -89,7 +89,7 @@ namespace cryptonote
in.height = height;
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");
return false;
@ -491,7 +491,7 @@ namespace cryptonote
// 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.
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)
{

@ -37,7 +37,7 @@
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
{

@ -80,9 +80,13 @@ namespace cryptonote
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.
@ -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
CRITICAL_REGION_LOCAL(m_transactions_lock);
@ -173,17 +177,17 @@ namespace cryptonote
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_fee_too_low = true;
return false;
}
size_t tx_size_limit = get_transaction_size_limit(version);
if (!kept_by_block && blob_size > tx_size_limit)
size_t tx_weight_limit = get_transaction_weight_limit(version);
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_too_big = true;
return false;
@ -227,7 +231,7 @@ namespace cryptonote
// may become valid again, so ignore the failed inputs check.
if(kept_by_block)
{
meta.blob_size = blob_size;
meta.weight = tx_weight;
meta.fee = fee;
meta.max_used_block_id = null_hash;
meta.max_used_block_height = 0;
@ -248,7 +252,7 @@ namespace cryptonote
m_blockchain.add_txpool_tx(tx, meta);
if (!insert_key_images(tx, kept_by_block))
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)
{
@ -267,7 +271,7 @@ namespace cryptonote
}else
{
//update transactions container
meta.blob_size = blob_size;
meta.weight = tx_weight;
meta.kept_by_block = kept_by_block;
meta.fee = fee;
meta.max_used_block_id = max_used_block_id;
@ -290,7 +294,7 @@ namespace cryptonote
m_blockchain.add_txpool_tx(tx, meta);
if (!insert_key_images(tx, kept_by_block))
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)
{
@ -304,13 +308,13 @@ namespace cryptonote
}
tvc.m_verifivation_failed = false;
m_txpool_size += blob_size;
m_txpool_weight += tx_weight;
++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;
}
@ -321,26 +325,26 @@ namespace cryptonote
size_t blob_size = 0;
if (!get_transaction_hash(tx, h, blob_size) || blob_size == 0)
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);
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);
m_txpool_max_size = bytes;
m_txpool_max_weight = bytes;
}
//---------------------------------------------------------------------------------
void tx_memory_pool::prune(size_t bytes)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
if (bytes == 0)
bytes = m_txpool_max_size;
bytes = m_txpool_max_weight;
CRITICAL_REGION_LOCAL1(m_blockchain);
LockedTXN lock(m_blockchain);
bool changed = false;
@ -349,7 +353,7 @@ namespace cryptonote
auto it = --m_txs_by_fee_and_receive_time.end();
while (it != m_txs_by_fee_and_receive_time.begin())
{
if (m_txpool_size <= bytes)
if (m_txpool_weight <= bytes)
break;
try
{
@ -374,11 +378,11 @@ namespace cryptonote
return;
}
// 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_txpool_size -= txblob.size();
m_txpool_weight -= it->first.second;
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--);
changed = true;
}
@ -390,8 +394,8 @@ namespace cryptonote
}
if (changed)
++m_cookie;
if (m_txpool_size > bytes)
MINFO("Pool size after pruning is larger than limit: " << m_txpool_size << "/" << bytes);
if (m_txpool_weight > 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)
@ -446,7 +450,7 @@ namespace cryptonote
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_LOCAL1(m_blockchain);
@ -470,7 +474,7 @@ namespace cryptonote
MERROR("Failed to parse tx from txpool");
return false;
}
blob_size = meta.blob_size;
tx_weight = meta.weight;
fee = meta.fee;
relayed = meta.relayed;
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
m_blockchain.remove_txpool_tx(id);
m_txpool_size -= blob_size;
m_txpool_weight -= tx_weight;
remove_transaction_keyimages(tx);
}
catch (const std::exception &e)
@ -552,7 +556,7 @@ namespace cryptonote
{
// remove first, so we only remove key images if the tx removal succeeds
m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= bd.size();
m_txpool_weight -= get_transaction_weight(tx, bd.size());
remove_transaction_keyimages(tx);
}
}
@ -670,7 +674,7 @@ namespace cryptonote
const uint64_t now = time(NULL);
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){
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;
}, false, include_unrelayed_txes);
}
@ -682,15 +686,15 @@ namespace cryptonote
const uint64_t now = time(NULL);
std::map<uint64_t, txpool_histo> agebytes;
stats.txs_total = m_blockchain.get_txpool_tx_count(include_unrelayed_txes);
std::vector<uint32_t> sizes;
sizes.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){
sizes.push_back(meta.blob_size);
stats.bytes_total += meta.blob_size;
if (!stats.bytes_min || meta.blob_size < stats.bytes_min)
stats.bytes_min = meta.blob_size;
if (meta.blob_size > stats.bytes_max)
stats.bytes_max = meta.blob_size;
std::vector<uint32_t> weights;
weights.reserve(stats.txs_total);
m_blockchain.for_all_txpool_txes([&stats, &weights, now, &agebytes](const crypto::hash &txid, const txpool_tx_meta_t &meta, const cryptonote::blobdata *bd){
weights.push_back(meta.weight);
stats.bytes_total += meta.weight;
if (!stats.bytes_min || meta.weight < stats.bytes_min)
stats.bytes_min = meta.weight;
if (meta.weight > stats.bytes_max)
stats.bytes_max = meta.weight;
if (!meta.relayed)
stats.num_not_relayed++;
stats.fee_total += meta.fee;
@ -702,12 +706,12 @@ namespace cryptonote
stats.num_failing++;
uint64_t age = now - meta.receive_time + (now == meta.receive_time);
agebytes[age].txs++;
agebytes[age].bytes += meta.blob_size;
agebytes[age].bytes += meta.weight;
if (meta.double_spend_seen)
++stats.num_double_spends;
return true;
}, 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)
{
/* looking for 98th percentile */
@ -771,7 +775,8 @@ namespace cryptonote
return true;
}
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.kept_by_block = meta.kept_by_block;
txi.max_used_block_height = meta.max_used_block_height;
@ -842,7 +847,8 @@ namespace cryptonote
return true;
}
txi.tx = tx;
txi.blob_size = meta.blob_size;
txi.blob_size = bd->size();
txi.weight = meta.weight;
txi.fee = meta.fee;
txi.kept_by_block = meta.kept_by_block;
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 << "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
<< "kept_by_block: " << (meta.kept_by_block ? '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
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_LOCAL1(m_blockchain);
uint64_t best_coinbase = 0, coinbase = 0;
total_size = 0;
total_weight = 0;
fee = 0;
//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_size_v5 = 2 * median_size - 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_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5;
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);
@ -1162,12 +1169,12 @@ namespace cryptonote
MERROR(" failed to find tx meta");
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
if (max_total_size < total_size + meta.blob_size)
// Can not exceed maximum block weight
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;
}
@ -1177,9 +1184,9 @@ namespace cryptonote
// If we're getting lower coinbase tx,
// stop including more tx
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;
}
coinbase = block_reward + fee + meta.fee;
@ -1191,11 +1198,11 @@ namespace cryptonote
}
else
{
// If we've exceeded the penalty free size,
// If we've exceeded the penalty free weight,
// 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;
}
}
@ -1241,16 +1248,16 @@ namespace cryptonote
}
bl.tx_hashes.push_back(sorted_it->second);
total_size += meta.blob_size;
total_weight += meta.weight;
fee += meta.fee;
best_coinbase = coinbase;
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;
LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, size "
<< total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase)
LOG_PRINT_L2("Block template filled with " << bl.tx_hashes.size() << " txes, weight "
<< total_weight << "/" << max_total_weight << ", coinbase " << print_money(best_coinbase)
<< " (including " << print_money(fee) << " in fees)");
return true;
}
@ -1259,14 +1266,14 @@ namespace cryptonote
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
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;
m_txpool_size = 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_txpool_size += meta.blob_size;
if (meta.blob_size > tx_size_limit) {
LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.blob_size << " bytes), removing it from pool");
m_txpool_weight = 0;
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_weight += meta.weight;
if (meta.weight > tx_weight_limit) {
LOG_PRINT_L1("Transaction " << txid << " is too big (" << meta.weight << " bytes), removing it from pool");
remove.insert(txid);
}
else if (m_blockchain.have_tx(txid)) {
@ -1293,7 +1300,7 @@ namespace cryptonote
}
// remove tx from db first
m_blockchain.remove_txpool_tx(txid);
m_txpool_size -= txblob.size();
m_txpool_weight -= get_transaction_weight(tx, txblob.size());
remove_transaction_keyimages(tx);
auto sorted_it = find_tx_in_sorted_container(txid);
if (sorted_it == m_txs_by_fee_and_receive_time.end())
@ -1318,15 +1325,15 @@ namespace cryptonote
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_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_spent_key_images.clear();
m_txpool_size = 0;
m_txpool_weight = 0;
std::vector<crypto::hash> remove;
// 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");
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_txpool_size += meta.blob_size;
m_txs_by_fee_and_receive_time.emplace(std::pair<double, time_t>(meta.fee / (double)meta.weight, meta.receive_time), txid);
m_txpool_weight += meta.weight;
return true;
}, true);
if (!r)

@ -84,7 +84,7 @@ namespace cryptonote
*
* This handling includes:
* 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
* saving the transactions to disk on shutdown
* 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)
*
* @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
@ -133,7 +133,7 @@ namespace cryptonote
*
* @param id the hash of the transaction
* @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 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?
@ -141,7 +141,7 @@ namespace cryptonote
*
* @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
@ -198,11 +198,11 @@ namespace cryptonote
/**
* @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
*/
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
@ -219,16 +219,16 @@ namespace cryptonote
* @brief Chooses transactions for a block to include
*
* @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 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 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
*
* @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
@ -249,7 +249,7 @@ namespace cryptonote
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 include_unrelayed_txes include unrelayed txes in the result
@ -370,21 +370,21 @@ namespace cryptonote
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_TX_DETAILS_ARCHIVE_VER 12
#define CURRENT_MEMPOOL_TX_DETAILS_ARCHIVE_VER 13
/**
* @brief information about a single transaction
@ -393,6 +393,7 @@ namespace cryptonote
{
transaction tx; //!< the transaction
size_t blob_size; //!< the transaction's size
size_t weight; //!< the transaction's weight
uint64_t fee; //!< the transaction's fee amount
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
@ -522,7 +523,7 @@ namespace cryptonote
/**
* @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);
@ -578,8 +579,8 @@ private:
Blockchain& m_blockchain; //!< reference to the Blockchain object
size_t m_txpool_max_size;
size_t m_txpool_size;
size_t m_txpool_max_weight;
size_t m_txpool_weight;
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)
return;
ar & td.do_not_relay;
if (version < 13)
return;
ar & td.weight;
}
}
}

@ -76,6 +76,7 @@ namespace {
<< "difficulty: " << boost::lexical_cast<std::string>(header.difficulty) << std::endl
<< "POW hash: " << header.pow_hash << std::endl
<< "block size: " << header.block_size << std::endl
<< "block weight: " << header.block_weight << std::endl
<< "num txes: " << header.num_txes << std::endl
<< "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
<< "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
<< "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;
@ -857,8 +858,9 @@ bool t_rpc_command_executor::print_transaction_pool_long() {
tools::msg_writer() << "id: " << tx_info.id_hash << std::endl
<< tx_info.tx_json << 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/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
<< "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
@ -940,8 +942,9 @@ bool t_rpc_command_executor::print_transaction_pool_short() {
{
tools::msg_writer() << "id: " << tx_info.id_hash << 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/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
<< "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
@ -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;
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)
{
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_BLOCK_HEADERS_RANGE::request bhreq;
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response bhres;
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::request fereq;
cryptonote::COMMAND_RPC_GET_PER_KB_FEE_ESTIMATE::response feres;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request fereq;
cryptonote::COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response feres;
epee::json_rpc::error error_resp;
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);
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);
return true;
@ -1762,8 +1765,8 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
double avgdiff = 0;
double avgnumtxes = 0;
double avgreward = 0;
std::vector<uint64_t> sizes;
sizes.reserve(nblocks);
std::vector<uint64_t> weights;
weights.reserve(nblocks);
uint64_t earliest = std::numeric_limits<uint64_t>::max(), latest = 0;
std::vector<unsigned> major_versions(256, 0), minor_versions(256, 0);
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;
avgnumtxes += bhr.num_txes;
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.minor_version) == 1, "major_version expected to be uint8_t");
major_versions[(unsigned)bhr.major_version]++;
@ -1782,9 +1785,9 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
avgdiff /= nblocks;
avgnumtxes /= 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
<< ", 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;
while (max_major > 0 && !major_versions[--max_major]);

@ -758,7 +758,20 @@ namespace rct {
std::vector<uint64_t> proof_amounts;
size_t n_amounts = outamounts.size();
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;
if (range_proof_type == RangeProofMultiOutputBulletproof)

@ -29,6 +29,7 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "misc_log_ex.h"
#include "cryptonote_config.h"
#include "rctTypes.h"
using namespace crypto;
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)
{
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");
return 1 << (proof.L.size() - 6);
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");
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)
@ -254,4 +273,28 @@ namespace rct {
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;
}
}

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

@ -195,8 +195,8 @@ namespace cryptonote
res.stagenet = m_nettype == STAGENET;
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.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median();
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
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.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();
@ -1313,7 +1313,7 @@ namespace cryptonote
response.hash = string_tools::pod_to_hex(hash);
response.difficulty = m_core.get_blockchain_storage().block_difficulty(height);
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.pow_hash = fill_pow_hash ? string_tools::pod_to_hex(get_block_longhash(blk, height)) : "";
return true;
@ -1646,8 +1646,8 @@ namespace cryptonote
res.stagenet = m_nettype == STAGENET;
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.block_size_limit = m_core.get_blockchain_storage().get_current_cumulative_blocksize_limit();
res.block_size_median = m_core.get_blockchain_storage().get_current_cumulative_blocksize_median();
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
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.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();
@ -1839,14 +1839,15 @@ namespace cryptonote
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;
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;
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;
return true;
}

@ -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_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("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("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)
@ -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_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_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_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);

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

@ -472,7 +472,8 @@ namespace rpc
res.info.testnet = m_core.get_nettype() == TESTNET;
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.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.status = Message::STATUS_OK;
@ -763,7 +764,7 @@ namespace rpc
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;
}

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

@ -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_hash, tx.tx_hash);
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, max_used_block_hash, tx.max_used_block_hash);
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.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.max_used_block_hash, max_used_block_hash);
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, 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_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);
}
@ -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.cumulative_difficulty, cumulative_difficulty);
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);
}

@ -86,9 +86,9 @@ typedef cryptonote::simple_wallet sw;
#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() \
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");
return true;
}
const uint64_t per_kb_fee = m_wallet->get_per_kb_fee();
const uint64_t typical_size_kb = 13;
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 bool per_byte = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE);
const uint64_t base_fee = m_wallet->get_base_fee();
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;
for (uint32_t priority = 1; priority <= 4; ++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;
try
{
uint64_t base_size = typical_size_kb * 1024;
blocks = m_wallet->estimate_backlog(base_size, base_size + 1023, fees);
uint64_t base_size = typical_size * size_granularity;
blocks = m_wallet->estimate_backlog(base_size, base_size + size_granularity - 1, fees);
}
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)
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();
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();
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;
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();
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;
if (locked) {
@ -5466,12 +5481,28 @@ bool simple_wallet::sweep_single(const std::vector<std::string> &args_)
if (fake_outs_count == 0)
fake_outs_count = DEFAULT_MIX;
}
else if (ring_size == 0)
{
fail_msg_writer() << tr("Ring size must not be 0");
return true;
}
else
{
fake_outs_count = ring_size - 1;
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;
bool payment_id_seen = false;

@ -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();
if (fake_outs_count == 0)
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));

@ -50,12 +50,13 @@ void NodeRPCProxy::invalidate()
m_height = 0;
for (size_t n = 0; n < 256; ++n)
m_earliest_height[n] = 0;
m_dynamic_per_kb_fee_estimate = 0;
m_dynamic_per_kb_fee_estimate_cached_height = 0;
m_dynamic_per_kb_fee_estimate_grace_blocks = 0;
m_dynamic_base_fee_estimate = 0;
m_dynamic_base_fee_estimate_cached_height = 0;
m_dynamic_base_fee_estimate_grace_blocks = 0;
m_fee_quantization_mask = 1;
m_rpc_version = 0;
m_target_height = 0;
m_block_size_limit = 0;
m_block_weight_limit = 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");
m_height = resp_t.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;
}
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>();
}
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();
if (res)
return res;
block_size_limit = m_block_size_limit;
block_weight_limit = m_block_weight_limit;
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>();
}
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;
@ -161,10 +162,10 @@ boost::optional<std::string> NodeRPCProxy::get_dynamic_per_kb_fee_estimate(uint6
if (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_PER_KB_FEE_ESTIMATE::response resp_t = AUTO_VAL_INIT(resp_t);
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 = 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(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_per_kb_fee_estimate = resp_t.fee;
m_dynamic_per_kb_fee_estimate_cached_height = height;
m_dynamic_per_kb_fee_estimate_grace_blocks = grace_blocks;
m_dynamic_base_fee_estimate = resp_t.fee;
m_dynamic_base_fee_estimate_cached_height = height;
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>();
}

@ -47,9 +47,10 @@ public:
boost::optional<std::string> get_height(uint64_t &height) const;
void set_height(uint64_t h);
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_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:
boost::optional<std::string> get_info() const;
@ -59,12 +60,13 @@ private:
mutable uint64_t m_height;
mutable uint64_t m_earliest_height[256];
mutable uint64_t m_dynamic_per_kb_fee_estimate;
mutable uint64_t m_dynamic_per_kb_fee_estimate_cached_height;
mutable uint64_t m_dynamic_per_kb_fee_estimate_grace_blocks;
mutable uint64_t m_dynamic_base_fee_estimate;
mutable uint64_t m_dynamic_base_fee_estimate_cached_height;
mutable uint64_t m_dynamic_base_fee_estimate_grace_blocks;
mutable uint64_t m_fee_quantization_mask;
mutable uint32_t m_rpc_version;
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;
};

@ -84,8 +84,8 @@ using namespace cryptonote;
// used to choose when to stop adding outputs to a tx
#define APPROXIMATE_INPUT_BYTES 80
// used to target a given block size (additional outputs may be added on top to build fee)
#define TX_SIZE_TARGET(bytes) (bytes*2/3)
// used to target a given block weight (additional outputs may be added on top to build fee)
#define TX_WEIGHT_TARGET(bytes) (bytes*2/3)
// arbitrary, used to generate different hashes from the same input
#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;
}
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)
@ -576,7 +578,12 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
// rangeSigs
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
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
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;
}
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)
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
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()
{
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 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_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
m_http_client.disconnect();
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_login = std::move(daemon_login);
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;
for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
if (proof.V.size() > 1)
range_proof_type = rct::RangeProofMultiOutputBulletproof;
range_proof_type = rct::RangeProofPaddedBulletproof;
}
crypto::secret_key tx_key;
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;
for (const rct::Bulletproof &proof: ptx.tx.rct_signatures.p.bulletproofs)
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);
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
{
static const uint64_t old_multipliers[3] = {1, 2, 3};
static const uint64_t new_multipliers[3] = {1, 20, 166};
static const uint64_t newer_multipliers[4] = {1, 4, 20, 166};
static const struct
{
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)
fee_algorithm = get_fee_algorithm();
@ -5817,47 +5873,68 @@ uint64_t wallet2::get_fee_multiplier(uint32_t priority, int fee_algorithm) const
priority = 1;
}
THROW_WALLET_EXCEPTION_IF(fee_algorithm < 0 || fee_algorithm > 3, error::invalid_priority);
// 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)
{
switch (fee_algorithm)
{
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);
}
return multipliers[fee_algorithm].multipliers[priority-1];
}
THROW_WALLET_EXCEPTION_IF (false, error::invalid_priority);
return 1;
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::get_dynamic_per_kb_fee_estimate() const
uint64_t wallet2::get_dynamic_base_fee_estimate() const
{
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)
return fee;
LOG_PRINT_L1("Failed to query per kB fee, using " << print_money(FEE_PER_KB));
return FEE_PER_KB;
const uint64_t base_fee = use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? FEE_PER_BYTE : 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)
return m_light_wallet_per_kb_fee;
{
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;
}
bool use_dyn_fee = use_fork_rules(HF_VERSION_DYNAMIC_FEE, -720 * 1);
if (!use_dyn_fee)
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
{
// 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))
return 2;
if (use_fork_rules(3, -720 * 14))
@ -5865,19 +5942,39 @@ int wallet2::get_fee_algorithm() const
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
{
if (mixin < 6 && use_fork_rules(7, 10)) {
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 7, using 7");
mixin = 6;
}
else if (mixin < 4 && use_fork_rules(6, 10)) {
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 6, using 5");
mixin = 4;
const uint64_t min_ring_size = get_min_ring_size();
if (mixin + 1 < min_ring_size)
{
MWARNING("Requested ring size " << (mixin + 1) << " too low, using " << min_ring_size);
mixin = min_ring_size-1;
}
else if (mixin < 2 && use_fork_rules(2, 10)) {
MWARNING("Requested ring size " << (mixin + 1) << " too low for hard fork 2, using 3");
mixin = 2;
const uint64_t max_ring_size = get_max_ring_size();
if (max_ring_size && mixin + 1 > max_ring_size)
{
MWARNING("Requested ring size " << (mixin + 1) << " too high, using " << max_ring_size);
mixin = max_ring_size-1;
}
return mixin;
}
@ -5889,7 +5986,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
try
{
// 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)});
if (blocks.size() != 1)
{
@ -5903,10 +6003,10 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
}
// get the current full reward zone
uint64_t block_size_limit = 0;
const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
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
const size_t N = 10;
@ -5930,14 +6030,14 @@ uint32_t wallet2::adjust_priority(uint32_t priority)
MERROR("Bad blockheaders size");
return priority;
}
size_t block_size_sum = 0;
size_t block_weight_sum = 0;
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
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());
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 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 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
size_t attempt_count = 0;
@ -5991,13 +6093,12 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
pending_tx ptx;
// 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 = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
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);
do
{
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);
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);
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");
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;
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);
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(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;
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_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;
LOG_PRINT_L2("transfer_selected_rct: starting with fee " << print_money (needed_money));
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);
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(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
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);
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(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");
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;
cryptonote::transaction tx;
pending_tx ptx;
size_t bytes;
size_t weight;
uint64_t needed_fee;
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) {
if (merge_destinations)
@ -8022,13 +8123,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<TX> txes;
bool adding_fee; // true if new outputs go towards fee, rather than destinations
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 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_quantization_mask = get_fee_quantization_mask();
// throw if attempting a transaction with no destinations
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
// 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
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 unlocked_balance_subtotal = 0;
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);
// 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_size_two_rings = estimate_tx_size(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!");
const size_t tx_size_per_ring = tx_size_two_rings - tx_size_one_ring;
const uint64_t fractional_threshold = (fee_multiplier * fee_per_kb * tx_size_per_ring) / 1024;
const size_t tx_weight_one_ring = estimate_tx_weight(use_rct, 1, 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_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_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
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
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
// 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);
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)) {
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_dust_indices: " << strjoin(*unused_dust_indices, " "));
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
{
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
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;
}
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
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));
@ -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
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;
// if we have preferred picks, but haven't yet used all of them, continue
if (preferred_inputs.empty())
@ -8315,8 +8418,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
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);
try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
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_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;
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 = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
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);
uint64_t inputs = 0, outputs = needed_fee;
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,
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);
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);
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)");
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,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
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_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" 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");
tx.tx = test_tx;
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
tx.needed_fee = needed_fee;
accumulated_fee += test_ptx.fee;
@ -8475,7 +8577,7 @@ skip_tx:
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
tx.tx = test_tx;
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.weight = get_transaction_weight(test_tx, txBlob.size());
}
std::vector<wallet2::pending_tx> ptx_vector;
@ -8486,7 +8588,7 @@ skip_tx:
for (size_t idx: tx.selected_transfers)
tx_money += m_transfers[idx].amount();
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 " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
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;
cryptonote::transaction tx;
pending_tx ptx;
size_t bytes;
size_t weight;
uint64_t needed_fee;
std::vector<std::vector<get_outs_entry>> outs;
TX() : bytes(0), needed_fee(0) {}
TX() : weight(0), needed_fee(0) {}
};
std::vector<TX> txes;
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;
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 bulletproof = use_fork_rules(get_bulletproof_fork(), 0);
const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofBulletproof : rct::RangeProofBorromean;
const uint64_t fee_per_kb = get_per_kb_fee();
const rct::RangeProofType range_proof_type = bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean;
const uint64_t base_fee = get_base_fee();
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");
@ -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
// 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
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];
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
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_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);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
<< upper_transaction_weight_limit);
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_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit));
if (try_tx) {
cryptonote::transaction test_tx;
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 = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
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);
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,
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);
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;
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)");
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,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(fee_per_kb, txBlob, fee_multiplier);
LOG_PRINT_L2("Made an attempt at a final " << get_size_string(txBlob) << " tx, with " << print_money(test_ptx.fee) <<
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_weight_string(test_ptx.tx, txBlob.size()) << " tx, with " << print_money(test_ptx.fee) <<
" fee and " << print_money(test_ptx.change_dts.amount) << " change");
} 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");
tx.tx = test_tx;
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.weight = get_transaction_weight(test_tx, txBlob.size());
tx.outs = outs;
tx.needed_fee = needed_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);
tx.tx = test_tx;
tx.ptx = test_ptx;
tx.bytes = txBlob.size();
tx.weight = get_transaction_weight(test_tx, txBlob.size());
}
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)
tx_money += m_transfers[idx].amount();
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 " <<
print_money(tx.ptx.fee) << " fee, " << print_money(tx.ptx.change_dts.amount) << " change");
ptx_vector.push_back(tx.ptx);
@ -8763,12 +8884,15 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
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)
return m_upper_transaction_size_limit;
if (m_upper_transaction_weight_limit > 0)
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;
return full_reward_zone - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
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;
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f) const
@ -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()
{
// request all outputs with less than 3 instances
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(min_mixin + 1, false, true, false);
// request all outputs with less instances than the min ring size
return select_available_outputs_from_histogram(get_min_ring_size(), false, true, false);
}
//----------------------------------------------------------------------------------------------------
std::vector<size_t> wallet2::select_available_mixable_outputs()
{
// request all outputs with at least 3 instances, so we can use mixin 2 with
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(min_mixin + 1, true, true, true);
// request all outputs with at least as many instances as the min ring size
return select_available_outputs_from_histogram(get_min_ring_size(), true, true, true);
}
//----------------------------------------------------------------------------------------------------
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
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
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;
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);
else
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_OK, error::get_tx_pool_error);
uint64_t block_size_limit = 0;
const auto result = m_node_rpc_proxy.get_block_size_limit(block_size_limit);
uint64_t block_weight_limit = 0;
const auto result = m_node_rpc_proxy.get_block_weight_limit(block_weight_limit);
throw_on_rpc_response_error(result, "get_info");
uint64_t full_reward_zone = block_size_limit / 2;
THROW_WALLET_EXCEPTION_IF(full_reward_zone == 0, error::wallet_internal_error, "Invalid block size limit from daemon");
uint64_t full_reward_zone = block_weight_limit / 2;
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;
for (const auto &fee_level: fee_levels)
{
const double our_fee_byte_min = fee_level.first;
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)
{
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;
}
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)
priority_size_min += i.blob_size;
priority_weight_min += i.weight;
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_max = priority_size_max / full_reward_zone;
MDEBUG("estimate_backlog: priority_size " << priority_size_min << " - " << priority_size_max << " for "
uint64_t nblocks_min = priority_weight_min / full_reward_zone;
uint64_t nblocks_max = priority_weight_max / full_reward_zone;
MDEBUG("estimate_backlog: priority_weight " << priority_weight_min << " - " << priority_weight_max << " for "
<< 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));
}
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(max_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_tx_weight == 0, error::wallet_internal_error, "Invalid 0 fee");
for (uint64_t fee: fees)
{
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;
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);
}
return estimate_backlog(fee_levels);

@ -630,14 +630,9 @@ namespace tools
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;}
// 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 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); }
@ -1084,10 +1079,13 @@ namespace tools
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(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_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;
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_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;
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;
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;
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);
@ -1269,7 +1267,7 @@ namespace tools
std::unordered_map<std::string, std::string> m_attributes;
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;
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<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");
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;
// calculate total amount being sent to all destinations
@ -1970,7 +1968,7 @@ namespace tools
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);
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;
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool

@ -679,30 +679,30 @@ namespace tools
//----------------------------------------------------------------------------------------------------
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")
, 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; }
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::ostringstream ss;
cryptonote::transaction tx = m_tx;
ss << transfer_error::to_string() <<
", tx_size_limit = " << m_tx_size_limit <<
", tx size = " << get_object_blobsize(m_tx) <<
", tx_weight_limit = " << m_tx_weight_limit <<
", tx weight = " << get_transaction_weight(m_tx) <<
", tx:\n" << cryptonote::obj_to_json_str(tx);
return ss.str();
}
private:
cryptonote::transaction m_tx;
uint64_t m_tx_size_limit;
uint64_t m_tx_weight_limit;
};
//----------------------------------------------------------------------------------------------------
struct zero_destination : public transfer_error

@ -1542,7 +1542,6 @@ namespace tools
rpc_transfers.spent = td.m_spent;
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_size = txBlob.size();
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) : "";
res.transfers.push_back(rpc_transfers);

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

@ -36,24 +36,24 @@ using namespace cryptonote;
namespace
{
bool construct_miner_tx_by_size(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,
size_t target_block_size, uint64_t fee = 0)
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_weights, size_t target_tx_weight,
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;
size_t current_size = get_object_blobsize(miner_tx);
size_t current_weight = get_transaction_weight(miner_tx);
size_t try_count = 0;
while (target_tx_size != current_size)
while (target_tx_weight != current_weight)
{
++try_count;
if (10 < try_count)
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())
miner_tx.extra.resize(miner_tx.extra.size() - diff);
else
@ -61,28 +61,28 @@ namespace
}
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);
}
current_size = get_object_blobsize(miner_tx);
current_weight = get_transaction_weight(miner_tx);
}
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)
{
std::vector<size_t> block_sizes;
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_prev), median_block_count);
std::vector<size_t> block_weights;
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));
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),
miner_account.get_keys().m_account_address, block_sizes, 2 * median, 2 * median);
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_weights, 2 * median, 2 * median);
if (!r)
return false;
@ -97,7 +97,7 @@ namespace
for (size_t i = 0; i < block_count; ++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;
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
DO_CALLBACK(events, "mark_invalid_block");
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;
events.push_back(blk_1_bad_1);
DO_CALLBACK(events, "mark_invalid_block");
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;
events.push_back(blk_1_bad_2);
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;
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_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);
std::vector<size_t> block_sizes;
generator.get_last_n_block_sizes(block_sizes, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
size_t median = misc_utils::median(block_sizes);
std::vector<size_t> block_weights;
generator.get_last_n_block_weights(block_weights, get_block_hash(blk_7), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
size_t median = misc_utils::median(block_weights);
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),
miner_account.get_keys().m_account_address, block_sizes, 2 * median - txs_1_size, 2 * median, txs_fee);
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_weights, 2 * median - txs_1_weight, 2 * median, txs_fee);
if (!r)
return false;
@ -206,7 +206,7 @@ bool gen_block_reward::generate(std::vector<test_event_entry>& events) const
block blk_8;
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);
DO_CALLBACK(events, "mark_checked_block");

@ -586,11 +586,11 @@ bool gen_block_invalid_binary_format::generate(std::vector<test_event_entry>& ev
block blk_test;
std::vector<crypto::hash> tx_hashes;
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);
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,
crypto::hash(), diffic, transaction(), tx_hashes, txs_size))
crypto::hash(), diffic, transaction(), tx_hashes, txs_weight))
return false;
blobdata blob = t_serializable_object_to_blob(blk_test);

@ -42,7 +42,7 @@ using namespace cryptonote;
// Tests
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(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);
MAKE_GENESIS_BLOCK(events, blk_0, miner_account, ts_start);
// create 8 miner accounts, and have them mine the next 8 blocks
cryptonote::account_base miner_accounts[8];
// create 12 miner accounts, and have them mine the next 12 blocks
cryptonote::account_base miner_accounts[12];
const cryptonote::block *prev_block = &blk_0;
cryptonote::block blocks[8 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW];
for (size_t n = 0; n < 8; ++n) {
cryptonote::block blocks[12 + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW];
for (size_t n = 0; n < 12; ++n) {
miner_accounts[n].generate();
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,
@ -70,16 +70,16 @@ bool gen_bp_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
// rewind
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)
{
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,
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),
false, "Failed to generate block");
events.push_back(blocks[8+i]);
blk_last = blocks[8+i];
events.push_back(blocks[12+i]);
blk_last = blocks[12+i];
}
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];
src.amount = input_amounts_available[n];
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;
for (size_t i = 0; i < blocks[m].miner_tx.vout.size(); ++i)
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;
subaddresses[miner_accounts[n].get_keys().m_account_address.m_spend_public_key] = {0,0};
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");
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)
n_amounts += sizes[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)
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;
}
bool gen_bp_tx_valid_1::generate(std::vector<test_event_entry>& events) const
{
const int mixin = 6;
const int out_idx[] = {1, -1};
const size_t mixin = 10;
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const size_t bp_sizes[] = {1, (size_t)-1};
const bool multi_out[] = {false};
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"); });
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_1"); });
}
bool gen_bp_tx_invalid_1_1::generate(std::vector<test_event_entry>& events) const
{
const int mixin = 6;
const int out_idx[] = {1, -1};
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {1, 1, (size_t)-1};
const bool multi_out[] = {false};
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"); });
const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL);
}
bool gen_bp_tx_valid_2::generate(std::vector<test_event_entry>& events) const
{
const int mixin = 6;
const int out_idx[] = {1, -1};
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const size_t bp_sizes[] = {2, (size_t)-1};
const bool multi_out[] = {true};
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"); });
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_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 int out_idx[] = {1, -1};
const size_t mixin = 10;
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 size_t bp_sizes[] = {4, 2, 1, (size_t)-1};
const bool multi_out[] = {true};
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"); });
const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof };
return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL);
}
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 int out_idx[] = {1, -1};
const size_t mixin = 10;
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 bool multi_out[] = {true};
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"); });
const rct::RangeProofType range_proof_type[] = { rct::RangeProofMultiOutputBulletproof };
return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, NULL);
}
bool gen_bp_txs_valid_2_and_2::generate(std::vector<test_event_entry>& events) const
{
const int mixin = 6;
const int out_idx[] = {1, -1};
const size_t mixin = 10;
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 bool multi_out[] = {true};
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"); });
const rct::RangeProofType range_proof_type[] = { rct::RangeProofPaddedBulletproof, rct::RangeProofPaddedBulletproof};
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 int out_idx[] = {1, -1};
const size_t mixin = 10;
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 size_t bp_sizes[] = {2, (size_t)-1, 8, 2, (size_t)-1, 16, 16, 1, (size_t)-1};
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"); });
const rct::RangeProofType range_proof_type[] = {rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof, rct::RangeProofMultiOutputBulletproof};
return generate_with(events, mixin, 3, amounts_paid, false, range_proof_type, NULL, NULL);
}
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");
const int mixin = 6;
const int out_idx[] = {1, -1};
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const bool multi_out[] = {false};
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
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.p.bulletproofs.empty());
tx.rct_signatures.p.bulletproofs.pop_back();
CHECK_TEST_CONDITION(!tx.rct_signatures.p.bulletproofs.empty());
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
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_too_many_proofs");
const int mixin = 6;
const int out_idx[] = {1, -1};
const size_t mixin = 10;
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const bool multi_out[] = {false};
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){
const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
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.p.bulletproofs.empty());
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
{
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_wrong_amount");
const int mixin = 6;
const int out_idx[] = {1, -1};
const size_t mixin = 10;
const uint64_t amounts_paid[] = {10000, (uint64_t)-1};
const bool multi_out[] = {false};
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t idx){
const rct::RangeProofType range_proof_type[] = { rct::RangeProofBulletproof };
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.p.bulletproofs.empty());
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");
const int mixin = 6;
const int out_idx[] = {1, -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 bool multi_out[] = {true};
return generate_with(events, out_idx, mixin, 1, amounts_paid, false, multi_out, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
DEFINE_TESTS_ERROR_CONTEXT("gen_bp_tx_invalid_borromean_type");
const size_t mixin = 10;
const uint64_t amounts_paid[] = {5000, 5000, (uint64_t)-1};
const rct::RangeProofType range_proof_type[] = {rct::RangeProofPaddedBulletproof};
return generate_with(events, mixin, 1, amounts_paid, false, range_proof_type, NULL, [&](cryptonote::transaction &tx, size_t tx_idx){
CHECK_TEST_CONDITION(tx.rct_signatures.type == rct::RCTTypeBulletproof);
CHECK_TEST_CONDITION(tx.rct_signatures.p.bulletproofs.size() == 2);
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;
tx.rct_signatures.type = rct::RCTTypeSimple;
return true;
});
}

@ -81,8 +81,8 @@ struct gen_bp_tx_validation_base : public test_chain_unit_base
return true;
}
bool 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,
bool generate_with(std::vector<test_event_entry>& events, 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<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;
@ -95,7 +95,7 @@ private:
template<>
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 = {
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> {};
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;
};
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;
};
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
{
@ -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> {};
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;
};
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
{
@ -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> {};
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;
};
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> {};

@ -69,13 +69,13 @@ void test_generator::get_block_chain(std::vector<block_info>& blockchain, const
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;
get_block_chain(blockchain, head, n);
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);
}
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;
get_block_reward(misc_utils::median(block_sizes), block_size, 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);
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_weight);
}
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,
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.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;
size_t txs_size = 0;
size_t txs_weight = 0;
BOOST_FOREACH(auto& tx, tx_list)
{
uint64_t fee = 0;
bool r = get_tx_fee(tx, fee);
CHECK_AND_ASSERT_MES(r, false, "wrong transaction passed to construct_block");
total_fee += fee;
txs_size += get_object_blobsize(tx);
txs_weight += get_transaction_weight(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)
{
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;
size_t actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
if (target_block_size < actual_block_size)
size_t actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
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);
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
if (actual_block_size == target_block_size)
actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (actual_block_weight == target_block_weight)
{
break;
}
else
{
CHECK_AND_ASSERT_MES(target_block_size < actual_block_size, false, "Unexpected block size");
delta = actual_block_size - target_block_size;
CHECK_AND_ASSERT_MES(target_block_weight < actual_block_weight, false, "Unexpected block size");
delta = actual_block_weight - target_block_weight;
blk.miner_tx.extra.resize(blk.miner_tx.extra.size() - delta);
actual_block_size = txs_size + get_object_blobsize(blk.miner_tx);
if (actual_block_size == target_block_size)
actual_block_weight = txs_weight + get_transaction_weight(blk.miner_tx);
if (actual_block_weight == target_block_weight)
{
break;
}
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);
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))
blk.timestamp++;
add_block(blk, txs_size, block_sizes, already_generated_coins);
add_block(blk, txs_weight, block_weights, already_generated_coins);
return true;
}
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;
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,
@ -204,10 +204,10 @@ bool test_generator::construct_block(cryptonote::block& blk, const cryptonote::b
// Keep difficulty unchanged
uint64_t timestamp = blk_prev.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN;
uint64_t already_generated_coins = get_already_generated_coins(prev_id);
std::vector<size_t> block_sizes;
get_last_n_block_sizes(block_sizes, prev_id, CRYPTONOTE_REWARD_BLOCKS_WINDOW);
std::vector<size_t> block_weights;
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,
@ -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 transaction& miner_tx/* = transaction()*/,
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.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;
uint64_t already_generated_coins = get_already_generated_coins(prev_block);
std::vector<size_t> block_sizes;
get_last_n_block_sizes(block_sizes, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
std::vector<size_t> block_weights;
get_last_n_block_weights(block_weights, get_block_hash(prev_block), CRYPTONOTE_REWARD_BLOCKS_WINDOW);
if (actual_params & bf_miner_tx)
{
blk.miner_tx = miner_tx;
}
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
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;
}
@ -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();
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;
}
bool test_generator::construct_block_manually_tx(cryptonote::block& blk, const cryptonote::block& prev_block,
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);
}

@ -159,20 +159,20 @@ public:
block_info()
: prev_id()
, 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)
, already_generated_coins(an_already_generated_coins)
, block_size(a_block_size)
, block_weight(a_block_weight)
{
}
crypto::hash prev_id;
uint64_t already_generated_coins;
size_t block_size;
size_t block_weight;
};
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_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 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);
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,
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::block& blk_prev, const cryptonote::account_base& miner_acc,
const std::list<cryptonote::transaction>& tx_list = std::list<cryptonote::transaction>());

@ -227,14 +227,16 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_bp_tx_valid_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_4_2_1);
GENERATE_AND_PLAY(gen_bp_tx_valid_16_16);
GENERATE_AND_PLAY(gen_bp_tx_valid_3);
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_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_too_many_proofs);
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);
MLOG(level, "\nREPORT:");

@ -40,7 +40,7 @@
#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>
{
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 outputs = a_outputs;
static const bool rct = a_rct;
static const bool bulletproof = a_bulletproof;
typedef multi_tx_test_base<a_ring_size> base_class;
@ -72,7 +71,7 @@ public:
std::vector<crypto::secret_key> additional_tx_keys;
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};
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;
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));
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;
}

@ -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, 10, true);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 1, 2, false, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, false, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, false, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, false, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, false, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, false);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, true);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, true);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, true);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, true);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 10, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 100, 2, false);
TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 10, 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, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofBorromean);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof);
TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof);
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, 10, 2, 64);

@ -40,14 +40,14 @@ namespace
class block_reward_and_already_generated_coins : public ::testing::Test
{
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;
uint64_t m_block_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_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:
virtual void SetUp()
@ -84,9 +84,9 @@ namespace
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;
@ -96,28 +96,28 @@ namespace
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);
ASSERT_TRUE(m_block_not_too_big);
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);
ASSERT_TRUE(m_block_not_too_big);
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);
ASSERT_TRUE(m_block_not_too_big);
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);
ASSERT_TRUE(m_block_not_too_big);
@ -125,21 +125,21 @@ namespace
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);
ASSERT_TRUE(m_block_not_too_big);
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);
ASSERT_FALSE(m_block_not_too_big);
}
#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)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
@ -147,7 +147,7 @@ namespace
#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)
size_t huge_size = std::numeric_limits<uint32_t>::max() + UINT64_C(2);
@ -157,94 +157,94 @@ namespace
#endif // __x86_64__
//--------------------------------------------------------------------------------------------------------------------
class block_reward_and_last_block_sizes : public ::testing::Test
class block_reward_and_last_block_weights : public ::testing::Test
{
protected:
virtual void SetUp()
{
m_last_block_sizes.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_sizes.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_sizes.push_back(13 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_weights.push_back(3 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_weights.push_back(5 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_weights.push_back(7 * CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1);
m_last_block_weights.push_back(11 * 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_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;
std::vector<size_t> m_last_block_sizes;
uint64_t m_last_block_sizes_median;
std::vector<size_t> m_last_block_weights;
uint64_t m_last_block_weights_median;
bool m_block_not_too_big;
uint64_t m_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_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_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_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_LT(m_block_reward, m_standard_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_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);
}
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_EQ(m_block_reward, m_standard_block_reward * 63 / 64);
// 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_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_EQ(m_block_reward, m_standard_block_reward * 15 / 64);
}

@ -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_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_difficulty(0));
ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0));

@ -129,7 +129,7 @@ TEST(bulletproofs, multi_splitting)
}
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));
for (size_t i = 0; i < n_outputs; ++i)
{

@ -58,46 +58,46 @@ namespace
TEST_F(fee, 10xmr)
{
// 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_per_kb_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_per_kb_fee(10000000000000, 1, 3), 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_base_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 / 100, 3), clamp_fee(2000000000));
ASSERT_EQ(Blockchain::get_dynamic_base_fee(10000000000000, 1, 3), 2000000000);
// 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_per_kb_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_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 * 2, 3), clamp_fee(2000000000 / 2));
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_base_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 * 20000ull, 3), clamp_fee(2000000000 / 20000));
}
TEST_F(fee, 1xmr)
{
// 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_per_kb_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_per_kb_fee(1000000000000, 1, 3), 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_base_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 / 100, 3), clamp_fee(200000000));
ASSERT_EQ(Blockchain::get_dynamic_base_fee(1000000000000, 1, 3), 200000000);
// 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_per_kb_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_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 * 2, 3), clamp_fee(200000000 / 2));
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_base_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 * 20000ull, 3), clamp_fee(200000000 / 20000));
}
TEST_F(fee, dot3xmr)
{
// 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_per_kb_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_per_kb_fee(300000000000, 1, 3), 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_base_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 / 100, 3), clamp_fee(60000000));
ASSERT_EQ(Blockchain::get_dynamic_base_fee(300000000000, 1, 3), 60000000);
// 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_per_kb_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_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 * 2, 3), clamp_fee(60000000 / 2));
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_base_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 * 20000ull, 3), clamp_fee(60000000 / 20000));
}
static bool is_more_or_less(double x, double y)
@ -116,7 +116,7 @@ namespace
600000000000ull, // .6 monero, minimum reward per block at 2min
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 * 2,
CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 * 10,
@ -127,9 +127,9 @@ namespace
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));
}
}
}

@ -72,7 +72,7 @@ public:
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 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_difficulty(const uint64_t& height) const { return 0; }
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 void add_block( const block& blk
, const size_t& block_size
, size_t block_weight
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, uint64_t num_rct_outs

Loading…
Cancel
Save