30 #include <boost/range/adaptor/reversed.hpp>
40 #include "berkeleydb/db_bdb.h"
43 static const char *db_types[] = {
51 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
52 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "blockchain.db"
62 for (i=0; db_types[i]; i++)
64 if (db_types[i] == db_type)
74 for (i=0; db_types[i]; i++)
91 ,
"Specify sync option, using format [safe|fast|fastest]:[sync|async]:[<nblocks_per_sync>[blocks]|<nbytes_per_sync>[bytes]]."
92 ,
"fast:async:250000000bytes"
96 ,
"Try to salvage a blockchain database if it seems corrupted"
102 if (db_type ==
"lmdb")
104 #if defined(BERKELEY_DB)
105 if (db_type ==
"berkeley")
106 return new BlockchainBDB();
118 void BlockchainDB::pop_block()
121 std::vector<transaction> txs;
129 bool miner_tx =
false;
135 LOG_PRINT_L3(
"null tx_hash_ptr - needed to compute: " << tx_hash);
139 tx_hash = *tx_hash_ptr;
142 std::vector<std::pair<crypto::hash, uint64_t>> utxos_to_remove;
145 for (
size_t i = 0; i < tx.
vin.size(); ++i)
150 add_spent_key(boost::get<txin_to_key>(tx_input).k_image);
154 const auto &txin = boost::get<txin_to_key_public>(tx_input);
155 utxos_to_remove.push_back({txin.tx_hash, txin.relative_offset});
156 add_tx_input(txin.tx_hash, txin.relative_offset, tx.
hash, i);
158 else if (tx_input.type() ==
typeid(
txin_gen))
165 LOG_PRINT_L1(
"Unsupported input type, removing key images and aborting transaction addition");
170 remove_spent_key(boost::get<txin_to_key>(tx_input).k_image);
179 uint64_t tx_id = add_transaction_data(blk_hash, txp, tx_hash, tx_prunable_hash);
181 std::vector<uint64_t> amount_output_indices(tx.
vout.size());
187 amount_output_indices[i] = add_output(tx_hash, tx.
vout[i], i, tx.
unlock_time, NULL);
189 add_tx_amount_output_indices(tx_id, amount_output_indices);
193 add_transaction_data(blk_hash, txp, tx_hash, tx_prunable_hash);
200 LOG_PRINT_L1(
"Unsupported output type, reinstating UTXOs, removing key images and aborting transaction addition");
205 remove_spent_key(boost::get<txin_to_key>(tx_input).k_image);
211 for(
auto utxo: utxos_to_remove)
213 remove_chainstate_utxo(utxo.first, utxo.second);
216 const auto &txout = boost::get<txout_to_key_public>(tx.
vout[i].target);
217 add_chainstate_utxo(tx.
hash, i,
addKeys(txout.address.m_view_public_key, txout.address.m_spend_public_key) , tx.
vout[i].amount, txp.first.unlock_time, miner_tx);
218 add_addr_output(tx.
hash, i,
addKeys(txout.address.m_view_public_key, txout.address.m_spend_public_key), tx.
vout[i].amount, txp.first.unlock_time);
223 uint64_t BlockchainDB::add_block(
const std::pair<block, blobdata>& blck
224 ,
size_t block_weight
228 ,
const std::vector<std::pair<transaction, blobdata>>& txs
231 const block &blk = blck.first;
235 throw std::runtime_error(
"Inconsistent tx/hashes sizes");
240 time_blk_hash += time1;
251 for (
const std::pair<transaction, blobdata>& tx : txs)
258 time_add_transaction += time1;
262 add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, 0, blk_hash);
264 time_add_block1 += time1;
278 void BlockchainDB::pop_block(
block& blk, std::vector<transaction>& txs)
284 for (
const auto& h : boost::adaptors::reverse(blk.
tx_hashes))
288 throw DB_ERROR(
"Failed to get pruned or unpruned transaction from the db");
290 remove_transaction(h);
300 void BlockchainDB::remove_transaction(
const crypto::hash& tx_hash)
308 remove_spent_key(boost::get<txin_to_key>(tx_input).k_image);
310 else if (tx_input.type() ==
typeid(txin_to_key_public))
312 const auto &txin = boost::get<txin_to_key_public>(tx_input);
314 transaction parent_tx =
get_tx(txin.tx_hash);
315 const auto &txout = boost::get<txout_to_key_public>(parent_tx.vout[txin.relative_offset].target);
318 add_chainstate_utxo(txin.tx_hash, txin.relative_offset,
addKeys(txout.address.m_view_public_key, txout.address.m_spend_public_key), txin.amount,
get_tx_unlock_time(txin.tx_hash), reinstate_coinbase);
319 remove_tx_input(txin.tx_hash, txin.relative_offset);
327 const auto &txout = boost::get<txout_to_key_public>(tx.
vout[i].target);
329 remove_chainstate_utxo(tx.
hash, i);
330 remove_addr_output(tx_hash, i,
addKeys(txout.address.m_view_public_key, txout.address.m_spend_public_key), tx.
vout[i].amount, tx.
unlock_time);
335 remove_transaction_data(tx_hash, tx);
343 throw DB_ERROR(
"Failed to parse block from blob retrieved from the db");
353 throw DB_ERROR(
"Failed to parse block from blob retrieved from the db");
364 throw DB_ERROR(
"Failed to parse transaction from blob retrieved from the db");
375 throw DB_ERROR(
"Failed to parse transaction base from blob retrieved from the db");
402 time_add_transaction = 0;
409 <<
"*********************************"
411 <<
"num_calls: " << num_calls
413 <<
"time_blk_hash: " << time_blk_hash <<
"ms"
417 <<
"time_add_block1: " << time_add_block1 <<
"ms"
419 <<
"time_add_transaction: " << time_add_transaction <<
"ms"
423 <<
"*********************************"
431 LOG_PRINT_L1(
"Database is opened read only - skipping fixup check");
The BlockchainDB backing store interface declaration/contract.
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t &height) const =0
fetch a block blob by height
virtual void set_hard_fork(HardFork *hf)
virtual block get_block_from_height(const uint64_t &height) const
fetch a block by height
static void init_options(boost::program_options::options_description &desc)
init command line options
virtual bool get_tx_blob(const crypto::hash &h, cryptonote::blobdata &tx) const =0
fetches the transaction blob with the given hash
virtual void batch_stop()=0
ends a batch transaction
bool m_open
Whether or not the BlockchainDB is open/ready for use.
virtual block get_block(const crypto::hash &h) const
fetches the block with the given hash
void show_stats()
show profiling stats
virtual transaction get_pruned_tx(const crypto::hash &h) const
fetches the transaction base with the given hash
virtual bool is_read_only() const =0
is BlockchainDB in read-only mode?
virtual uint64_t height() const =0
fetch the current blockchain height
virtual block get_top_block() const =0
fetch the top block
void add_transaction(const crypto::hash &blk_hash, const std::pair< transaction, blobdata > &tx, const crypto::hash *tx_hash_ptr=NULL, const crypto::hash *tx_prunable_hash_ptr=NULL)
helper function for add_transactions, to add each individual transaction
bool is_open() const
Gets the current open/ready state of the BlockchainDB.
virtual void fixup()
fix up anything that may be wrong due to past bugs
virtual uint64_t get_tx_unlock_time(const crypto::hash &h) const =0
fetch a transaction's unlock time/height
virtual cryptonote::blobdata get_block_blob(const crypto::hash &h) const =0
fetches the block with the given hash
virtual bool get_pruned_tx_blob(const crypto::hash &h, cryptonote::blobdata &tx) const =0
fetches the pruned transaction blob with the given hash
void reset_stats()
reset profiling stats
virtual transaction get_tx(const crypto::hash &h) const
fetches the transaction with the given hash
uint64_t time_tx_exists
a performance metric
uint64_t time_commit1
a performance metric
A generic BlockchainDB exception.
bool add(const cryptonote::block &block, uint64_t height)
add a new block
thrown when a requested transaction does not exist
std::vector< txin_v > vin
std::vector< tx_out > vout
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
public_key addKeys(const public_key &A, const public_key &B)
Holds cryptonote related classes and helpers.
boost::multiprecision::uint128_t difficulty_type
std::string arg_db_type_description
const command_line::arg_descriptor< bool > arg_db_salvage
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
bool get_block_hash(const block &b, crypto::hash &res)
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)
BlockchainDB * new_db(const std::string &db_type)
crypto::hash get_transaction_hash(const transaction &t)
const command_line::arg_descriptor< std::string > arg_db_type
bool is_coinbase(const transaction &tx)
bool blockchain_valid_db_type(const std::string &db_type)
blobdata tx_to_blob(const transaction &tx)
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
std::string blockchain_db_types(const std::string &sep)
const command_line::arg_descriptor< std::string > arg_db_sync_mode
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
uint64_t get_tick_count()
const T & move(const T &t)
unsigned __int64 uint64_t
std::vector< crypto::hash > tx_hashes