Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blockchain: keep alternative blocks in LMDB #5524

Merged
merged 1 commit into from
Jul 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion src/blockchain_db/blockchain_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ struct tx_data_t
};
#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
*/
Expand Down Expand Up @@ -1528,8 +1537,45 @@ class BlockchainDB
*
* @param: sz the block size
*/

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
*
Expand Down Expand Up @@ -1619,6 +1665,23 @@ class BlockchainDB
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;

/**
* @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;


//
Expand Down
155 changes: 155 additions & 0 deletions src/blockchain_db/lmdb/db_lmdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ namespace
* txpool_meta txn hash txn metadata
* 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
* 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
Expand Down Expand Up @@ -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_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_VERSIONS = "hf_versions";

Expand Down Expand Up @@ -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_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.
// 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.
Expand All @@ -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_blob, compare_hash32);
mdb_set_compare(txn, m_alt_blocks, compare_hash32);
mdb_set_compare(txn, m_properties, compare_string);

if (!(mdb_flags & MDB_RDONLY))
Expand Down Expand Up @@ -2241,6 +2248,50 @@ bool BlockchainLMDB::for_all_txpool_txes(std::function<bool(const crypto::hash&,
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
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
Expand Down Expand Up @@ -4062,6 +4113,110 @@ uint8_t BlockchainLMDB::get_hard_fork_version(uint64_t height) const
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
{
unsigned int flags;
Expand Down
13 changes: 13 additions & 0 deletions src/blockchain_db/lmdb/db_lmdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ typedef struct mdb_txn_cursors
MDB_cursor *m_txc_txpool_meta;
MDB_cursor *m_txc_txpool_blob;

MDB_cursor *m_txc_alt_blocks;

MDB_cursor *m_txc_hf_versions;

MDB_cursor *m_txc_properties;
Expand All @@ -87,6 +89,7 @@ typedef struct mdb_txn_cursors
#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_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_properties m_cursors->m_txc_properties

Expand All @@ -108,6 +111,7 @@ typedef struct mdb_rflags
bool m_rf_spent_keys;
bool m_rf_txpool_meta;
bool m_rf_txpool_blob;
bool m_rf_alt_blocks;
bool m_rf_hf_versions;
bool m_rf_properties;
} mdb_rflags;
Expand Down Expand Up @@ -288,13 +292,20 @@ class BlockchainLMDB : public BlockchainDB
virtual bool update_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_key_images(std::function<bool(const crypto::key_image&)>) const;
virtual bool for_blocks_range(const uint64_t& h1, const uint64_t& h2, std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) 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(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
, size_t block_weight
Expand Down Expand Up @@ -452,6 +463,8 @@ class BlockchainLMDB : public BlockchainDB
MDB_dbi m_txpool_meta;
MDB_dbi m_txpool_blob;

MDB_dbi m_alt_blocks;

MDB_dbi m_hf_starting_heights;
MDB_dbi m_hf_versions;

Expand Down
7 changes: 7 additions & 0 deletions src/blockchain_db/testdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@ class BaseTestDB: public cryptonote::BlockchainDB {

virtual uint64_t get_max_block_size() override { return 100000000; }
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; }
};

}
Loading