Skip to content

Commit

Permalink
ArticMine's new block weight algorithm /monero#5124
Browse files Browse the repository at this point in the history
This curbs runaway growth while still allowing substantial
spikes in block weight

Original specification from ArticMine:

here is the scaling proposal
Define: LongTermBlockWeight
Before fork:
LongTermBlockWeight = BlockWeight
At or after fork:
LongTermBlockWeight = min(BlockWeight, 1.4*LongTermEffectiveMedianBlockWeight)
Note: To avoid possible consensus issues over rounding the LongTermBlockWeight for a given block should be calculated to the nearest byte, and stored as a integer in the block itself. The stored LongTermBlockWeight is then used for future calculations of the LongTermEffectiveMedianBlockWeight and not recalculated each time.
Define:   LongTermEffectiveMedianBlockWeight
LongTermEffectiveMedianBlockWeight = max(300000, MedianOverPrevious100000Blocks(LongTermBlockWeight))
Change Definition of EffectiveMedianBlockWeight
From (current definition)
EffectiveMedianBlockWeight  = max(300000, MedianOverPrevious100Blocks(BlockWeight))
To (proposed definition)
EffectiveMedianBlockWeight  = min(max(300000, MedianOverPrevious100Blocks(BlockWeight)), 50*LongTermEffectiveMedianBlockWeight)
Notes:
1) There are no other changes to the existing penalty formula, median calculation, fees etc.
2) There is the requirement to store the LongTermBlockWeight of a block unencrypted in the block itself. This  is to avoid possible consensus issues over rounding and also to prevent the calculations from becoming unwieldy as we move away from the fork.
3) When the  EffectiveMedianBlockWeight cap is reached it is still possible to mine blocks up to 2x the EffectiveMedianBlockWeight by paying the corresponding penalty.

Note: the long term block weight is stored in the database, but not in the actual block itself,
since it requires recalculating anyway for verification.

AEON Notes:
 - The surge factor is reduced from x50 to x20.
 - The long term growth rate is reduced from 40% to 4%.
  • Loading branch information
moneromooo-monero authored and stoffu committed Jun 17, 2019
1 parent ce8249e commit 8d0d838
Show file tree
Hide file tree
Showing 24 changed files with 1,245 additions and 56 deletions.
3 changes: 2 additions & 1 deletion src/blockchain_db/blockchain_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,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
, uint64_t long_term_block_size
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, const std::vector<transaction>& txs
Expand Down Expand Up @@ -241,7 +242,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_size, long_term_block_size, cumulative_difficulty, coins_generated, num_rct_outs, blk_hash);
TIME_MEASURE_FINISH(time1);
time_add_block1 += time1;

Expand Down
17 changes: 16 additions & 1 deletion src/blockchain_db/blockchain_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,12 +359,14 @@ class BlockchainDB
*
* @param blk the block to be added
* @param block_size the size of the block (transactions and all)
* @param long_term_block_size the long term block 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 blk_hash the hash of the block
*/
virtual void add_block( const block& blk
, const size_t& block_size
, uint64_t long_term_block_size
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, uint64_t num_rct_outs
Expand All @@ -376,7 +378,7 @@ class BlockchainDB
*
* 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, const size_t& block_size, uint64_t long_term_block_size, 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
Expand Down Expand Up @@ -790,6 +792,7 @@ class BlockchainDB
*
* @param blk the block to be added
* @param block_size the size of the block (transactions and all)
* @param long_term_block_size the long term 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
Expand All @@ -798,6 +801,7 @@ class BlockchainDB
*/
virtual uint64_t add_block( const block& blk
, const size_t& block_size
, uint64_t long_term_block_size
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, const std::vector<transaction>& txs
Expand Down Expand Up @@ -985,6 +989,17 @@ class BlockchainDB
*/
virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const = 0;

/**
* @brief fetch a block's long term size
*
* If the block does not exist, the subclass should throw BLOCK_DNE
*
* @param height the height requested
*
* @return the long term size
*/
virtual uint64_t get_block_long_term_size(const uint64_t& height) const = 0;

/**
* @brief fetch a block's hash
*
Expand Down
209 changes: 204 additions & 5 deletions src/blockchain_db/lmdb/db_lmdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/current_function.hpp>
#include <boost/circular_buffer.hpp>
#include <memory> // std::unique_ptr
#include <cstring> // memcpy
#include <random>
Expand All @@ -54,7 +55,7 @@ using epee::string_tools::pod_to_hex;
using namespace crypto;

// Increase when the DB structure changes
#define VERSION 4
#define VERSION 5

namespace
{
Expand Down Expand Up @@ -284,7 +285,20 @@ typedef struct mdb_block_info_3
uint64_t bi_cum_rct;
} mdb_block_info_3;

typedef mdb_block_info_3 mdb_block_info;
typedef struct mdb_block_info_4
{
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_diff_lo;
uint64_t bi_diff_hi;
crypto::hash bi_hash;
uint64_t bi_cum_rct;
uint64_t bi_long_term_block_size;
} mdb_block_info_4;

typedef mdb_block_info_4 mdb_block_info;

typedef struct blk_height {
crypto::hash bh_hash;
Expand Down Expand Up @@ -692,7 +706,7 @@ uint64_t BlockchainLMDB::get_estimated_batch_size(uint64_t batch_num_blocks, uin
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, const size_t& block_size, uint64_t long_term_block_size, 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__);
Expand Down Expand Up @@ -752,6 +766,7 @@ void BlockchainLMDB::add_block(const block& blk, const size_t& block_size, const
const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data;
bi.bi_cum_rct += bi_prev->bi_cum_rct;
}
bi.bi_long_term_block_size = long_term_block_size;

MDB_val_set(val, bi);
result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP);
Expand Down Expand Up @@ -2103,6 +2118,29 @@ uint64_t BlockchainLMDB::get_block_already_generated_coins(const uint64_t& heigh
return ret;
}

uint64_t BlockchainLMDB::get_block_long_term_size(const uint64_t& height) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();

TXN_PREFIX_RDONLY();
RCURSOR(block_info);

MDB_val_set(result, height);
auto get_result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &result, MDB_GET_BOTH);
if (get_result == MDB_NOTFOUND)
{
throw0(BLOCK_DNE(std::string("Attempt to get block long term size from height ").append(boost::lexical_cast<std::string>(height)).append(" failed -- block info not in db").c_str()));
}
else if (get_result)
throw0(DB_ERROR("Error attempting to retrieve a long term block size from the db"));

mdb_block_info *bi = (mdb_block_info *)result.mv_data;
uint64_t ret = bi->bi_long_term_block_size;
TXN_POSTFIX_RDONLY();
return ret;
}

crypto::hash BlockchainLMDB::get_block_hash_from_height(const uint64_t& height) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
Expand Down Expand Up @@ -3096,7 +3134,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, const size_t& block_size, uint64_t long_term_block_size, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated,
const std::vector<transaction>& txs)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
Expand All @@ -3115,7 +3153,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_size, long_term_block_size, cumulative_difficulty, coins_generated, txs);
}
catch (const DB_ERROR_TXN_START& e)
{
Expand Down Expand Up @@ -4440,6 +4478,167 @@ void BlockchainLMDB::migrate_3_4()
txn.commit();
}

void BlockchainLMDB::migrate_4_5()
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
uint64_t i;
int result;
mdb_txn_safe txn(false);
MDB_val k, v;
char *ptr;
bool past_long_term_size = false;

MGINFO_YELLOW("Migrating blockchain from DB version 4 to 5 - this may take a while:");

do {
LOG_PRINT_L1("migrating block info:");

result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));

MDB_stat db_stats;
if ((result = mdb_stat(txn, m_blocks, &db_stats)))
throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
const uint64_t blockchain_height = db_stats.ms_entries;

boost::circular_buffer<uint64_t> long_term_block_sizes(CRYPTONOTE_LONG_TERM_BLOCK_SIZE_WINDOW_SIZE);

/* the block_info table name is the same but the old version and new version
* have incompatible data. Create a new table. We want the name to be similar
* to the old name so that it will occupy the same location in the DB.
*/
MDB_dbi o_block_info = m_block_info;
lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
mdb_set_dupsort(txn, m_block_info, compare_uint64);


MDB_cursor *c_blocks;
result = mdb_cursor_open(txn, m_blocks, &c_blocks);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str()));

MDB_cursor *c_old, *c_cur;
i = 0;
while(1) {
if (!(i % 1000)) {
if (i) {
LOGIF(el::Level::Info) {
std::cout << i << " / " << blockchain_height << " \r" << std::flush;
}
txn.commit();
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
}
result = mdb_cursor_open(txn, m_block_info, &c_cur);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str()));
result = mdb_cursor_open(txn, o_block_info, &c_old);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str()));
result = mdb_cursor_open(txn, m_blocks, &c_blocks);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str()));
if (!i) {
MDB_stat db_stat;
result = mdb_stat(txn, m_block_info, &db_stats);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str()));
i = db_stats.ms_entries;
}
}
result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT);
if (result == MDB_NOTFOUND) {
txn.commit();
break;
}
else if (result)
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str()));
const mdb_block_info_3 *bi_old = (const mdb_block_info_3*)v.mv_data;
mdb_block_info_4 bi;
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_diff_lo = bi_old->bi_diff_lo;
bi.bi_diff_hi = bi_old->bi_diff_hi;
bi.bi_hash = bi_old->bi_hash;
bi.bi_cum_rct = bi_old->bi_cum_rct;

// get block major version to determine which rule is in place
if (!past_long_term_size)
{
MDB_val_copy<uint64_t> kb(bi.bi_height);
MDB_val vb;
result = mdb_cursor_get(c_blocks, &kb, &vb, MDB_SET);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
if (vb.mv_size == 0)
throw0(DB_ERROR("Invalid data from m_blocks"));
const uint8_t block_major_version = *((const uint8_t*)vb.mv_data);
if (block_major_version >= HF_VERSION_LONG_TERM_BLOCK_SIZE)
past_long_term_size = true;
}

uint64_t long_term_block_size;
if (past_long_term_size)
{
std::vector<uint64_t> sizes(long_term_block_sizes.begin(), long_term_block_sizes.end());
uint64_t long_term_effective_block_median_size = std::max<uint64_t>(CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V1, epee::misc_utils::median(sizes));
long_term_block_size = std::min<uint64_t>(bi.bi_size, long_term_effective_block_median_size + long_term_effective_block_median_size * 2 / 50);
}
else
{
long_term_block_size = bi.bi_size;
}
long_term_block_sizes.push_back(long_term_block_size);
bi.bi_long_term_block_size = long_term_block_size;

MDB_val_set(nv, bi);
result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str()));
/* we delete the old records immediately, so the overall DB and mapsize should not grow.
* This is a little slower than just letting mdb_drop() delete it all at the end, but
* it saves a significant amount of disk space.
*/
result = mdb_cursor_del(c_old, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str()));
i++;
}

result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
/* Delete the old table */
result = mdb_drop(txn, o_block_info, 1);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str()));

RENAME_DB("block_infn");
mdb_dbi_close(m_env, m_block_info);

lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
mdb_set_dupsort(txn, m_block_info, compare_uint64);

txn.commit();
} while(0);

uint32_t version = 5;
v.mv_data = (void *)&version;
v.mv_size = sizeof(version);
MDB_val_copy<const char*> vk("version");
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
result = mdb_put(txn, m_properties, &vk, &v, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str()));
txn.commit();
}

void BlockchainLMDB::migrate(const uint32_t oldversion)
{
if (oldversion < 1)
Expand Down
7 changes: 7 additions & 0 deletions src/blockchain_db/lmdb/db_lmdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ class BlockchainLMDB : public BlockchainDB

virtual uint64_t get_block_already_generated_coins(const uint64_t& height) const;

virtual uint64_t get_block_long_term_size(const uint64_t& height) const;

virtual crypto::hash get_block_hash_from_height(const uint64_t& height) const;

virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) const;
Expand Down Expand Up @@ -274,6 +276,7 @@ class BlockchainLMDB : public BlockchainDB

virtual uint64_t add_block( const block& blk
, const size_t& block_size
, uint64_t long_term_block_size
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, const std::vector<transaction>& txs
Expand Down Expand Up @@ -318,6 +321,7 @@ class BlockchainLMDB : public BlockchainDB

virtual void add_block( const block& blk
, const size_t& block_size
, uint64_t long_term_block_size
, const difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, uint64_t num_rct_outs
Expand Down Expand Up @@ -399,6 +403,9 @@ class BlockchainLMDB : public BlockchainDB
// migrate from DB version 3 to 4
void migrate_3_4();

// migrate from DB version 4 to 5
void migrate_4_5();

void cleanup_batch();

private:
Expand Down
Loading

0 comments on commit 8d0d838

Please sign in to comment.