mirror of
https://github.com/monero-project/monero.git
synced 2024-11-18 18:11:03 +00:00
blockchain: keep alternative blocks in LMDB
Alternative blocks are cleared on startup unless --keep-alt-blocks is passed on the command line
This commit is contained in:
parent
2bf855e3cd
commit
06b8f29992
7 changed files with 379 additions and 94 deletions
|
@ -129,6 +129,15 @@ struct tx_data_t
|
||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
struct alt_block_data_t
|
||||||
|
{
|
||||||
|
uint64_t height;
|
||||||
|
uint64_t cumulative_weight;
|
||||||
|
uint64_t cumulative_difficulty_low;
|
||||||
|
uint64_t cumulative_difficulty_high;
|
||||||
|
uint64_t already_generated_coins;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief a struct containing txpool per transaction metadata
|
* @brief a struct containing txpool per transaction metadata
|
||||||
*/
|
*/
|
||||||
|
@ -1528,8 +1537,45 @@ public:
|
||||||
*
|
*
|
||||||
* @param: sz the block size
|
* @param: sz the block size
|
||||||
*/
|
*/
|
||||||
|
|
||||||
virtual void add_max_block_size(uint64_t sz) = 0;
|
virtual void add_max_block_size(uint64_t sz) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief add a new alternative block
|
||||||
|
*
|
||||||
|
* @param: blkid the block hash
|
||||||
|
* @param: data: the metadata for the block
|
||||||
|
* @param: blob: the block's blob
|
||||||
|
*/
|
||||||
|
virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get an alternative block by hash
|
||||||
|
*
|
||||||
|
* @param: blkid the block hash
|
||||||
|
* @param: data: the metadata for the block
|
||||||
|
* @param: blob: the block's blob
|
||||||
|
*
|
||||||
|
* @return true if the block was found in the alternative blocks list, false otherwise
|
||||||
|
*/
|
||||||
|
virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief remove an alternative block
|
||||||
|
*
|
||||||
|
* @param: blkid the block hash
|
||||||
|
*/
|
||||||
|
virtual void remove_alt_block(const crypto::hash &blkid) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief get the number of alternative blocks stored
|
||||||
|
*/
|
||||||
|
virtual uint64_t get_alt_block_count() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief drop all alternative blocks
|
||||||
|
*/
|
||||||
|
virtual void drop_alt_blocks() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief runs a function over all txpool transactions
|
* @brief runs a function over all txpool transactions
|
||||||
*
|
*
|
||||||
|
@ -1619,6 +1665,23 @@ public:
|
||||||
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const = 0;
|
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const = 0;
|
||||||
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const = 0;
|
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief runs a function over all alternative blocks stored
|
||||||
|
*
|
||||||
|
* The subclass should run the passed function for each alt block it has
|
||||||
|
* stored, passing (blkid, data, blob) as its parameters.
|
||||||
|
*
|
||||||
|
* If any call to the function returns false, the subclass should return
|
||||||
|
* false. Otherwise, the subclass returns true.
|
||||||
|
*
|
||||||
|
* The subclass should throw DB_ERROR if any of the expected values are
|
||||||
|
* not found. Current implementations simply return false.
|
||||||
|
*
|
||||||
|
* @param std::function f the function to run
|
||||||
|
*
|
||||||
|
* @return false if the function returns false for any output, otherwise true
|
||||||
|
*/
|
||||||
|
virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const = 0;
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -194,6 +194,8 @@ namespace
|
||||||
* txpool_meta txn hash txn metadata
|
* txpool_meta txn hash txn metadata
|
||||||
* txpool_blob txn hash txn blob
|
* txpool_blob txn hash txn blob
|
||||||
*
|
*
|
||||||
|
* alt_blocks block hash {block data, block blob}
|
||||||
|
*
|
||||||
* Note: where the data items are of uniform size, DUPFIXED tables have
|
* Note: where the data items are of uniform size, DUPFIXED tables have
|
||||||
* been used to save space. In most of these cases, a dummy "zerokval"
|
* been used to save space. In most of these cases, a dummy "zerokval"
|
||||||
* key is used when accessing the table; the Key listed above will be
|
* key is used when accessing the table; the Key listed above will be
|
||||||
|
@ -221,6 +223,8 @@ const char* const LMDB_SPENT_KEYS = "spent_keys";
|
||||||
const char* const LMDB_TXPOOL_META = "txpool_meta";
|
const char* const LMDB_TXPOOL_META = "txpool_meta";
|
||||||
const char* const LMDB_TXPOOL_BLOB = "txpool_blob";
|
const char* const LMDB_TXPOOL_BLOB = "txpool_blob";
|
||||||
|
|
||||||
|
const char* const LMDB_ALT_BLOCKS = "alt_blocks";
|
||||||
|
|
||||||
const char* const LMDB_HF_STARTING_HEIGHTS = "hf_starting_heights";
|
const char* const LMDB_HF_STARTING_HEIGHTS = "hf_starting_heights";
|
||||||
const char* const LMDB_HF_VERSIONS = "hf_versions";
|
const char* const LMDB_HF_VERSIONS = "hf_versions";
|
||||||
|
|
||||||
|
@ -1400,6 +1404,8 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||||
lmdb_db_open(txn, LMDB_TXPOOL_META, MDB_CREATE, m_txpool_meta, "Failed to open db handle for m_txpool_meta");
|
lmdb_db_open(txn, LMDB_TXPOOL_META, MDB_CREATE, m_txpool_meta, "Failed to open db handle for m_txpool_meta");
|
||||||
lmdb_db_open(txn, LMDB_TXPOOL_BLOB, MDB_CREATE, m_txpool_blob, "Failed to open db handle for m_txpool_blob");
|
lmdb_db_open(txn, LMDB_TXPOOL_BLOB, MDB_CREATE, m_txpool_blob, "Failed to open db handle for m_txpool_blob");
|
||||||
|
|
||||||
|
lmdb_db_open(txn, LMDB_ALT_BLOCKS, MDB_CREATE, m_alt_blocks, "Failed to open db handle for m_alt_blocks");
|
||||||
|
|
||||||
// this subdb is dropped on sight, so it may not be present when we open the DB.
|
// this subdb is dropped on sight, so it may not be present when we open the DB.
|
||||||
// Since we use MDB_CREATE, we'll get an exception if we open read-only and it does not exist.
|
// Since we use MDB_CREATE, we'll get an exception if we open read-only and it does not exist.
|
||||||
// So we don't open for read-only, and also not drop below. It is not used elsewhere.
|
// So we don't open for read-only, and also not drop below. It is not used elsewhere.
|
||||||
|
@ -1423,6 +1429,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
|
||||||
|
|
||||||
mdb_set_compare(txn, m_txpool_meta, compare_hash32);
|
mdb_set_compare(txn, m_txpool_meta, compare_hash32);
|
||||||
mdb_set_compare(txn, m_txpool_blob, compare_hash32);
|
mdb_set_compare(txn, m_txpool_blob, compare_hash32);
|
||||||
|
mdb_set_compare(txn, m_alt_blocks, compare_hash32);
|
||||||
mdb_set_compare(txn, m_properties, compare_string);
|
mdb_set_compare(txn, m_properties, compare_string);
|
||||||
|
|
||||||
if (!(mdb_flags & MDB_RDONLY))
|
if (!(mdb_flags & MDB_RDONLY))
|
||||||
|
@ -2241,6 +2248,50 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BlockchainLMDB::for_all_alt_blocks(std::function<bool(const crypto::hash&, const alt_block_data_t&, const cryptonote::blobdata*)> f, bool include_blob) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
TXN_PREFIX_RDONLY();
|
||||||
|
RCURSOR(alt_blocks);
|
||||||
|
|
||||||
|
MDB_val k;
|
||||||
|
MDB_val v;
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
MDB_cursor_op op = MDB_FIRST;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, op);
|
||||||
|
op = MDB_NEXT;
|
||||||
|
if (result == MDB_NOTFOUND)
|
||||||
|
break;
|
||||||
|
if (result)
|
||||||
|
throw0(DB_ERROR(lmdb_error("Failed to enumerate alt blocks: ", result).c_str()));
|
||||||
|
const crypto::hash &blkid = *(const crypto::hash*)k.mv_data;
|
||||||
|
if (v.mv_size < sizeof(alt_block_data_t))
|
||||||
|
throw0(DB_ERROR("alt_blocks record is too small"));
|
||||||
|
const alt_block_data_t *data = (const alt_block_data_t*)v.mv_data;
|
||||||
|
const cryptonote::blobdata *passed_bd = NULL;
|
||||||
|
cryptonote::blobdata bd;
|
||||||
|
if (include_blob)
|
||||||
|
{
|
||||||
|
bd.assign(reinterpret_cast<const char*>(v.mv_data) + sizeof(alt_block_data_t), v.mv_size - sizeof(alt_block_data_t));
|
||||||
|
passed_bd = &bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f(blkid, *data, passed_bd)) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TXN_POSTFIX_RDONLY();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const
|
bool BlockchainLMDB::block_exists(const crypto::hash& h, uint64_t *height) const
|
||||||
{
|
{
|
||||||
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
@ -4062,6 +4113,110 @@ uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlockchainLMDB::add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
mdb_txn_cursors *m_cursors = &m_wcursors;
|
||||||
|
|
||||||
|
CURSOR(alt_blocks)
|
||||||
|
|
||||||
|
MDB_val k = {sizeof(blkid), (void *)&blkid};
|
||||||
|
const size_t val_size = sizeof(alt_block_data_t) + blob.size();
|
||||||
|
std::unique_ptr<char[]> val(new char[val_size]);
|
||||||
|
memcpy(val.get(), &data, sizeof(alt_block_data_t));
|
||||||
|
memcpy(val.get() + sizeof(alt_block_data_t), blob.data(), blob.size());
|
||||||
|
MDB_val v = {val_size, (void *)val.get()};
|
||||||
|
if (auto result = mdb_cursor_put(m_cur_alt_blocks, &k, &v, MDB_NODUPDATA)) {
|
||||||
|
if (result == MDB_KEYEXIST)
|
||||||
|
throw1(DB_ERROR("Attempting to add alternate block that's already in the db"));
|
||||||
|
else
|
||||||
|
throw1(DB_ERROR(lmdb_error("Error adding alternate block to db transaction: ", result).c_str()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockchainLMDB::get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB:: " << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
TXN_PREFIX_RDONLY();
|
||||||
|
RCURSOR(alt_blocks);
|
||||||
|
|
||||||
|
MDB_val_set(k, blkid);
|
||||||
|
MDB_val v;
|
||||||
|
int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET);
|
||||||
|
if (result == MDB_NOTFOUND)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str()));
|
||||||
|
if (v.mv_size < sizeof(alt_block_data_t))
|
||||||
|
throw0(DB_ERROR("Record size is less than expected"));
|
||||||
|
|
||||||
|
const alt_block_data_t *ptr = (const alt_block_data_t*)v.mv_data;
|
||||||
|
if (data)
|
||||||
|
*data = *ptr;
|
||||||
|
if (blob)
|
||||||
|
blob->assign((const char*)(ptr + 1), v.mv_size - sizeof(alt_block_data_t));
|
||||||
|
|
||||||
|
TXN_POSTFIX_RDONLY();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockchainLMDB::remove_alt_block(const crypto::hash &blkid)
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
mdb_txn_cursors *m_cursors = &m_wcursors;
|
||||||
|
|
||||||
|
CURSOR(alt_blocks)
|
||||||
|
|
||||||
|
MDB_val k = {sizeof(blkid), (void *)&blkid};
|
||||||
|
MDB_val v;
|
||||||
|
int result = mdb_cursor_get(m_cur_alt_blocks, &k, &v, MDB_SET);
|
||||||
|
if (result)
|
||||||
|
throw0(DB_ERROR(lmdb_error("Error locating alternate block " + epee::string_tools::pod_to_hex(blkid) + " in the db: ", result).c_str()));
|
||||||
|
result = mdb_cursor_del(m_cur_alt_blocks, 0);
|
||||||
|
if (result)
|
||||||
|
throw0(DB_ERROR(lmdb_error("Error deleting alternate block " + epee::string_tools::pod_to_hex(blkid) + " from the db: ", result).c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t BlockchainLMDB::get_alt_block_count()
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB:: " << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
TXN_PREFIX_RDONLY();
|
||||||
|
RCURSOR(alt_blocks);
|
||||||
|
|
||||||
|
MDB_stat db_stats;
|
||||||
|
int result = mdb_stat(m_txn, m_alt_blocks, &db_stats);
|
||||||
|
uint64_t count = 0;
|
||||||
|
if (result != MDB_NOTFOUND)
|
||||||
|
{
|
||||||
|
if (result)
|
||||||
|
throw0(DB_ERROR(lmdb_error("Failed to query m_alt_blocks: ", result).c_str()));
|
||||||
|
count = db_stats.ms_entries;
|
||||||
|
}
|
||||||
|
TXN_POSTFIX_RDONLY();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockchainLMDB::drop_alt_blocks()
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
TXN_PREFIX(0);
|
||||||
|
|
||||||
|
auto result = mdb_drop(*txn_ptr, m_alt_blocks, 0);
|
||||||
|
if (result)
|
||||||
|
throw1(DB_ERROR(lmdb_error("Error dropping alternative blocks: ", result).c_str()));
|
||||||
|
|
||||||
|
TXN_POSTFIX_SUCCESS();
|
||||||
|
}
|
||||||
|
|
||||||
bool BlockchainLMDB::is_read_only() const
|
bool BlockchainLMDB::is_read_only() const
|
||||||
{
|
{
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
|
|
|
@ -67,6 +67,8 @@ typedef struct mdb_txn_cursors
|
||||||
MDB_cursor *m_txc_txpool_meta;
|
MDB_cursor *m_txc_txpool_meta;
|
||||||
MDB_cursor *m_txc_txpool_blob;
|
MDB_cursor *m_txc_txpool_blob;
|
||||||
|
|
||||||
|
MDB_cursor *m_txc_alt_blocks;
|
||||||
|
|
||||||
MDB_cursor *m_txc_hf_versions;
|
MDB_cursor *m_txc_hf_versions;
|
||||||
|
|
||||||
MDB_cursor *m_txc_properties;
|
MDB_cursor *m_txc_properties;
|
||||||
|
@ -87,6 +89,7 @@ typedef struct mdb_txn_cursors
|
||||||
#define m_cur_spent_keys m_cursors->m_txc_spent_keys
|
#define m_cur_spent_keys m_cursors->m_txc_spent_keys
|
||||||
#define m_cur_txpool_meta m_cursors->m_txc_txpool_meta
|
#define m_cur_txpool_meta m_cursors->m_txc_txpool_meta
|
||||||
#define m_cur_txpool_blob m_cursors->m_txc_txpool_blob
|
#define m_cur_txpool_blob m_cursors->m_txc_txpool_blob
|
||||||
|
#define m_cur_alt_blocks m_cursors->m_txc_alt_blocks
|
||||||
#define m_cur_hf_versions m_cursors->m_txc_hf_versions
|
#define m_cur_hf_versions m_cursors->m_txc_hf_versions
|
||||||
#define m_cur_properties m_cursors->m_txc_properties
|
#define m_cur_properties m_cursors->m_txc_properties
|
||||||
|
|
||||||
|
@ -108,6 +111,7 @@ typedef struct mdb_rflags
|
||||||
bool m_rf_spent_keys;
|
bool m_rf_spent_keys;
|
||||||
bool m_rf_txpool_meta;
|
bool m_rf_txpool_meta;
|
||||||
bool m_rf_txpool_blob;
|
bool m_rf_txpool_blob;
|
||||||
|
bool m_rf_alt_blocks;
|
||||||
bool m_rf_hf_versions;
|
bool m_rf_hf_versions;
|
||||||
bool m_rf_properties;
|
bool m_rf_properties;
|
||||||
} mdb_rflags;
|
} mdb_rflags;
|
||||||
|
@ -288,6 +292,12 @@ public:
|
||||||
virtual bool update_pruning();
|
virtual bool update_pruning();
|
||||||
virtual bool check_pruning();
|
virtual bool check_pruning();
|
||||||
|
|
||||||
|
virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob);
|
||||||
|
virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob);
|
||||||
|
virtual void remove_alt_block(const crypto::hash &blkid);
|
||||||
|
virtual uint64_t get_alt_block_count();
|
||||||
|
virtual void drop_alt_blocks();
|
||||||
|
|
||||||
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const;
|
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const txpool_tx_meta_t&, const cryptonote::blobdata*)> f, bool include_blob = false, bool include_unrelayed_txes = true) const;
|
||||||
|
|
||||||
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
|
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
|
||||||
|
@ -295,6 +305,7 @@ public:
|
||||||
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const;
|
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>, bool pruned) const;
|
||||||
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const;
|
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, uint64_t height, size_t tx_idx)> f) const;
|
||||||
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const;
|
virtual bool for_all_outputs(uint64_t amount, const std::function<bool(uint64_t height)> &f) const;
|
||||||
|
virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const;
|
||||||
|
|
||||||
virtual uint64_t add_block( const std::pair<block, blobdata>& blk
|
virtual uint64_t add_block( const std::pair<block, blobdata>& blk
|
||||||
, size_t block_weight
|
, size_t block_weight
|
||||||
|
@ -452,6 +463,8 @@ private:
|
||||||
MDB_dbi m_txpool_meta;
|
MDB_dbi m_txpool_meta;
|
||||||
MDB_dbi m_txpool_blob;
|
MDB_dbi m_txpool_blob;
|
||||||
|
|
||||||
|
MDB_dbi m_alt_blocks;
|
||||||
|
|
||||||
MDB_dbi m_hf_starting_heights;
|
MDB_dbi m_hf_starting_heights;
|
||||||
MDB_dbi m_hf_versions;
|
MDB_dbi m_hf_versions;
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,13 @@ public:
|
||||||
|
|
||||||
virtual uint64_t get_max_block_size() override { return 100000000; }
|
virtual uint64_t get_max_block_size() override { return 100000000; }
|
||||||
virtual void add_max_block_size(uint64_t sz) override { }
|
virtual void add_max_block_size(uint64_t sz) override { }
|
||||||
|
|
||||||
|
virtual void add_alt_block(const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata &blob) override {}
|
||||||
|
virtual bool get_alt_block(const crypto::hash &blkid, alt_block_data_t *data, cryptonote::blobdata *blob) override { return false; }
|
||||||
|
virtual void remove_alt_block(const crypto::hash &blkid) override {}
|
||||||
|
virtual uint64_t get_alt_block_count() override { return 0; }
|
||||||
|
virtual void drop_alt_blocks() override {}
|
||||||
|
virtual bool for_all_alt_blocks(std::function<bool(const crypto::hash &blkid, const alt_block_data_t &data, const cryptonote::blobdata *blob)> f, bool include_blob = false) const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -726,9 +726,9 @@ bool Blockchain::reset_and_set_genesis_block(const block& b)
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||||
m_timestamps_and_difficulties_height = 0;
|
m_timestamps_and_difficulties_height = 0;
|
||||||
m_alternative_chains.clear();
|
|
||||||
invalidate_block_template_cache();
|
invalidate_block_template_cache();
|
||||||
m_db->reset();
|
m_db->reset();
|
||||||
|
m_db->drop_alt_blocks();
|
||||||
m_hardfork->init();
|
m_hardfork->init();
|
||||||
|
|
||||||
db_wtxn_guard wtxn_guard(m_db);
|
db_wtxn_guard wtxn_guard(m_db);
|
||||||
|
@ -853,10 +853,15 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk, bool *orph
|
||||||
// try to find block in alternative chain
|
// try to find block in alternative chain
|
||||||
catch (const BLOCK_DNE& e)
|
catch (const BLOCK_DNE& e)
|
||||||
{
|
{
|
||||||
blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h);
|
alt_block_data_t data;
|
||||||
if (m_alternative_chains.end() != it_alt)
|
cryptonote::blobdata blob;
|
||||||
|
if (m_db->get_alt_block(h, &data, &blob))
|
||||||
{
|
{
|
||||||
blk = it_alt->second.bl;
|
if (!cryptonote::parse_and_validate_block_from_blob(blob, blk))
|
||||||
|
{
|
||||||
|
MERROR("Found block " << h << " in alt chain, but failed to parse it");
|
||||||
|
throw std::runtime_error("Found block in alt chain, but failed to parse it");
|
||||||
|
}
|
||||||
if (orphan)
|
if (orphan)
|
||||||
*orphan = true;
|
*orphan = true;
|
||||||
return true;
|
return true;
|
||||||
|
@ -1014,7 +1019,7 @@ bool Blockchain::rollback_blockchain_switching(std::list<block>& original_chain,
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// This function attempts to switch to an alternate chain, returning
|
// This function attempts to switch to an alternate chain, returning
|
||||||
// boolean based on success therein.
|
// boolean based on success therein.
|
||||||
bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain)
|
bool Blockchain::switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain)
|
||||||
{
|
{
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||||
|
@ -1025,7 +1030,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
||||||
CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
|
CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed");
|
||||||
|
|
||||||
// verify that main chain has front of alt chain's parent block
|
// verify that main chain has front of alt chain's parent block
|
||||||
if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
|
if (!m_db->block_exists(alt_chain.front().bl.prev_id))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Attempting to move to an alternate chain, but it doesn't appear to connect to the main chain!");
|
LOG_ERROR("Attempting to move to an alternate chain, but it doesn't appear to connect to the main chain!");
|
||||||
return false;
|
return false;
|
||||||
|
@ -1034,7 +1039,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
||||||
// pop blocks from the blockchain until the top block is the parent
|
// pop blocks from the blockchain until the top block is the parent
|
||||||
// of the front block of the alt chain.
|
// of the front block of the alt chain.
|
||||||
std::list<block> disconnected_chain;
|
std::list<block> disconnected_chain;
|
||||||
while (m_db->top_block_hash() != alt_chain.front()->second.bl.prev_id)
|
while (m_db->top_block_hash() != alt_chain.front().bl.prev_id)
|
||||||
{
|
{
|
||||||
block b = pop_block_from_blockchain();
|
block b = pop_block_from_blockchain();
|
||||||
disconnected_chain.push_front(b);
|
disconnected_chain.push_front(b);
|
||||||
|
@ -1045,11 +1050,11 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
||||||
//connecting new alternative chain
|
//connecting new alternative chain
|
||||||
for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
|
for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++)
|
||||||
{
|
{
|
||||||
auto ch_ent = *alt_ch_iter;
|
const auto &bei = *alt_ch_iter;
|
||||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||||
|
|
||||||
// add block to main chain
|
// add block to main chain
|
||||||
bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc);
|
bool r = handle_block_to_main_chain(bei.bl, bvc);
|
||||||
|
|
||||||
// if adding block to main chain failed, rollback to previous state and
|
// if adding block to main chain failed, rollback to previous state and
|
||||||
// return false
|
// return false
|
||||||
|
@ -1065,14 +1070,18 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
||||||
// FIXME: Why do we keep invalid blocks around? Possibly in case we hear
|
// FIXME: Why do we keep invalid blocks around? Possibly in case we hear
|
||||||
// about them again so we can immediately dismiss them, but needs some
|
// about them again so we can immediately dismiss them, but needs some
|
||||||
// looking into.
|
// looking into.
|
||||||
add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl));
|
const crypto::hash blkid = cryptonote::get_block_hash(bei.bl);
|
||||||
MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl));
|
add_block_as_invalid(bei, blkid);
|
||||||
m_alternative_chains.erase(*alt_ch_iter++);
|
MERROR("The block was inserted as invalid while connecting new alternative chain, block_id: " << blkid);
|
||||||
|
m_db->remove_alt_block(blkid);
|
||||||
|
alt_ch_iter++;
|
||||||
|
|
||||||
for(auto alt_ch_to_orph_iter = alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); )
|
for(auto alt_ch_to_orph_iter = alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); )
|
||||||
{
|
{
|
||||||
add_block_as_invalid((*alt_ch_to_orph_iter)->second, (*alt_ch_to_orph_iter)->first);
|
const auto &bei = *alt_ch_to_orph_iter++;
|
||||||
m_alternative_chains.erase(*alt_ch_to_orph_iter++);
|
const crypto::hash blkid = cryptonote::get_block_hash(bei.bl);
|
||||||
|
add_block_as_invalid(bei, blkid);
|
||||||
|
m_db->remove_alt_block(blkid);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1097,9 +1106,9 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
||||||
}
|
}
|
||||||
|
|
||||||
//removing alt_chain entries from alternative chains container
|
//removing alt_chain entries from alternative chains container
|
||||||
for (auto ch_ent: alt_chain)
|
for (const auto &bei: alt_chain)
|
||||||
{
|
{
|
||||||
m_alternative_chains.erase(ch_ent);
|
m_db->remove_alt_block(cryptonote::get_block_hash(bei.bl));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hardfork->reorganize_from_chain_height(split_height);
|
m_hardfork->reorganize_from_chain_height(split_height);
|
||||||
|
@ -1115,7 +1124,7 @@ bool Blockchain::switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// This function calculates the difficulty target for the block being added to
|
// This function calculates the difficulty target for the block being added to
|
||||||
// an alternate chain.
|
// an alternate chain.
|
||||||
difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const
|
difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const
|
||||||
{
|
{
|
||||||
if (m_fixed_difficulty)
|
if (m_fixed_difficulty)
|
||||||
{
|
{
|
||||||
|
@ -1133,7 +1142,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||||
|
|
||||||
// Figure out start and stop offsets for main chain blocks
|
// Figure out start and stop offsets for main chain blocks
|
||||||
size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height;
|
size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front().height : bei.height;
|
||||||
size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
|
size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast<size_t>(DIFFICULTY_BLOCKS_COUNT), alt_chain.size());
|
||||||
main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
|
main_chain_count = std::min(main_chain_count, main_chain_stop_offset);
|
||||||
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
|
size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count;
|
||||||
|
@ -1151,10 +1160,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||||
// make sure we haven't accidentally grabbed too many blocks...maybe don't need this check?
|
// make sure we haven't accidentally grabbed too many blocks...maybe don't need this check?
|
||||||
CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT);
|
CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT);
|
||||||
|
|
||||||
for (auto it : alt_chain)
|
for (const auto &bei : alt_chain)
|
||||||
{
|
{
|
||||||
timestamps.push_back(it->second.bl.timestamp);
|
timestamps.push_back(bei.bl.timestamp);
|
||||||
cumulative_difficulties.push_back(it->second.cumulative_difficulty);
|
cumulative_difficulties.push_back(bei.cumulative_difficulty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if the alt chain is long enough for the difficulty calc, grab difficulties
|
// if the alt chain is long enough for the difficulty calc, grab difficulties
|
||||||
|
@ -1166,10 +1175,10 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
size_t max_i = timestamps.size()-1;
|
size_t max_i = timestamps.size()-1;
|
||||||
// get difficulties and timestamps from most recent blocks in alt chain
|
// get difficulties and timestamps from most recent blocks in alt chain
|
||||||
for(auto it: boost::adaptors::reverse(alt_chain))
|
for (const auto bei: boost::adaptors::reverse(alt_chain))
|
||||||
{
|
{
|
||||||
timestamps[max_i - count] = it->second.bl.timestamp;
|
timestamps[max_i - count] = bei.bl.timestamp;
|
||||||
cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty;
|
cumulative_difficulties[max_i - count] = bei.cumulative_difficulty;
|
||||||
count++;
|
count++;
|
||||||
if(count >= DIFFICULTY_BLOCKS_COUNT)
|
if(count >= DIFFICULTY_BLOCKS_COUNT)
|
||||||
break;
|
break;
|
||||||
|
@ -1390,16 +1399,17 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||||
//build alternative subchain, front -> mainchain, back -> alternative head
|
//build alternative subchain, front -> mainchain, back -> alternative head
|
||||||
//block is not related with head of main chain
|
//block is not related with head of main chain
|
||||||
//first of all - look in alternative chains container
|
//first of all - look in alternative chains container
|
||||||
auto it_prev = m_alternative_chains.find(*from_block);
|
alt_block_data_t prev_data;
|
||||||
|
bool parent_in_alt = m_db->get_alt_block(*from_block, &prev_data, NULL);
|
||||||
bool parent_in_main = m_db->block_exists(*from_block);
|
bool parent_in_main = m_db->block_exists(*from_block);
|
||||||
if(it_prev == m_alternative_chains.end() && !parent_in_main)
|
if (!parent_in_alt && !parent_in_main)
|
||||||
{
|
{
|
||||||
MERROR("Unknown from block");
|
MERROR("Unknown from block");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//we have new block in alternative chain
|
//we have new block in alternative chain
|
||||||
std::list<blocks_ext_by_hash::const_iterator> alt_chain;
|
std::list<block_extended_info> alt_chain;
|
||||||
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
block_verification_context bvc = boost::value_initialized<block_verification_context>();
|
||||||
std::vector<uint64_t> timestamps;
|
std::vector<uint64_t> timestamps;
|
||||||
if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
|
if (!build_alt_chain(*from_block, alt_chain, timestamps, bvc))
|
||||||
|
@ -1414,7 +1424,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
height = alt_chain.back()->second.height + 1;
|
height = alt_chain.back().height + 1;
|
||||||
}
|
}
|
||||||
b.major_version = m_hardfork->get_ideal_version(height);
|
b.major_version = m_hardfork->get_ideal_version(height);
|
||||||
b.minor_version = m_hardfork->get_ideal_version();
|
b.minor_version = m_hardfork->get_ideal_version();
|
||||||
|
@ -1429,14 +1439,14 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
median_weight = it_prev->second.block_cumulative_weight - it_prev->second.block_cumulative_weight / 20;
|
median_weight = prev_data.cumulative_weight - prev_data.cumulative_weight / 20;
|
||||||
already_generated_coins = alt_chain.back()->second.already_generated_coins;
|
already_generated_coins = alt_chain.back().already_generated_coins;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: consider moving away from block_extended_info at some point
|
// FIXME: consider moving away from block_extended_info at some point
|
||||||
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
||||||
bei.bl = b;
|
bei.bl = b;
|
||||||
bei.height = alt_chain.size() ? it_prev->second.height + 1 : m_db->get_block_height(*from_block) + 1;
|
bei.height = alt_chain.size() ? prev_data.height + 1 : m_db->get_block_height(*from_block) + 1;
|
||||||
|
|
||||||
diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei);
|
diffic = get_next_difficulty_for_alternative_chain(alt_chain, bei);
|
||||||
}
|
}
|
||||||
|
@ -1615,16 +1625,25 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const
|
bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const
|
||||||
{
|
{
|
||||||
//build alternative subchain, front -> mainchain, back -> alternative head
|
//build alternative subchain, front -> mainchain, back -> alternative head
|
||||||
blocks_ext_by_hash::const_iterator alt_it = m_alternative_chains.find(prev_id);
|
cryptonote::alt_block_data_t data;
|
||||||
|
cryptonote::blobdata blob;
|
||||||
|
bool found = m_db->get_alt_block(prev_id, &data, &blob);
|
||||||
timestamps.clear();
|
timestamps.clear();
|
||||||
while(alt_it != m_alternative_chains.end())
|
while(found)
|
||||||
{
|
{
|
||||||
alt_chain.push_front(alt_it);
|
block_extended_info bei;
|
||||||
timestamps.push_back(alt_it->second.bl.timestamp);
|
CHECK_AND_ASSERT_MES(cryptonote::parse_and_validate_block_from_blob(blob, bei.bl), false, "Failed to parse alt block");
|
||||||
alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id);
|
bei.height = data.height;
|
||||||
|
bei.block_cumulative_weight = data.cumulative_weight;
|
||||||
|
bei.cumulative_difficulty = data.cumulative_difficulty_high;
|
||||||
|
bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low;
|
||||||
|
bei.already_generated_coins = data.already_generated_coins;
|
||||||
|
timestamps.push_back(bei.bl.timestamp);
|
||||||
|
alt_chain.push_front(std::move(bei));
|
||||||
|
found = m_db->get_alt_block(bei.bl.prev_id, &data, &blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if block to be added connects to known blocks that aren't part of the
|
// if block to be added connects to known blocks that aren't part of the
|
||||||
|
@ -1632,20 +1651,20 @@ bool Blockchain::build_alt_chain(const crypto::hash &prev_id, std::list<blocks_e
|
||||||
if(!alt_chain.empty())
|
if(!alt_chain.empty())
|
||||||
{
|
{
|
||||||
// make sure alt chain doesn't somehow start past the end of the main chain
|
// make sure alt chain doesn't somehow start past the end of the main chain
|
||||||
CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front()->second.height, false, "main blockchain wrong height");
|
CHECK_AND_ASSERT_MES(m_db->height() > alt_chain.front().height, false, "main blockchain wrong height");
|
||||||
|
|
||||||
// make sure that the blockchain contains the block that should connect
|
// make sure that the blockchain contains the block that should connect
|
||||||
// this alternate chain with it.
|
// this alternate chain with it.
|
||||||
if (!m_db->block_exists(alt_chain.front()->second.bl.prev_id))
|
if (!m_db->block_exists(alt_chain.front().bl.prev_id))
|
||||||
{
|
{
|
||||||
MERROR("alternate chain does not appear to connect to main chain...");
|
MERROR("alternate chain does not appear to connect to main chain...");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure block connects correctly to the main chain
|
// make sure block connects correctly to the main chain
|
||||||
auto h = m_db->get_block_hash_from_height(alt_chain.front()->second.height - 1);
|
auto h = m_db->get_block_hash_from_height(alt_chain.front().height - 1);
|
||||||
CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain has wrong connection to main chain");
|
CHECK_AND_ASSERT_MES(h == alt_chain.front().bl.prev_id, false, "alternative chain has wrong connection to main chain");
|
||||||
complete_timestamps_vector(m_db->get_block_height(alt_chain.front()->second.bl.prev_id), timestamps);
|
complete_timestamps_vector(m_db->get_block_height(alt_chain.front().bl.prev_id), timestamps);
|
||||||
}
|
}
|
||||||
// if block not associated with known alternate chain
|
// if block not associated with known alternate chain
|
||||||
else
|
else
|
||||||
|
@ -1700,12 +1719,13 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||||
|
|
||||||
//block is not related with head of main chain
|
//block is not related with head of main chain
|
||||||
//first of all - look in alternative chains container
|
//first of all - look in alternative chains container
|
||||||
auto it_prev = m_alternative_chains.find(b.prev_id);
|
alt_block_data_t prev_data;
|
||||||
|
bool parent_in_alt = m_db->get_alt_block(b.prev_id, &prev_data, NULL);
|
||||||
bool parent_in_main = m_db->block_exists(b.prev_id);
|
bool parent_in_main = m_db->block_exists(b.prev_id);
|
||||||
if(it_prev != m_alternative_chains.end() || parent_in_main)
|
if (parent_in_alt || parent_in_main)
|
||||||
{
|
{
|
||||||
//we have new block in alternative chain
|
//we have new block in alternative chain
|
||||||
std::list<blocks_ext_by_hash::const_iterator> alt_chain;
|
std::list<block_extended_info> alt_chain;
|
||||||
std::vector<uint64_t> timestamps;
|
std::vector<uint64_t> timestamps;
|
||||||
if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc))
|
if (!build_alt_chain(b.prev_id, alt_chain, timestamps, bvc))
|
||||||
return false;
|
return false;
|
||||||
|
@ -1713,10 +1733,10 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||||
// FIXME: consider moving away from block_extended_info at some point
|
// FIXME: consider moving away from block_extended_info at some point
|
||||||
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
block_extended_info bei = boost::value_initialized<block_extended_info>();
|
||||||
bei.bl = b;
|
bei.bl = b;
|
||||||
const uint64_t prev_height = alt_chain.size() ? it_prev->second.height : m_db->get_block_height(b.prev_id);
|
const uint64_t prev_height = alt_chain.size() ? prev_data.height : m_db->get_block_height(b.prev_id);
|
||||||
bei.height = prev_height + 1;
|
bei.height = prev_height + 1;
|
||||||
uint64_t block_reward = get_outs_money_amount(b.miner_tx);
|
uint64_t block_reward = get_outs_money_amount(b.miner_tx);
|
||||||
bei.already_generated_coins = block_reward + (alt_chain.size() ? it_prev->second.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
|
bei.already_generated_coins = block_reward + (alt_chain.size() ? prev_data.already_generated_coins : m_db->get_block_already_generated_coins(prev_height));
|
||||||
|
|
||||||
// verify that the block's timestamp is within the acceptable range
|
// verify that the block's timestamp is within the acceptable range
|
||||||
// (not earlier than the median of the last X blocks)
|
// (not earlier than the median of the last X blocks)
|
||||||
|
@ -1760,7 +1780,8 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||||
difficulty_type main_chain_cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
|
difficulty_type main_chain_cumulative_difficulty = m_db->get_block_cumulative_difficulty(m_db->height() - 1);
|
||||||
if (alt_chain.size())
|
if (alt_chain.size())
|
||||||
{
|
{
|
||||||
bei.cumulative_difficulty = it_prev->second.cumulative_difficulty;
|
bei.cumulative_difficulty = prev_data.cumulative_difficulty_high;
|
||||||
|
bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + prev_data.cumulative_difficulty_low;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1771,15 +1792,21 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||||
|
|
||||||
// add block to alternate blocks storage,
|
// add block to alternate blocks storage,
|
||||||
// as well as the current "alt chain" container
|
// as well as the current "alt chain" container
|
||||||
auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei));
|
CHECK_AND_ASSERT_MES(!m_db->get_alt_block(id, NULL, NULL), false, "insertion of new alternative block returned as it already exists");
|
||||||
CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist");
|
cryptonote::alt_block_data_t data;
|
||||||
alt_chain.push_back(i_res.first);
|
data.height = bei.height;
|
||||||
|
data.cumulative_weight = bei.block_cumulative_weight;
|
||||||
|
data.cumulative_difficulty_low = (bei.cumulative_difficulty & 0xffffffffffffffff).convert_to<uint64_t>();
|
||||||
|
data.cumulative_difficulty_high = ((bei.cumulative_difficulty >> 64) & 0xffffffffffffffff).convert_to<uint64_t>();
|
||||||
|
data.already_generated_coins = bei.already_generated_coins;
|
||||||
|
m_db->add_alt_block(id, data, cryptonote::block_to_blob(bei.bl));
|
||||||
|
alt_chain.push_back(bei);
|
||||||
|
|
||||||
// FIXME: is it even possible for a checkpoint to show up not on the main chain?
|
// FIXME: is it even possible for a checkpoint to show up not on the main chain?
|
||||||
if(is_a_checkpoint)
|
if(is_a_checkpoint)
|
||||||
{
|
{
|
||||||
//do reorganize!
|
//do reorganize!
|
||||||
MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height);
|
MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << ", checkpoint is found in alternative chain on height " << bei.height);
|
||||||
|
|
||||||
bool r = switch_to_alternative_blockchain(alt_chain, true);
|
bool r = switch_to_alternative_blockchain(alt_chain, true);
|
||||||
|
|
||||||
|
@ -1791,7 +1818,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||||
else if(main_chain_cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain
|
else if(main_chain_cumulative_difficulty < bei.cumulative_difficulty) //check if difficulty bigger then in main chain
|
||||||
{
|
{
|
||||||
//do reorganize!
|
//do reorganize!
|
||||||
MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_db->height() - 1 << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1) << std::endl << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty);
|
MGINFO_GREEN("###### REORGANIZE on height: " << alt_chain.front().height << " of " << m_db->height() - 1 << " with cum_difficulty " << m_db->get_block_cumulative_difficulty(m_db->height() - 1) << std::endl << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty);
|
||||||
|
|
||||||
bool r = switch_to_alternative_blockchain(alt_chain, false);
|
bool r = switch_to_alternative_blockchain(alt_chain, false);
|
||||||
if (r)
|
if (r)
|
||||||
|
@ -1811,7 +1838,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
|
||||||
//block orphaned
|
//block orphaned
|
||||||
bvc.m_marked_as_orphaned = true;
|
bvc.m_marked_as_orphaned = true;
|
||||||
MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height
|
MERROR_VER("Block recognized as orphaned and rejected, id = " << id << ", height " << block_height
|
||||||
<< ", parent in alt " << (it_prev != m_alternative_chains.end()) << ", parent in main " << parent_in_main
|
<< ", parent in alt " << parent_in_alt << ", parent in main " << parent_in_main
|
||||||
<< " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")");
|
<< " (parent " << b.prev_id << ", current top " << get_tail_id() << ", chain height " << get_current_blockchain_height() << ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1916,11 +1943,20 @@ bool Blockchain::get_alternative_blocks(std::vector<block>& blocks) const
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||||
|
|
||||||
blocks.reserve(m_alternative_chains.size());
|
blocks.reserve(m_db->get_alt_block_count());
|
||||||
for (const auto& alt_bl: m_alternative_chains)
|
m_db->for_all_alt_blocks([&blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) {
|
||||||
|
if (!blob)
|
||||||
{
|
{
|
||||||
blocks.push_back(alt_bl.second.bl);
|
MERROR("No blob, but blobs were requested");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
cryptonote::block bl;
|
||||||
|
if (cryptonote::parse_and_validate_block_from_blob(*blob, bl))
|
||||||
|
blocks.push_back(std::move(bl));
|
||||||
|
else
|
||||||
|
MERROR("Failed to parse block from blob");
|
||||||
|
return true;
|
||||||
|
}, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
|
@ -1928,7 +1964,7 @@ size_t Blockchain::get_alternative_blocks_count() const
|
||||||
{
|
{
|
||||||
LOG_PRINT_L3("Blockchain::" << __func__);
|
LOG_PRINT_L3("Blockchain::" << __func__);
|
||||||
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
CRITICAL_REGION_LOCAL(m_blockchain_lock);
|
||||||
return m_alternative_chains.size();
|
return m_db->get_alt_block_count();
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------
|
//------------------------------------------------------------------
|
||||||
// This function adds the output specified by <amount, i> to the result_outs container
|
// This function adds the output specified by <amount, i> to the result_outs container
|
||||||
|
@ -2416,9 +2452,9 @@ bool Blockchain::have_block(const crypto::hash& id) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_alternative_chains.count(id))
|
if(m_db->get_alt_block(id, NULL, NULL))
|
||||||
{
|
{
|
||||||
LOG_PRINT_L2("block " << id << " found in m_alternative_chains");
|
LOG_PRINT_L2("block " << id << " found in alternative chains");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4800,11 +4836,35 @@ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>>
|
||||||
{
|
{
|
||||||
std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
|
std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>> chains;
|
||||||
|
|
||||||
for (const auto &i: m_alternative_chains)
|
blocks_ext_by_hash alt_blocks;
|
||||||
|
alt_blocks.reserve(m_db->get_alt_block_count());
|
||||||
|
m_db->for_all_alt_blocks([&alt_blocks](const crypto::hash &blkid, const cryptonote::alt_block_data_t &data, const cryptonote::blobdata *blob) {
|
||||||
|
if (!blob)
|
||||||
{
|
{
|
||||||
const crypto::hash &top = i.first;
|
MERROR("No blob, but blobs were requested");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cryptonote::block bl;
|
||||||
|
block_extended_info bei;
|
||||||
|
if (cryptonote::parse_and_validate_block_from_blob(*blob, bei.bl))
|
||||||
|
{
|
||||||
|
bei.height = data.height;
|
||||||
|
bei.block_cumulative_weight = data.cumulative_weight;
|
||||||
|
bei.cumulative_difficulty = data.cumulative_difficulty_high;
|
||||||
|
bei.cumulative_difficulty = (bei.cumulative_difficulty << 64) + data.cumulative_difficulty_low;
|
||||||
|
bei.already_generated_coins = data.already_generated_coins;
|
||||||
|
alt_blocks.insert(std::make_pair(cryptonote::get_block_hash(bei.bl), std::move(bei)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MERROR("Failed to parse block from blob");
|
||||||
|
return true;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
for (const auto &i: alt_blocks)
|
||||||
|
{
|
||||||
|
const crypto::hash top = cryptonote::get_block_hash(i.second.bl);
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (const auto &j: m_alternative_chains)
|
for (const auto &j: alt_blocks)
|
||||||
{
|
{
|
||||||
if (j.second.bl.prev_id == top)
|
if (j.second.bl.prev_id == top)
|
||||||
{
|
{
|
||||||
|
@ -4818,7 +4878,7 @@ std::list<std::pair<Blockchain::block_extended_info,std::vector<crypto::hash>>>
|
||||||
auto h = i.second.bl.prev_id;
|
auto h = i.second.bl.prev_id;
|
||||||
chain.push_back(top);
|
chain.push_back(top);
|
||||||
blocks_ext_by_hash::const_iterator prev;
|
blocks_ext_by_hash::const_iterator prev;
|
||||||
while ((prev = m_alternative_chains.find(h)) != m_alternative_chains.end())
|
while ((prev = alt_blocks.find(h)) != alt_blocks.end())
|
||||||
{
|
{
|
||||||
chain.push_back(h);
|
chain.push_back(h);
|
||||||
h = prev->second.bl.prev_id;
|
h = prev->second.bl.prev_id;
|
||||||
|
|
|
@ -91,17 +91,6 @@ namespace cryptonote
|
||||||
class Blockchain
|
class Blockchain
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
|
||||||
* @brief Now-defunct (TODO: remove) struct from in-memory blockchain
|
|
||||||
*/
|
|
||||||
struct transaction_chain_entry
|
|
||||||
{
|
|
||||||
transaction tx;
|
|
||||||
uint64_t m_keeper_block_height;
|
|
||||||
size_t m_blob_size;
|
|
||||||
std::vector<uint64_t> m_global_output_indexes;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief container for passing a block and metadata about it on the blockchain
|
* @brief container for passing a block and metadata about it on the blockchain
|
||||||
*/
|
*/
|
||||||
|
@ -109,7 +98,7 @@ namespace cryptonote
|
||||||
{
|
{
|
||||||
block bl; //!< the block
|
block bl; //!< the block
|
||||||
uint64_t height; //!< the height of the block in the blockchain
|
uint64_t height; //!< the height of the block in the blockchain
|
||||||
size_t block_cumulative_weight; //!< the weight of the block
|
uint64_t block_cumulative_weight; //!< the weight of the block
|
||||||
difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block
|
difficulty_type cumulative_difficulty; //!< the accumulated difficulty after that block
|
||||||
uint64_t already_generated_coins; //!< the total coins minted after that block
|
uint64_t already_generated_coins; //!< the total coins minted after that block
|
||||||
};
|
};
|
||||||
|
@ -1011,20 +1000,12 @@ namespace cryptonote
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
|
// TODO: evaluate whether or not each of these typedefs are left over from blockchain_storage
|
||||||
typedef std::unordered_map<crypto::hash, size_t> blocks_by_id_index;
|
|
||||||
|
|
||||||
typedef std::unordered_map<crypto::hash, transaction_chain_entry> transactions_container;
|
|
||||||
|
|
||||||
typedef std::unordered_set<crypto::key_image> key_images_container;
|
typedef std::unordered_set<crypto::key_image> key_images_container;
|
||||||
|
|
||||||
typedef std::vector<block_extended_info> blocks_container;
|
typedef std::vector<block_extended_info> blocks_container;
|
||||||
|
|
||||||
typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash;
|
typedef std::unordered_map<crypto::hash, block_extended_info> blocks_ext_by_hash;
|
||||||
|
|
||||||
typedef std::unordered_map<crypto::hash, block> blocks_by_hash;
|
|
||||||
|
|
||||||
typedef std::map<uint64_t, std::vector<std::pair<crypto::hash, size_t>>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction
|
|
||||||
|
|
||||||
|
|
||||||
BlockchainDB* m_db;
|
BlockchainDB* m_db;
|
||||||
|
|
||||||
|
@ -1033,7 +1014,6 @@ namespace cryptonote
|
||||||
mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
|
mutable epee::critical_section m_blockchain_lock; // TODO: add here reader/writer lock
|
||||||
|
|
||||||
// main chain
|
// main chain
|
||||||
transactions_container m_transactions;
|
|
||||||
size_t m_current_block_cumul_weight_limit;
|
size_t m_current_block_cumul_weight_limit;
|
||||||
size_t m_current_block_cumul_weight_median;
|
size_t m_current_block_cumul_weight_median;
|
||||||
|
|
||||||
|
@ -1074,9 +1054,6 @@ namespace cryptonote
|
||||||
boost::thread_group m_async_pool;
|
boost::thread_group m_async_pool;
|
||||||
std::unique_ptr<boost::asio::io_service::work> m_async_work_idle;
|
std::unique_ptr<boost::asio::io_service::work> m_async_work_idle;
|
||||||
|
|
||||||
// all alternative chains
|
|
||||||
blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info
|
|
||||||
|
|
||||||
// some invalid blocks
|
// some invalid blocks
|
||||||
blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info
|
blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info
|
||||||
|
|
||||||
|
@ -1183,7 +1160,7 @@ namespace cryptonote
|
||||||
*
|
*
|
||||||
* @return false if the reorganization fails, otherwise true
|
* @return false if the reorganization fails, otherwise true
|
||||||
*/
|
*/
|
||||||
bool switch_to_alternative_blockchain(std::list<blocks_ext_by_hash::const_iterator>& alt_chain, bool discard_disconnected_chain);
|
bool switch_to_alternative_blockchain(std::list<block_extended_info>& alt_chain, bool discard_disconnected_chain);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief removes the most recent block from the blockchain
|
* @brief removes the most recent block from the blockchain
|
||||||
|
@ -1246,7 +1223,7 @@ namespace cryptonote
|
||||||
*
|
*
|
||||||
* @return true on success, false otherwise
|
* @return true on success, false otherwise
|
||||||
*/
|
*/
|
||||||
bool build_alt_chain(const crypto::hash &prev_id, std::list<blocks_ext_by_hash::const_iterator>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const;
|
bool build_alt_chain(const crypto::hash &prev_id, std::list<block_extended_info>& alt_chain, std::vector<uint64_t> ×tamps, block_verification_context& bvc) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief gets the difficulty requirement for a new block on an alternate chain
|
* @brief gets the difficulty requirement for a new block on an alternate chain
|
||||||
|
@ -1256,7 +1233,7 @@ namespace cryptonote
|
||||||
*
|
*
|
||||||
* @return the difficulty requirement
|
* @return the difficulty requirement
|
||||||
*/
|
*/
|
||||||
difficulty_type get_next_difficulty_for_alternative_chain(const std::list<blocks_ext_by_hash::const_iterator>& alt_chain, block_extended_info& bei) const;
|
difficulty_type get_next_difficulty_for_alternative_chain(const std::list<block_extended_info>& alt_chain, block_extended_info& bei) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief sanity checks a miner transaction before validating an entire block
|
* @brief sanity checks a miner transaction before validating an entire block
|
||||||
|
|
|
@ -208,6 +208,11 @@ namespace cryptonote
|
||||||
"is acted upon."
|
"is acted upon."
|
||||||
, ""
|
, ""
|
||||||
};
|
};
|
||||||
|
static const command_line::arg_descriptor<bool> arg_keep_alt_blocks = {
|
||||||
|
"keep-alt-blocks"
|
||||||
|
, "Keep alternative blocks on restart"
|
||||||
|
, false
|
||||||
|
};
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------------------
|
||||||
core::core(i_cryptonote_protocol* pprotocol):
|
core::core(i_cryptonote_protocol* pprotocol):
|
||||||
|
@ -325,6 +330,7 @@ namespace cryptonote
|
||||||
command_line::add_arg(desc, arg_prune_blockchain);
|
command_line::add_arg(desc, arg_prune_blockchain);
|
||||||
command_line::add_arg(desc, arg_reorg_notify);
|
command_line::add_arg(desc, arg_reorg_notify);
|
||||||
command_line::add_arg(desc, arg_block_rate_notify);
|
command_line::add_arg(desc, arg_block_rate_notify);
|
||||||
|
command_line::add_arg(desc, arg_keep_alt_blocks);
|
||||||
|
|
||||||
miner::init_options(desc);
|
miner::init_options(desc);
|
||||||
BlockchainDB::init_options(desc);
|
BlockchainDB::init_options(desc);
|
||||||
|
@ -456,6 +462,7 @@ namespace cryptonote
|
||||||
std::string check_updates_string = command_line::get_arg(vm, arg_check_updates);
|
std::string check_updates_string = command_line::get_arg(vm, arg_check_updates);
|
||||||
size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight);
|
size_t max_txpool_weight = command_line::get_arg(vm, arg_max_txpool_weight);
|
||||||
bool prune_blockchain = command_line::get_arg(vm, arg_prune_blockchain);
|
bool prune_blockchain = command_line::get_arg(vm, arg_prune_blockchain);
|
||||||
|
bool keep_alt_blocks = command_line::get_arg(vm, arg_keep_alt_blocks);
|
||||||
|
|
||||||
boost::filesystem::path folder(m_config_folder);
|
boost::filesystem::path folder(m_config_folder);
|
||||||
if (m_nettype == FAKECHAIN)
|
if (m_nettype == FAKECHAIN)
|
||||||
|
@ -671,6 +678,9 @@ namespace cryptonote
|
||||||
r = m_miner.init(vm, m_nettype);
|
r = m_miner.init(vm, m_nettype);
|
||||||
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance");
|
CHECK_AND_ASSERT_MES(r, false, "Failed to initialize miner instance");
|
||||||
|
|
||||||
|
if (!keep_alt_blocks && !m_blockchain_storage.get_db().is_read_only())
|
||||||
|
m_blockchain_storage.get_db().drop_alt_blocks();
|
||||||
|
|
||||||
if (prune_blockchain)
|
if (prune_blockchain)
|
||||||
{
|
{
|
||||||
// display a message if the blockchain is not pruned yet
|
// display a message if the blockchain is not pruned yet
|
||||||
|
|
Loading…
Reference in a new issue