30 #include <boost/range/adaptor/reversed.hpp> 31 #include <unordered_set> 41 #include "berkeleydb/db_bdb.h" 44 static const char *db_types[] = {
52 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY 53 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "blockchain.db" 63 for (i=0; db_types[i]; i++)
65 if (db_types[i] == db_type)
75 for (i=0; db_types[i]; i++)
92 ,
"Specify sync option, using format [safe|fast|fastest]:[sync|async]:[<nblocks_per_sync>[blocks]|<nbytes_per_sync>[bytes]]." 93 ,
"fast:async:250000000bytes" 97 ,
"Try to salvage a blockchain database if it seems corrupted" 103 ,
"Try to salvage a addr tx database if it seems corrupted" 109 if (db_type ==
"lmdb")
111 #if defined(BERKELEY_DB) 112 if (db_type ==
"berkeley")
113 return new BlockchainBDB();
126 void BlockchainDB::pop_block()
129 std::vector<transaction> txs;
137 bool miner_tx =
false;
143 LOG_PRINT_L3(
"null tx_hash_ptr - needed to compute: " << tx_hash);
147 tx_hash = *tx_hash_ptr;
150 std::vector<std::pair<crypto::hash, uint64_t>> utxos_to_remove;
152 std::unordered_set<cryptonote::account_public_address> addr_tx_addresses;
155 for (
size_t i = 0; i < tx.
vin.size(); ++i)
160 add_spent_key(boost::get<txin_to_key>(tx_input).k_image);
164 const auto &txin = boost::get<txin_to_key_public>(tx_input);
165 utxos_to_remove.push_back({txin.tx_hash, txin.relative_offset});
166 add_tx_input(txin.tx_hash, txin.relative_offset, tx.
hash, i);
170 const auto &txout = boost::get<txout_to_key_public>(parent_tx.
vout[txin.relative_offset].target);
171 if(addr_tx_addresses.find(txout.address) == addr_tx_addresses.end()){
173 addr_tx_addresses.insert(txout.address);
176 else if (tx_input.type() ==
typeid(
txin_gen))
183 LOG_PRINT_L1(
"Unsupported input type, removing key images and aborting transaction addition");
188 remove_spent_key(boost::get<txin_to_key>(tx_input).k_image);
192 const auto &txin = boost::get<txin_to_key_public>(tx_input);
193 remove_tx_input(txin.tx_hash, txin.relative_offset);
196 const auto &txout = boost::get<txout_to_key_public>(parent_tx.
vout[txin.relative_offset].target);
197 if (addr_tx_addresses.find(txout.address) != addr_tx_addresses.end()) {
199 addr_tx_addresses.erase(txout.address);
209 uint64_t tx_id = add_transaction_data(blk_hash, txp, tx_hash, tx_prunable_hash);
211 std::vector<uint64_t> amount_output_indices(tx.
vout.size());
217 amount_output_indices[i] = add_output(tx_hash, tx.
vout[i], i, tx.
unlock_time, NULL);
219 add_tx_amount_output_indices(tx_id, amount_output_indices);
223 add_transaction_data(blk_hash, txp, tx_hash, tx_prunable_hash);
230 LOG_PRINT_L1(
"Unsupported output type, reinstating UTXOs, removing key images and aborting transaction addition");
233 remove_spent_key(boost::get<txin_to_key>(tx_input).k_image);
237 const auto &txin = boost::get<txin_to_key_public>(tx_input);
238 remove_tx_input(txin.tx_hash, txin.relative_offset);
241 const auto &txout = boost::get<txout_to_key_public>(
242 parent_tx.
vout[txin.relative_offset].target);
243 if (addr_tx_addresses.find(txout.address) !=
244 addr_tx_addresses.end()) {
246 addr_tx_addresses.erase(txout.address);
253 for(
auto utxo: utxos_to_remove)
255 remove_chainstate_utxo(utxo.first, utxo.second);
258 const auto &txout = boost::get<txout_to_key_public>(tx.
vout[i].target);
259 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);
260 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);
261 if(addr_tx_addresses.find(txout.address) == addr_tx_addresses.end()){
263 addr_tx_addresses.insert(txout.address);
269 uint64_t BlockchainDB::add_block(
const std::pair<block, blobdata>& blck
270 ,
size_t block_weight
274 ,
const std::vector<std::pair<transaction, blobdata>>& txs
277 const block &blk = blck.first;
281 throw std::runtime_error(
"Inconsistent tx/hashes sizes");
286 time_blk_hash += time1;
297 for (
const std::pair<transaction, blobdata>& tx : txs)
304 time_add_transaction += time1;
308 add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, 0, blk_hash);
310 time_add_block1 += time1;
324 void BlockchainDB::pop_block(
block& blk, std::vector<transaction>& txs)
330 for (
const auto& h : boost::adaptors::reverse(blk.
tx_hashes))
334 throw DB_ERROR(
"Failed to get pruned or unpruned transaction from the db");
336 remove_transaction(h);
346 void BlockchainDB::remove_transaction(
const crypto::hash& tx_hash)
351 std::unordered_set<cryptonote::account_public_address> addr_tx_addresses;
357 remove_spent_key(boost::get<txin_to_key>(tx_input).k_image);
359 else if (tx_input.type() ==
typeid(txin_to_key_public))
361 const auto &txin = boost::get<txin_to_key_public>(tx_input);
363 transaction parent_tx =
get_tx(txin.tx_hash);
364 const auto &txout = boost::get<txout_to_key_public>(parent_tx.vout[txin.relative_offset].target);
367 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);
368 remove_tx_input(txin.tx_hash, txin.relative_offset);
371 if(addr_tx_addresses.find(txout.address) == addr_tx_addresses.end()){
372 remove_addr_tx(txin.tx_hash,
addKeys(txout.address.m_view_public_key, txout.address.m_spend_public_key));
373 addr_tx_addresses.insert(txout.address);
382 const auto &txout = boost::get<txout_to_key_public>(tx.
vout[i].target);
384 remove_chainstate_utxo(tx_hash, i);
385 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);
388 if(addr_tx_addresses.find(txout.address) == addr_tx_addresses.end()){
390 addr_tx_addresses.insert(txout.address);
396 remove_transaction_data(tx_hash, tx);
404 throw DB_ERROR(
"Failed to parse block from blob retrieved from the db");
414 throw DB_ERROR(
"Failed to parse block from blob retrieved from the db");
425 throw DB_ERROR(
"Failed to parse transaction from blob retrieved from the db");
436 throw DB_ERROR(
"Failed to parse transaction base from blob retrieved from the db");
463 time_add_transaction = 0;
470 <<
"*********************************" 472 <<
"num_calls: " << num_calls
474 <<
"time_blk_hash: " << time_blk_hash <<
"ms" 478 <<
"time_add_block1: " << time_add_block1 <<
"ms" 480 <<
"time_add_transaction: " << time_add_transaction <<
"ms" 484 <<
"*********************************" 492 LOG_PRINT_L1(
"Database is opened read only - skipping fixup check");
bool is_coinbase(const transaction &tx)
virtual bool get_tx_blob(const crypto::hash &h, cryptonote::blobdata &tx) const =0
fetches the transaction blob with the given hash
std::vector< crypto::hash > tx_hashes
uint64_t get_tick_count()
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t &height) const =0
fetch a block blob by height
boost::variant< txin_gen, txin_to_script, txin_to_scripthash, txin_to_key, txin_to_key_public > txin_v
const command_line::arg_descriptor< std::string > arg_db_sync_mode
virtual void remove_addr_tx(const crypto::hash tx_hash, const crypto::public_key &combined_key)=0
public_key addKeys(const public_key &A, const public_key &B)
std::string blockchain_db_types(const std::string &sep)
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 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
thrown when a requested transaction does not exist
std::vector< tx_out > vout
Holds cryptonote related classes and helpers.
void show_stats()
show profiling stats
bool blockchain_valid_db_type(const std::string &db_type)
std::vector< txin_v > vin
blobdata tx_to_blob(const transaction &tx)
BlockchainDB * new_db(const std::string &db_type)
virtual block get_block_from_height(const uint64_t &height) const
fetch a block by height
bool add(const cryptonote::block &block, uint64_t height)
add a new block
bool get_block_hash(const block &b, crypto::hash &res)
uint64_t time_tx_exists
a performance metric
virtual bool is_read_only() const =0
is BlockchainDB in read-only mode?
virtual transaction get_tx(const crypto::hash &h) const
fetches the transaction with the given hash
unsigned __int64 uint64_t
virtual block get_top_block() const =0
fetch the top block
virtual uint64_t height() const =0
fetch the current blockchain height
virtual void batch_stop()=0
ends a batch transaction
virtual uint64_t get_tx_unlock_time(const crypto::hash &h) const =0
fetch a transaction's unlock time/height
virtual transaction get_pruned_tx(const crypto::hash &h) const
fetches the transaction base with the given hash
virtual void add_addr_tx(const crypto::hash tx_hash, const crypto::public_key &combined_key)=0
bool parse_and_validate_tx_base_from_blob(const blobdata &tx_blob, transaction &tx)
bool m_open
Whether or not the BlockchainDB is open/ready for use.
const command_line::arg_descriptor< std::string > arg_db_type
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
void add_arg(boost::program_options::options_description &description, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg, bool unique=true)
bool is_open() const
Gets the current open/ready state of the BlockchainDB.
virtual void set_hard_fork(HardFork *hf)
A generic BlockchainDB exception.
The BlockchainDB backing store interface declaration/contract.
const T & move(const T &t)
boost::multiprecision::uint128_t difficulty_type
const command_line::arg_descriptor< bool > arg_addr_db_salvage
crypto::hash get_transaction_hash(const transaction &t)
virtual void fixup()
fix up anything that may be wrong due to past bugs
const command_line::arg_descriptor< bool > arg_db_salvage
static void init_options(boost::program_options::options_description &desc)
init command line options
std::string arg_db_type_description
uint64_t time_commit1
a performance metric
virtual block get_block(const crypto::hash &h) const
fetches the block with the given hash
virtual cryptonote::blobdata get_block_blob(const crypto::hash &h) const =0
fetches the block with the given hash
void reset_stats()
reset profiling stats
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)