mirror of
https://github.com/monero-project/monero.git
synced 2024-12-14 12:26:31 +02:00
Add functions to iterate through blocks, txes, outputs, key images
This commit is contained in:
parent
0a5a5e8597
commit
5f397e4412
@ -533,6 +533,126 @@ void BlockchainBDB::remove_spent_key(const crypto::key_image& k_image)
|
|||||||
throw1(DB_ERROR("Error adding removal of key image to db transaction"));
|
throw1(DB_ERROR("Error adding removal of key image to db transaction"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BlockchainBDB::for_all_key_images(std::function<bool(const crypto::key_image&)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainBDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
bdb_cur cur(DB_DEFAULT_TX, m_spent_keys);
|
||||||
|
|
||||||
|
Dbt_copy<crypto::key_image> k;
|
||||||
|
Dbt_copy<char> v;
|
||||||
|
bool ret = true;
|
||||||
|
int result;
|
||||||
|
while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
|
||||||
|
{
|
||||||
|
if (!f(k))
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result != DB_NOTFOUND)
|
||||||
|
ret = false;
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockchainBDB::for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainBDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
bdb_cur cur(DB_DEFAULT_TX, m_blocks);
|
||||||
|
|
||||||
|
Dbt_copy<uint64_t> k;
|
||||||
|
Dbt_safe v;
|
||||||
|
bool ret = true;
|
||||||
|
int result;
|
||||||
|
while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
|
||||||
|
{
|
||||||
|
uint64_t height = k - 1;
|
||||||
|
blobdata bd;
|
||||||
|
bd.assign(reinterpret_cast<char*>(v.get_data()), v.get_size());
|
||||||
|
block b;
|
||||||
|
if (!parse_and_validate_block_from_blob(bd, b))
|
||||||
|
throw0(DB_ERROR("Failed to parse block from blob retrieved from the db"));
|
||||||
|
crypto::hash hash;
|
||||||
|
if (!get_block_hash(b, hash))
|
||||||
|
throw0(DB_ERROR("Failed to get block hash from blob retrieved from the db"));
|
||||||
|
if (!f(height, hash, b))
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result != DB_NOTFOUND)
|
||||||
|
ret = false;
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockchainBDB::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainBDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
bdb_cur cur(DB_DEFAULT_TX, m_txs);
|
||||||
|
|
||||||
|
Dbt_copy<crypto::hash> k;
|
||||||
|
Dbt_safe v;
|
||||||
|
bool ret = true;
|
||||||
|
int result;
|
||||||
|
while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
|
||||||
|
{
|
||||||
|
blobdata bd;
|
||||||
|
bd.assign(reinterpret_cast<char*>(v.get_data()), v.get_size());
|
||||||
|
transaction tx;
|
||||||
|
if (!parse_and_validate_tx_from_blob(bd, tx))
|
||||||
|
throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
|
||||||
|
if (!f(k, tx))
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result != DB_NOTFOUND)
|
||||||
|
ret = false;
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockchainBDB::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainBDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
bdb_cur cur(DB_DEFAULT_TX, m_output_amounts);
|
||||||
|
|
||||||
|
Dbt_copy<uint64_t> k;
|
||||||
|
Dbt_copy<uint32_t> v;
|
||||||
|
bool ret = true;
|
||||||
|
int result;
|
||||||
|
while ((result = cur->get(&k, &v, DB_NEXT)) == 0)
|
||||||
|
{
|
||||||
|
uint32_t global_index = v - 1;
|
||||||
|
tx_out_index toi = get_output_tx_and_index_from_global(global_index);
|
||||||
|
if (!f(k, toi.first, toi.second))
|
||||||
|
{
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result != DB_NOTFOUND)
|
||||||
|
ret = false;
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
blobdata BlockchainBDB::output_to_blob(const tx_out& output) const
|
blobdata BlockchainBDB::output_to_blob(const tx_out& output) const
|
||||||
{
|
{
|
||||||
LOG_PRINT_L3("BlockchainBDB::" << __func__);
|
LOG_PRINT_L3("BlockchainBDB::" << __func__);
|
||||||
|
@ -375,6 +375,11 @@ private:
|
|||||||
|
|
||||||
void get_output_global_indices(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<uint64_t> &global_indices);
|
void get_output_global_indices(const uint64_t& amount, const std::vector<uint64_t> &offsets, std::vector<uint64_t> &global_indices);
|
||||||
|
|
||||||
|
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
|
||||||
|
virtual bool for_all_blocks(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&)>) const;
|
||||||
|
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const;
|
||||||
|
|
||||||
// Hard fork related storage
|
// Hard fork related storage
|
||||||
virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height);
|
virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height);
|
||||||
virtual uint64_t get_hard_fork_starting_height(uint8_t version) const;
|
virtual uint64_t get_hard_fork_starting_height(uint8_t version) const;
|
||||||
|
@ -493,6 +493,11 @@ public:
|
|||||||
// returns true if key image <img> is present in spent key images storage
|
// returns true if key image <img> is present in spent key images storage
|
||||||
virtual bool has_key_image(const crypto::key_image& img) const = 0;
|
virtual bool has_key_image(const crypto::key_image& img) const = 0;
|
||||||
|
|
||||||
|
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const = 0;
|
||||||
|
virtual bool for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)>) const = 0;
|
||||||
|
virtual bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const = 0;
|
||||||
|
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const = 0;
|
||||||
|
|
||||||
// Hard fork related storage
|
// Hard fork related storage
|
||||||
virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height) = 0;
|
virtual void set_hard_fork_starting_height(uint8_t version, uint64_t height) = 0;
|
||||||
virtual uint64_t get_hard_fork_starting_height(uint8_t version) const = 0;
|
virtual uint64_t get_hard_fork_starting_height(uint8_t version) const = 0;
|
||||||
|
@ -1968,6 +1968,165 @@ bool BlockchainLMDB::has_key_image(const crypto::key_image& img) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BlockchainLMDB::for_all_key_images(std::function<bool(const crypto::key_image&)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
mdb_txn_safe txn;
|
||||||
|
if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
|
||||||
|
throw0(DB_ERROR("Failed to create a transaction for the db"));
|
||||||
|
|
||||||
|
MDB_val k;
|
||||||
|
MDB_val v;
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
lmdb_cur cur(txn, m_spent_keys);
|
||||||
|
MDB_cursor_op op = MDB_FIRST;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int ret = mdb_cursor_get(cur, &k, &v, op);
|
||||||
|
op = MDB_NEXT;
|
||||||
|
if (ret == MDB_NOTFOUND)
|
||||||
|
break;
|
||||||
|
if (ret < 0)
|
||||||
|
throw0(DB_ERROR("Failed to enumerate key images"));
|
||||||
|
const crypto::key_image k_image = *(crypto::key_image*)k.mv_data;
|
||||||
|
if (!f(k_image)) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockchainLMDB::for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const cryptonote::block&)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
mdb_txn_safe txn;
|
||||||
|
if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
|
||||||
|
throw0(DB_ERROR("Failed to create a transaction for the db"));
|
||||||
|
|
||||||
|
MDB_val k;
|
||||||
|
MDB_val v;
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
lmdb_cur cur(txn, m_blocks);
|
||||||
|
MDB_cursor_op op = MDB_FIRST;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int ret = mdb_cursor_get(cur, &k, &v, op);
|
||||||
|
op = MDB_NEXT;
|
||||||
|
if (ret == MDB_NOTFOUND)
|
||||||
|
break;
|
||||||
|
if (ret)
|
||||||
|
throw0(DB_ERROR("Failed to enumerate blocks"));
|
||||||
|
uint64_t height = *(uint64_t*)k.mv_data;
|
||||||
|
blobdata bd;
|
||||||
|
bd.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size);
|
||||||
|
block b;
|
||||||
|
if (!parse_and_validate_block_from_blob(bd, b))
|
||||||
|
throw0(DB_ERROR("Failed to parse block from blob retrieved from the db"));
|
||||||
|
crypto::hash hash;
|
||||||
|
if (!get_block_hash(b, hash))
|
||||||
|
throw0(DB_ERROR("Failed to get block hash from blob retrieved from the db"));
|
||||||
|
if (!f(height, hash, b)) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockchainLMDB::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
mdb_txn_safe txn;
|
||||||
|
if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
|
||||||
|
throw0(DB_ERROR("Failed to create a transaction for the db"));
|
||||||
|
|
||||||
|
MDB_val k;
|
||||||
|
MDB_val v;
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
lmdb_cur cur(txn, m_txs);
|
||||||
|
MDB_cursor_op op = MDB_FIRST;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int ret = mdb_cursor_get(cur, &k, &v, op);
|
||||||
|
op = MDB_NEXT;
|
||||||
|
if (ret == MDB_NOTFOUND)
|
||||||
|
break;
|
||||||
|
if (ret)
|
||||||
|
throw0(DB_ERROR("Failed to enumerate transactions"));
|
||||||
|
const crypto::hash hash = *(crypto::hash*)k.mv_data;
|
||||||
|
blobdata bd;
|
||||||
|
bd.assign(reinterpret_cast<char*>(v.mv_data), v.mv_size);
|
||||||
|
transaction tx;
|
||||||
|
if (!parse_and_validate_tx_from_blob(bd, tx))
|
||||||
|
throw0(DB_ERROR("Failed to parse tx from blob retrieved from the db"));
|
||||||
|
if (!f(hash, tx)) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockchainLMDB::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const
|
||||||
|
{
|
||||||
|
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
|
||||||
|
check_open();
|
||||||
|
|
||||||
|
mdb_txn_safe txn;
|
||||||
|
if (mdb_txn_begin(m_env, NULL, MDB_RDONLY, txn))
|
||||||
|
throw0(DB_ERROR("Failed to create a transaction for the db"));
|
||||||
|
|
||||||
|
MDB_val k;
|
||||||
|
MDB_val v;
|
||||||
|
bool ret = true;
|
||||||
|
|
||||||
|
lmdb_cur cur(txn, m_output_amounts);
|
||||||
|
MDB_cursor_op op = MDB_FIRST;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int ret = mdb_cursor_get(cur, &k, &v, op);
|
||||||
|
op = MDB_NEXT;
|
||||||
|
if (ret == MDB_NOTFOUND)
|
||||||
|
break;
|
||||||
|
if (ret)
|
||||||
|
throw0(DB_ERROR("Failed to enumerate outputs"));
|
||||||
|
uint64_t amount = *(uint64_t*)k.mv_data;
|
||||||
|
uint64_t global_index = *(uint64_t*)v.mv_data;
|
||||||
|
tx_out_index toi = get_output_tx_and_index_from_global(global_index);
|
||||||
|
if (!f(amount, toi.first, toi.second)) {
|
||||||
|
ret = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.close();
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// batch_num_blocks: (optional) Used to check if resize needed before batch transaction starts.
|
// batch_num_blocks: (optional) Used to check if resize needed before batch transaction starts.
|
||||||
void BlockchainLMDB::batch_start(uint64_t batch_num_blocks)
|
void BlockchainLMDB::batch_start(uint64_t batch_num_blocks)
|
||||||
{
|
{
|
||||||
|
@ -189,6 +189,11 @@ public:
|
|||||||
|
|
||||||
virtual bool has_key_image(const crypto::key_image& img) const;
|
virtual bool has_key_image(const crypto::key_image& img) const;
|
||||||
|
|
||||||
|
virtual bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
|
||||||
|
virtual bool for_all_blocks(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&)>) const;
|
||||||
|
virtual bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const;
|
||||||
|
|
||||||
virtual uint64_t add_block( const block& blk
|
virtual uint64_t add_block( const block& blk
|
||||||
, const size_t& block_size
|
, const size_t& block_size
|
||||||
, const difficulty_type& cumulative_difficulty
|
, const difficulty_type& cumulative_difficulty
|
||||||
|
@ -3188,3 +3188,23 @@ bool Blockchain::get_hard_fork_voting_info(uint8_t version, uint32_t &window, ui
|
|||||||
{
|
{
|
||||||
return m_hardfork->get_voting_info(version, window, votes, threshold, voting);
|
return m_hardfork->get_voting_info(version, window, votes, threshold, voting);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Blockchain::for_all_key_images(std::function<bool(const crypto::key_image&)> f) const
|
||||||
|
{
|
||||||
|
return m_db->for_all_key_images(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Blockchain::for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const block&)> f) const
|
||||||
|
{
|
||||||
|
return m_db->for_all_blocks(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Blockchain::for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)> f) const
|
||||||
|
{
|
||||||
|
return m_db->for_all_transactions(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Blockchain::for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)> f) const
|
||||||
|
{
|
||||||
|
return m_db->for_all_outputs(f);;
|
||||||
|
}
|
||||||
|
@ -165,6 +165,11 @@ namespace cryptonote
|
|||||||
uint8_t get_ideal_hard_fork_version() const { return m_hardfork->get_ideal_version(); }
|
uint8_t get_ideal_hard_fork_version() const { return m_hardfork->get_ideal_version(); }
|
||||||
bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const;
|
bool get_hard_fork_voting_info(uint8_t version, uint32_t &window, uint32_t &votes, uint32_t &threshold, uint8_t &voting) const;
|
||||||
|
|
||||||
|
bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
|
||||||
|
bool for_all_blocks(std::function<bool(uint64_t, const crypto::hash&, const block&)>) const;
|
||||||
|
bool for_all_transactions(std::function<bool(const crypto::hash&, const cryptonote::transaction&)>) const;
|
||||||
|
bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)>) const;
|
||||||
|
|
||||||
BlockchainDB& get_db()
|
BlockchainDB& get_db()
|
||||||
{
|
{
|
||||||
return *m_db;
|
return *m_db;
|
||||||
|
@ -1890,3 +1890,41 @@ void blockchain_storage::set_enforce_dns_checkpoints(bool enforce_checkpoints)
|
|||||||
{
|
{
|
||||||
m_enforce_dns_checkpoints = enforce_checkpoints;
|
m_enforce_dns_checkpoints = enforce_checkpoints;
|
||||||
}
|
}
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
bool blockchain_storage::for_all_key_images(std::function<bool(const crypto::key_image&)> f) const
|
||||||
|
{
|
||||||
|
for (key_images_container::const_iterator i = m_spent_keys.begin(); i != m_spent_keys.end(); ++i) {
|
||||||
|
if (!f(*i))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
bool blockchain_storage::for_all_blocks(std::function<bool(uint64_t, const block&)> f) const
|
||||||
|
{
|
||||||
|
for (blocks_container::const_iterator i = m_blocks.begin(); i != m_blocks.end(); ++i) {
|
||||||
|
if (!f(i->height, i->bl))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
bool blockchain_storage::for_all_transactions(std::function<bool(const transaction&)> f) const
|
||||||
|
{
|
||||||
|
for (transactions_container::const_iterator i = m_transactions.begin(); i != m_transactions.end(); ++i) {
|
||||||
|
if (!f(i->second.tx))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
bool blockchain_storage::for_all_outputs(std::function<bool(uint64_t, const crypto::hash&, size_t)> f) const
|
||||||
|
{
|
||||||
|
for (outputs_container::const_iterator i = m_outputs.begin(); i != m_outputs.end(); ++i) {
|
||||||
|
for (size_t n = 0; n < i->second.size(); ++n) {
|
||||||
|
if (!f(i->first, i->second[n].first, i->second[n].second))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -185,6 +185,11 @@ namespace cryptonote
|
|||||||
difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return m_blocks[height].cumulative_difficulty; }
|
difficulty_type get_block_cumulative_difficulty(uint64_t height) const { return m_blocks[height].cumulative_difficulty; }
|
||||||
uint64_t get_block_coins_generated(uint64_t height) const { return m_blocks[height].already_generated_coins; }
|
uint64_t get_block_coins_generated(uint64_t height) const { return m_blocks[height].already_generated_coins; }
|
||||||
|
|
||||||
|
bool for_all_key_images(std::function<bool(const crypto::key_image&)>) const;
|
||||||
|
bool for_all_blocks(std::function<bool(uint64_t height, const block&)>) const;
|
||||||
|
bool for_all_transactions(std::function<bool(const transaction&)>) const;
|
||||||
|
bool for_all_outputs(std::function<bool(uint64_t amount, const crypto::hash &tx_hash, size_t tx_idx)>) const;
|
||||||
|
|
||||||
// use for testing only
|
// use for testing only
|
||||||
bool debug_pop_block_from_blockchain() { return pop_block_from_blockchain(); }
|
bool debug_pop_block_from_blockchain() { return pop_block_from_blockchain(); }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user