2014-10-21 23:33:43 +03:00
// Copyright (c) 2014, The Monero Project
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# include "db_lmdb.h"
2014-10-23 22:37:10 +03:00
# include <boost/filesystem.hpp>
# include <memory> // std::unique_ptr
# include <cstring> // memcpy
# include "cryptonote_core/cryptonote_format_utils.h"
2014-10-28 02:45:33 +02:00
# include "crypto/crypto.h"
2015-02-12 01:55:53 +02:00
# include "profile_tools.h"
2014-10-23 22:37:10 +03:00
2014-10-29 04:25:03 +02:00
using epee : : string_tools : : pod_to_hex ;
2014-10-28 02:45:33 +02:00
namespace
{
2015-01-09 14:29:05 +02:00
template < typename T >
inline void throw0 ( const T & e )
2014-12-12 23:34:45 +02:00
{
LOG_PRINT_L0 ( e . what ( ) ) ;
throw e ;
}
2015-01-09 14:29:05 +02:00
template < typename T >
inline void throw1 ( const T & e )
2014-12-12 23:34:45 +02:00
{
LOG_PRINT_L1 ( e . what ( ) ) ;
throw e ;
}
2014-10-28 02:45:33 +02:00
// cursor needs to be closed when it goes out of scope,
// this helps if the function using it throws
struct lmdb_cur
2014-10-21 23:33:43 +03:00
{
2014-10-28 02:45:33 +02:00
lmdb_cur ( MDB_txn * txn , MDB_dbi dbi )
{
if ( mdb_cursor_open ( txn , dbi , & m_cur ) )
2014-12-12 23:34:45 +02:00
throw0 ( cryptonote : : DB_ERROR ( " Error opening db cursor " ) ) ;
2014-10-28 02:45:33 +02:00
done = false ;
}
~ lmdb_cur ( ) { close ( ) ; }
operator MDB_cursor * ( ) { return m_cur ; }
operator MDB_cursor * * ( ) { return & m_cur ; }
void close ( )
{
if ( ! done )
{
mdb_cursor_close ( m_cur ) ;
done = true ;
}
}
2014-12-07 13:23:40 +02:00
private :
2014-10-28 02:45:33 +02:00
MDB_cursor * m_cur ;
bool done ;
} ;
2014-10-21 23:33:43 +03:00
2014-12-12 15:27:05 +02:00
template < typename T >
struct MDB_val_copy : public MDB_val
{
MDB_val_copy ( const T & t ) : t_copy ( t )
{
mv_size = sizeof ( T ) ;
mv_data = & t_copy ;
}
private :
T t_copy ;
} ;
template < >
struct MDB_val_copy < cryptonote : : blobdata > : public MDB_val
{
MDB_val_copy ( const cryptonote : : blobdata & bd ) : data ( new char [ bd . size ( ) ] )
{
memcpy ( data . get ( ) , bd . data ( ) , bd . size ( ) ) ;
mv_size = bd . size ( ) ;
mv_data = data . get ( ) ;
}
private :
std : : unique_ptr < char [ ] > data ;
} ;
2014-10-29 04:25:03 +02:00
auto compare_uint64 = [ ] ( const MDB_val * a , const MDB_val * b ) {
2014-12-11 21:30:55 +02:00
const uint64_t va = * ( const uint64_t * ) a - > mv_data ;
const uint64_t vb = * ( const uint64_t * ) b - > mv_data ;
2014-10-29 04:25:03 +02:00
if ( va < vb ) return - 1 ;
else if ( va = = vb ) return 0 ;
else return 1 ;
} ;
2014-12-06 23:37:22 +02:00
const char * const LMDB_BLOCKS = " blocks " ;
const char * const LMDB_BLOCK_TIMESTAMPS = " block_timestamps " ;
const char * const LMDB_BLOCK_HEIGHTS = " block_heights " ;
const char * const LMDB_BLOCK_HASHES = " block_hashes " ;
const char * const LMDB_BLOCK_SIZES = " block_sizes " ;
const char * const LMDB_BLOCK_DIFFS = " block_diffs " ;
const char * const LMDB_BLOCK_COINS = " block_coins " ;
const char * const LMDB_TXS = " txs " ;
const char * const LMDB_TX_UNLOCKS = " tx_unlocks " ;
const char * const LMDB_TX_HEIGHTS = " tx_heights " ;
const char * const LMDB_TX_OUTPUTS = " tx_outputs " ;
const char * const LMDB_OUTPUT_TXS = " output_txs " ;
const char * const LMDB_OUTPUT_INDICES = " output_indices " ;
const char * const LMDB_OUTPUT_AMOUNTS = " output_amounts " ;
const char * const LMDB_OUTPUT_KEYS = " output_keys " ;
const char * const LMDB_OUTPUTS = " outputs " ;
const char * const LMDB_OUTPUT_GINDICES = " output_gindices " ;
const char * const LMDB_SPENT_KEYS = " spent_keys " ;
2014-10-21 23:33:43 +03:00
2014-10-28 02:45:33 +02:00
inline void lmdb_db_open ( MDB_txn * txn , const char * name , int flags , MDB_dbi & dbi , const std : : string & error_string )
{
if ( mdb_dbi_open ( txn , name , flags , & dbi ) )
2014-12-12 23:34:45 +02:00
throw0 ( cryptonote : : DB_OPEN_FAILURE ( error_string . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
}
} // anonymous namespace
namespace cryptonote
{
2015-03-03 23:09:49 +02:00
// If m_batch_active is set, a batch transaction exists beyond this class, such
// as a batch import with verification enabled, or possibly (later) a batch
// network sync.
//
// For some of the lookup methods, such as get_block_timestamp(), tx_exists(),
// and get_tx(), when m_batch_active is set, the lookup uses the batch
// transaction. This isn't only because the transaction is available, but it's
// necessary so that lookups include the database updates only present in the
// current batch write.
//
// A regular network sync without batch writes is expected to open a new read
// transaction, as those lookups are part of the validation done prior to the
// write for block and tx data, so no write transaction is open at the time.
2014-10-21 23:33:43 +03:00
void BlockchainLMDB : : add_block ( const block & blk
, const size_t & block_size
, const difficulty_type & cumulative_difficulty
, const uint64_t & coins_generated
2015-02-12 01:55:53 +02:00
, const crypto : : hash & blk_hash
2014-10-21 23:33:43 +03:00
)
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2015-02-12 01:55:53 +02:00
MDB_val_copy < crypto : : hash > val_h ( blk_hash ) ;
2014-10-23 22:37:10 +03:00
MDB_val unused ;
2014-10-28 02:45:33 +02:00
if ( mdb_get ( * m_write_txn , m_block_heights , & val_h , & unused ) = = 0 )
2014-12-12 23:34:45 +02:00
throw1 ( BLOCK_EXISTS ( " Attempting to add block that's already in the db " ) ) ;
2014-10-23 22:37:10 +03:00
if ( m_height > 0 )
{
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > parent_key ( blk . prev_id ) ;
2014-10-23 22:37:10 +03:00
MDB_val parent_h ;
2014-10-28 02:45:33 +02:00
if ( mdb_get ( * m_write_txn , m_block_heights , & parent_key , & parent_h ) )
2015-02-12 01:55:53 +02:00
{
LOG_PRINT_L3 ( " m_height: " < < m_height ) ;
LOG_PRINT_L3 ( " parent_key: " < < blk . prev_id ) ;
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to get top block hash to check for new block's parent " ) ) ;
2015-02-12 01:55:53 +02:00
}
2014-12-11 21:30:55 +02:00
uint64_t parent_height = * ( const uint64_t * ) parent_h . mv_data ;
2014-10-23 22:37:10 +03:00
if ( parent_height ! = m_height - 1 )
2014-12-12 23:34:45 +02:00
throw0 ( BLOCK_PARENT_DNE ( " Top block is not new block's parent " ) ) ;
2014-10-23 22:37:10 +03:00
}
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > key ( m_height ) ;
2014-10-23 22:37:10 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < blobdata > blob ( block_to_blob ( blk ) ) ;
2015-02-12 01:55:53 +02:00
auto res = mdb_put ( * m_write_txn , m_blocks , & key , & blob , 0 ) ;
if ( res )
throw0 ( DB_ERROR ( std : : string ( " Failed to add block blob to db transaction: " ) . append ( mdb_strerror ( res ) ) . c_str ( ) ) ) ;
2014-10-23 22:37:10 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < size_t > sz ( block_size ) ;
2014-10-23 22:37:10 +03:00
if ( mdb_put ( * m_write_txn , m_block_sizes , & key , & sz , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add block size to db transaction " ) ) ;
2014-10-23 22:37:10 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > ts ( blk . timestamp ) ;
if ( mdb_put ( * m_write_txn , m_block_timestamps , & key , & ts , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add block timestamp to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < difficulty_type > diff ( cumulative_difficulty ) ;
if ( mdb_put ( * m_write_txn , m_block_diffs , & key , & diff , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add block cumulative difficulty to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > coinsgen ( coins_generated ) ;
if ( mdb_put ( * m_write_txn , m_block_coins , & key , & coinsgen , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add block total generated coins to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_put ( * m_write_txn , m_block_heights , & val_h , & key , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add block height by hash to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_put ( * m_write_txn , m_block_hashes , & key , & val_h , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add block hash to db transaction " ) ) ;
2014-10-23 22:37:10 +03:00
2014-10-21 23:33:43 +03:00
}
2014-10-23 22:37:10 +03:00
void BlockchainLMDB : : remove_block ( )
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2014-12-13 01:20:41 +02:00
if ( m_height = = 0 )
throw0 ( BLOCK_DNE ( " Attempting to remove block from an empty blockchain " ) ) ;
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > k ( m_height - 1 ) ;
2014-10-28 02:45:33 +02:00
MDB_val h ;
if ( mdb_get ( * m_write_txn , m_block_hashes , & k , & h ) )
2014-12-12 23:34:45 +02:00
throw1 ( BLOCK_DNE ( " Attempting to remove block that's not in the db " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_blocks , & k , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of block to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_block_sizes , & k , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of block size to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_block_diffs , & k , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of block cumulative difficulty to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_block_coins , & k , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of block total generated coins to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
2014-12-11 21:36:27 +02:00
if ( mdb_del ( * m_write_txn , m_block_timestamps , & k , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of block timestamp to db transaction " ) ) ;
2014-12-11 21:36:27 +02:00
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_block_heights , & h , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of block height by hash to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_block_hashes , & k , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of block hash to db transaction " ) ) ;
2014-10-21 23:33:43 +03:00
}
2015-02-12 01:55:53 +02:00
void BlockchainLMDB : : add_transaction_data ( const crypto : : hash & blk_hash , const transaction & tx , const crypto : : hash & tx_hash )
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-24 02:47:36 +03:00
2015-02-12 01:55:53 +02:00
MDB_val_copy < crypto : : hash > val_h ( tx_hash ) ;
2014-10-24 02:47:36 +03:00
MDB_val unused ;
if ( mdb_get ( * m_write_txn , m_txs , & val_h , & unused ) = = 0 )
2014-12-12 23:34:45 +02:00
throw1 ( TX_EXISTS ( " Attempting to add transaction that's already in the db " ) ) ;
2014-10-24 02:47:36 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < blobdata > blob ( tx_to_blob ( tx ) ) ;
2014-10-24 02:47:36 +03:00
if ( mdb_put ( * m_write_txn , m_txs , & val_h , & blob , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add tx blob to db transaction " ) ) ;
2014-10-24 02:47:36 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > height ( m_height ) ;
2014-10-24 02:47:36 +03:00
if ( mdb_put ( * m_write_txn , m_tx_heights , & val_h , & height , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add tx block height to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > unlock_time ( tx . unlock_time ) ;
if ( mdb_put ( * m_write_txn , m_tx_unlocks , & val_h , & unlock_time , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add tx unlock time to db transaction " ) ) ;
2014-10-21 23:33:43 +03:00
}
2015-01-12 04:04:04 +02:00
void BlockchainLMDB : : remove_transaction_data ( const crypto : : hash & tx_hash , const transaction & tx )
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > val_h ( tx_hash ) ;
2014-10-28 02:45:33 +02:00
MDB_val unused ;
if ( mdb_get ( * m_write_txn , m_txs , & val_h , & unused ) )
2014-12-12 23:34:45 +02:00
throw1 ( TX_DNE ( " Attempting to remove transaction that isn't in the db " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_txs , & val_h , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of tx to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_tx_unlocks , & val_h , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of tx unlock time to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_tx_heights , & val_h , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of tx block height to db transaction " ) ) ;
2014-10-31 00:33:35 +02:00
2015-01-12 04:04:04 +02:00
remove_tx_outputs ( tx_hash , tx ) ;
2014-10-31 00:33:35 +02:00
2014-10-28 02:45:33 +02:00
if ( mdb_del ( * m_write_txn , m_tx_outputs , & val_h , NULL ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Failed to add removal of tx outputs to db transaction " ) ) ;
2014-10-28 02:45:33 +02:00
2014-10-21 23:33:43 +03:00
}
void BlockchainLMDB : : add_output ( const crypto : : hash & tx_hash , const tx_out & tx_output , const uint64_t & local_index )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-24 02:47:36 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > k ( m_num_outputs ) ;
MDB_val_copy < crypto : : hash > v ( tx_hash ) ;
2014-10-24 02:47:36 +03:00
if ( mdb_put ( * m_write_txn , m_output_txs , & k , & v , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add output tx hash to db transaction " ) ) ;
2014-10-24 02:47:36 +03:00
if ( mdb_put ( * m_write_txn , m_tx_outputs , & v , & k , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add tx output index to db transaction " ) ) ;
2014-10-24 02:47:36 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > val_local_index ( local_index ) ;
if ( mdb_put ( * m_write_txn , m_output_indices , & k , & val_local_index , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add tx output index to db transaction " ) ) ;
2014-10-24 02:47:36 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > val_amount ( tx_output . amount ) ;
if ( auto result = mdb_put ( * m_write_txn , m_output_amounts , & val_amount , & k , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( std : : string ( " Failed to add output amount to db transaction " ) . append ( mdb_strerror ( result ) ) . c_str ( ) ) ) ;
2014-10-24 02:47:36 +03:00
2014-10-31 23:34:15 +02:00
if ( tx_output . target . type ( ) = = typeid ( txout_to_key ) )
{
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : public_key > val_pubkey ( boost : : get < txout_to_key > ( tx_output . target ) . key ) ;
if ( mdb_put ( * m_write_txn , m_output_keys , & k , & val_pubkey , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add output pubkey to db transaction " ) ) ;
2014-10-31 23:34:15 +02:00
}
2014-10-31 00:33:35 +02:00
/****** Uncomment if ever outputs actually need to be stored in this manner
*
2014-10-28 02:45:33 +02:00
blobdata b = output_to_blob ( tx_output ) ;
2014-10-24 02:47:36 +03:00
v . mv_size = b . size ( ) ;
v . mv_data = & b ;
if ( mdb_put ( * m_write_txn , m_outputs , & k , & v , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add output to db transaction " ) ) ;
2014-10-24 02:47:36 +03:00
if ( mdb_put ( * m_write_txn , m_output_gindices , & v , & k , 0 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to add output global index to db transaction " ) ) ;
2014-10-31 00:33:35 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-10-24 02:47:36 +03:00
2014-10-29 04:25:03 +02:00
m_num_outputs + + ;
2014-10-21 23:33:43 +03:00
}
2015-01-12 04:04:04 +02:00
void BlockchainLMDB : : remove_tx_outputs ( const crypto : : hash & tx_hash , const transaction & tx )
2014-10-31 00:33:35 +02:00
{
2015-01-12 04:04:04 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-31 00:33:35 +02:00
lmdb_cur cur ( * m_write_txn , m_tx_outputs ) ;
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > k ( tx_hash ) ;
2014-10-31 00:33:35 +02:00
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
{
LOG_ERROR ( " Attempting to remove a tx's outputs, but none found. Continuing, but...be wary, because that's weird. " ) ;
}
else if ( result )
{
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to get an output " ) ) ;
2014-10-31 00:33:35 +02:00
}
else
{
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
mdb_cursor_get ( cur , & k , & v , MDB_FIRST_DUP ) ;
for ( uint64_t i = 0 ; i < num_elems ; + + i )
{
2015-01-12 04:04:04 +02:00
const tx_out tx_output = tx . vout [ i ] ;
remove_output ( * ( const uint64_t * ) v . mv_data , tx_output . amount ) ;
2014-10-31 00:33:35 +02:00
if ( i < num_elems - 1 )
{
mdb_cursor_get ( cur , & k , & v , MDB_NEXT_DUP ) ;
}
}
}
cur . close ( ) ;
}
2015-01-12 04:04:04 +02:00
// TODO: probably remove this function
2014-10-21 23:33:43 +03:00
void BlockchainLMDB : : remove_output ( const tx_out & tx_output )
2014-10-31 00:33:35 +02:00
{
2015-01-12 04:04:04 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ < < " (unused version - does nothing) " ) ;
2014-10-31 00:33:35 +02:00
return ;
}
2015-01-12 04:04:04 +02:00
void BlockchainLMDB : : remove_output ( const uint64_t & out_index , const uint64_t amount )
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-24 03:32:31 +03:00
2015-01-12 04:04:04 +02:00
MDB_val_copy < uint64_t > k ( out_index ) ;
2014-10-24 03:32:31 +03:00
2014-10-31 00:33:35 +02:00
/****** Uncomment if ever outputs actually need to be stored in this manner
2014-10-24 03:32:31 +03:00
blobdata b ;
t_serializable_object_to_blob ( tx_output , b ) ;
k . mv_size = b . size ( ) ;
k . mv_data = & b ;
if ( mdb_get ( * m_write_txn , m_output_gindices , & k , & v ) )
2014-12-12 23:34:45 +02:00
throw1 ( OUTPUT_DNE ( " Attempting to remove output that does not exist " ) ) ;
2014-10-24 03:32:31 +03:00
uint64_t gindex = * ( uint64_t * ) v . mv_data ;
auto result = mdb_del ( * m_write_txn , m_output_gindices , & k , NULL ) ;
if ( result ! = 0 & & result ! = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Error adding removal of output global index to db transaction " ) ) ;
2014-10-24 03:32:31 +03:00
2014-10-31 00:33:35 +02:00
result = mdb_del ( * m_write_txn , m_outputs , & v , NULL ) ;
if ( result ! = 0 & & result ! = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Error adding removal of output to db transaction " ) ) ;
2014-10-31 00:33:35 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-01-12 04:04:04 +02:00
auto result = mdb_del ( * m_write_txn , m_output_indices , & k , NULL ) ;
if ( result = = MDB_NOTFOUND )
{
LOG_PRINT_L0 ( " Unexpected: global output index not found in m_output_indices " ) ;
}
else if ( result )
{
throw1 ( DB_ERROR ( " Error adding removal of output tx index to db transaction " ) ) ;
}
2014-10-24 03:32:31 +03:00
2015-01-12 04:04:04 +02:00
result = mdb_del ( * m_write_txn , m_output_txs , & k , NULL ) ;
// if (result != 0 && result != MDB_NOTFOUND)
// throw1(DB_ERROR("Error adding removal of output tx hash to db transaction"));
if ( result = = MDB_NOTFOUND )
{
LOG_PRINT_L0 ( " Unexpected: global output index not found in m_output_txs " ) ;
}
else if ( result )
{
throw1 ( DB_ERROR ( " Error adding removal of output tx hash to db transaction " ) ) ;
}
2014-10-24 03:32:31 +03:00
2015-01-12 04:04:04 +02:00
result = mdb_del ( * m_write_txn , m_output_keys , & k , NULL ) ;
2014-10-31 23:34:15 +02:00
if ( result = = MDB_NOTFOUND )
{
2015-01-12 04:04:04 +02:00
LOG_PRINT_L0 ( " Unexpected: global output index not found in m_output_keys " ) ;
2014-10-31 23:34:15 +02:00
}
else if ( result )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Error adding removal of output pubkey to db transaction " ) ) ;
2014-10-31 23:34:15 +02:00
2015-01-12 04:04:04 +02:00
remove_amount_output_index ( amount , out_index ) ;
2014-10-31 00:33:35 +02:00
m_num_outputs - - ;
2014-10-21 23:33:43 +03:00
}
2015-01-12 04:04:04 +02:00
void BlockchainLMDB : : remove_amount_output_index ( const uint64_t amount , const uint64_t global_output_index )
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
check_open ( ) ;
lmdb_cur cur ( * m_write_txn , m_output_amounts ) ;
MDB_val_copy < uint64_t > k ( amount ) ;
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
throw1 ( OUTPUT_DNE ( " Attempting to get an output index by amount and amount index, but amount not found " ) ) ;
else if ( result )
throw0 ( DB_ERROR ( " DB error attempting to get an output " ) ) ;
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
mdb_cursor_get ( cur , & k , & v , MDB_FIRST_DUP ) ;
uint64_t amount_output_index = 0 ;
uint64_t goi = 0 ;
bool found_index = false ;
for ( uint64_t i = 0 ; i < num_elems ; + + i )
{
mdb_cursor_get ( cur , & k , & v , MDB_GET_CURRENT ) ;
goi = * ( const uint64_t * ) v . mv_data ;
if ( goi = = global_output_index )
{
amount_output_index = i ;
found_index = true ;
break ;
}
mdb_cursor_get ( cur , & k , & v , MDB_NEXT_DUP ) ;
}
if ( found_index )
{
// found the amount output index
// now delete it
result = mdb_cursor_del ( cur , 0 ) ;
if ( result )
throw0 ( DB_ERROR ( std : : string ( " Error deleting amount output index " ) . append ( boost : : lexical_cast < std : : string > ( amount_output_index ) ) . c_str ( ) ) ) ;
}
else
{
// not found
cur . close ( ) ;
throw1 ( OUTPUT_DNE ( " Failed to find amount output index " ) ) ;
}
cur . close ( ) ;
}
2014-10-21 23:33:43 +03:00
void BlockchainLMDB : : add_spent_key ( const crypto : : key_image & k_image )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-24 02:47:36 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : key_image > val_key ( k_image ) ;
2014-10-24 02:47:36 +03:00
MDB_val unused ;
if ( mdb_get ( * m_write_txn , m_spent_keys , & val_key , & unused ) = = 0 )
2014-12-12 23:34:45 +02:00
throw1 ( KEY_IMAGE_EXISTS ( " Attempting to add spent key image that's already in the db " ) ) ;
2014-10-24 02:47:36 +03:00
char anything = ' \0 ' ;
unused . mv_size = sizeof ( char ) ;
unused . mv_data = & anything ;
2015-02-12 01:55:53 +02:00
if ( auto result = mdb_put ( * m_write_txn , m_spent_keys , & val_key , & unused , 0 ) )
throw1 ( DB_ERROR ( std : : string ( " Error adding spent key image to db transaction: " ) . append ( mdb_strerror ( result ) ) . c_str ( ) ) ) ;
2014-10-21 23:33:43 +03:00
}
void BlockchainLMDB : : remove_spent_key ( const crypto : : key_image & k_image )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-24 03:32:31 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : key_image > k ( k_image ) ;
2014-10-28 02:45:33 +02:00
auto result = mdb_del ( * m_write_txn , m_spent_keys , & k , NULL ) ;
2014-10-24 03:32:31 +03:00
if ( result ! = 0 & & result ! = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Error adding removal of key image to db transaction " ) ) ;
2014-10-23 22:37:10 +03:00
}
2014-10-28 02:45:33 +02:00
blobdata BlockchainLMDB : : output_to_blob ( const tx_out & output )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-28 02:45:33 +02:00
blobdata b ;
if ( ! t_serializable_object_to_blob ( output , b ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Error serializing output to blob " ) ) ;
2014-10-28 02:45:33 +02:00
return b ;
}
2014-12-06 23:37:22 +02:00
tx_out BlockchainLMDB : : output_from_blob ( const blobdata & blob ) const
2014-10-28 02:45:33 +02:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-28 02:45:33 +02:00
std : : stringstream ss ;
ss < < blob ;
binary_archive < false > ba ( ss ) ;
tx_out o ;
if ( ! ( : : serialization : : serialize ( ba , o ) ) )
2014-12-12 23:34:45 +02:00
throw1 ( DB_ERROR ( " Error deserializing tx output blob " ) ) ;
2014-10-28 02:45:33 +02:00
return o ;
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_output_global_index ( const uint64_t & amount , const uint64_t & index ) const
2014-10-28 02:45:33 +02:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2015-01-09 23:01:22 +02:00
check_open ( ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2015-01-09 23:01:22 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
lmdb_cur cur ( txn , m_output_amounts ) ;
MDB_val_copy < uint64_t > k ( amount ) ;
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
throw1 ( OUTPUT_DNE ( " Attempting to get an output index by amount and amount index, but amount not found " ) ) ;
else if ( result )
throw0 ( DB_ERROR ( " DB error attempting to get an output " ) ) ;
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
if ( num_elems < = index )
throw1 ( OUTPUT_DNE ( " Attempting to get an output index by amount and amount index, but output not found " ) ) ;
mdb_cursor_get ( cur , & k , & v , MDB_FIRST_DUP ) ;
for ( uint64_t i = 0 ; i < index ; + + i )
{
mdb_cursor_get ( cur , & k , & v , MDB_NEXT_DUP ) ;
}
mdb_cursor_get ( cur , & k , & v , MDB_GET_CURRENT ) ;
uint64_t glob_index = * ( const uint64_t * ) v . mv_data ;
cur . close ( ) ;
txn . commit ( ) ;
return glob_index ;
2014-10-28 02:45:33 +02:00
}
2014-12-06 23:37:22 +02:00
void BlockchainLMDB : : check_open ( ) const
2014-10-23 22:37:10 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
if ( ! m_open )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB operation attempted on a not-open DB instance " ) ) ;
2014-10-21 23:33:43 +03:00
}
BlockchainLMDB : : ~ BlockchainLMDB ( )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2015-02-12 01:55:53 +02:00
// batch transaction shouldn't be active at this point. If it is, consider it aborted.
if ( m_batch_active )
batch_abort ( ) ;
2014-10-21 23:33:43 +03:00
}
2015-02-12 01:55:53 +02:00
BlockchainLMDB : : BlockchainLMDB ( bool batch_transactions )
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
// initialize folder to something "safe" just in case
// someone accidentally misuses this class...
m_folder = " thishsouldnotexistbecauseitisgibberish " ;
m_open = false ;
2015-02-12 01:55:53 +02:00
m_batch_transactions = batch_transactions ;
m_write_txn = nullptr ;
m_batch_active = false ;
2014-10-28 21:30:16 +02:00
m_height = 0 ;
2014-10-21 23:33:43 +03:00
}
2015-02-12 02:02:20 +02:00
void BlockchainLMDB : : open ( const std : : string & filename , const int mdb_flags )
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
if ( m_open )
2014-12-12 23:34:45 +02:00
throw0 ( DB_OPEN_FAILURE ( " Attempted to open db, but it's already open " ) ) ;
2014-10-23 22:37:10 +03:00
boost : : filesystem : : path direc ( filename ) ;
if ( boost : : filesystem : : exists ( direc ) )
{
if ( ! boost : : filesystem : : is_directory ( direc ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_OPEN_FAILURE ( " LMDB needs a directory path, but a file was passed " ) ) ;
2014-10-23 22:37:10 +03:00
}
else
2014-10-21 23:33:43 +03:00
{
2014-10-23 22:37:10 +03:00
if ( ! boost : : filesystem : : create_directory ( direc ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_OPEN_FAILURE ( std : : string ( " Failed to create directory " ) . append ( filename ) . c_str ( ) ) ) ;
2014-10-23 22:37:10 +03:00
}
2015-02-19 16:37:00 +02:00
// check for existing LMDB files in base directory
boost : : filesystem : : path old_files = direc . parent_path ( ) ;
if ( boost : : filesystem : : exists ( old_files / " data.mdb " ) | |
boost : : filesystem : : exists ( old_files / " lock.mdb " ) )
{
LOG_PRINT_L0 ( " Found existing LMDB files in " < < old_files . c_str ( ) ) ;
LOG_PRINT_L0 ( " Move data.mdb and/or lock.mdb to " < < filename < < " , or delete them, and then restart " ) ;
throw DB_ERROR ( " Database could not be opened " ) ;
}
2014-10-23 22:37:10 +03:00
m_folder = filename ;
// set up lmdb environment
if ( mdb_env_create ( & m_env ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create lmdb environment " ) ) ;
2014-10-28 02:45:33 +02:00
if ( mdb_env_set_maxdbs ( m_env , 20 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to set max number of dbs " ) ) ;
2014-11-13 01:27:25 +02:00
size_t mapsize = 1LL < < 34 ;
2014-10-31 23:34:15 +02:00
if ( auto result = mdb_env_set_mapsize ( m_env , mapsize ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( std : : string ( " Failed to set max memory map size: " ) . append ( mdb_strerror ( result ) ) . c_str ( ) ) ) ;
2015-02-12 01:55:53 +02:00
if ( auto result = mdb_env_open ( m_env , filename . c_str ( ) , mdb_flags , 0644 ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( std : : string ( " Failed to open lmdb environment: " ) . append ( mdb_strerror ( result ) ) . c_str ( ) ) ) ;
2014-10-23 22:37:10 +03:00
// get a read/write MDB_txn
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-23 22:37:10 +03:00
if ( mdb_txn_begin ( m_env , NULL , 0 , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-21 23:33:43 +03:00
2014-10-23 22:37:10 +03:00
// open necessary databases, and set properties as needed
// uses macros to avoid having to change things too many places
2014-10-28 02:45:33 +02:00
lmdb_db_open ( txn , LMDB_BLOCKS , MDB_INTEGERKEY | MDB_CREATE , m_blocks , " Failed to open db handle for m_blocks " ) ;
2014-10-23 22:37:10 +03:00
2014-10-28 02:45:33 +02:00
lmdb_db_open ( txn , LMDB_BLOCK_TIMESTAMPS , MDB_INTEGERKEY | MDB_CREATE , m_block_timestamps , " Failed to open db handle for m_block_timestamps " ) ;
lmdb_db_open ( txn , LMDB_BLOCK_HEIGHTS , MDB_CREATE , m_block_heights , " Failed to open db handle for m_block_heights " ) ;
lmdb_db_open ( txn , LMDB_BLOCK_HASHES , MDB_INTEGERKEY | MDB_CREATE , m_block_hashes , " Failed to open db handle for m_block_hashes " ) ;
lmdb_db_open ( txn , LMDB_BLOCK_SIZES , MDB_INTEGERKEY | MDB_CREATE , m_block_sizes , " Failed to open db handle for m_block_sizes " ) ;
lmdb_db_open ( txn , LMDB_BLOCK_DIFFS , MDB_INTEGERKEY | MDB_CREATE , m_block_diffs , " Failed to open db handle for m_block_diffs " ) ;
lmdb_db_open ( txn , LMDB_BLOCK_COINS , MDB_INTEGERKEY | MDB_CREATE , m_block_coins , " Failed to open db handle for m_block_coins " ) ;
2014-10-24 02:47:36 +03:00
2014-10-28 02:45:33 +02:00
lmdb_db_open ( txn , LMDB_TXS , MDB_CREATE , m_txs , " Failed to open db handle for m_txs " ) ;
lmdb_db_open ( txn , LMDB_TX_UNLOCKS , MDB_CREATE , m_tx_unlocks , " Failed to open db handle for m_tx_unlocks " ) ;
lmdb_db_open ( txn , LMDB_TX_HEIGHTS , MDB_CREATE , m_tx_heights , " Failed to open db handle for m_tx_heights " ) ;
lmdb_db_open ( txn , LMDB_TX_OUTPUTS , MDB_DUPSORT | MDB_CREATE , m_tx_outputs , " Failed to open db handle for m_tx_outputs " ) ;
2014-10-24 02:47:36 +03:00
2014-10-28 02:45:33 +02:00
lmdb_db_open ( txn , LMDB_OUTPUT_TXS , MDB_INTEGERKEY | MDB_CREATE , m_output_txs , " Failed to open db handle for m_output_txs " ) ;
lmdb_db_open ( txn , LMDB_OUTPUT_INDICES , MDB_INTEGERKEY | MDB_CREATE , m_output_indices , " Failed to open db handle for m_output_indices " ) ;
lmdb_db_open ( txn , LMDB_OUTPUT_AMOUNTS , MDB_INTEGERKEY | MDB_DUPSORT | MDB_CREATE , m_output_amounts , " Failed to open db handle for m_output_amounts " ) ;
2014-10-31 23:34:15 +02:00
lmdb_db_open ( txn , LMDB_OUTPUT_KEYS , MDB_INTEGERKEY | MDB_CREATE , m_output_keys , " Failed to open db handle for m_output_keys " ) ;
2014-10-31 00:33:35 +02:00
/*************** not used, but kept for posterity
2014-10-28 02:45:33 +02:00
lmdb_db_open ( txn , LMDB_OUTPUTS , MDB_INTEGERKEY | MDB_CREATE , m_outputs , " Failed to open db handle for m_outputs " ) ;
2014-10-31 00:33:35 +02:00
lmdb_db_open ( txn , LMDB_OUTPUT_GINDICES , MDB_CREATE , m_output_gindices , " Failed to open db handle for m_output_gindices " ) ;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-10-24 02:47:36 +03:00
2015-02-12 01:55:53 +02:00
lmdb_db_open ( txn , LMDB_SPENT_KEYS , MDB_CREATE , m_spent_keys , " Failed to open db handle for m_spent_keys " ) ;
2014-10-21 23:33:43 +03:00
2014-10-29 04:25:03 +02:00
mdb_set_dupsort ( txn , m_output_amounts , compare_uint64 ) ;
mdb_set_dupsort ( txn , m_tx_outputs , compare_uint64 ) ;
2014-10-23 22:37:10 +03:00
// get and keep current height
MDB_stat db_stats ;
if ( mdb_stat ( txn , m_blocks , & db_stats ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to query m_blocks " ) ) ;
2014-10-28 21:30:16 +02:00
LOG_PRINT_L2 ( " Setting m_height to: " < < db_stats . ms_entries ) ;
2014-10-23 22:37:10 +03:00
m_height = db_stats . ms_entries ;
2014-10-24 02:47:36 +03:00
// get and keep current number of outputs
2014-10-31 23:34:15 +02:00
if ( mdb_stat ( txn , m_output_indices , & db_stats ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to query m_output_indices " ) ) ;
2014-10-24 02:47:36 +03:00
m_num_outputs = db_stats . ms_entries ;
2014-10-23 22:37:10 +03:00
// commit the transaction
txn . commit ( ) ;
2014-10-21 23:33:43 +03:00
2014-10-23 22:37:10 +03:00
m_open = true ;
2014-10-21 23:33:43 +03:00
// from here, init should be finished
}
void BlockchainLMDB : : close ( )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
{
LOG_PRINT_L3 ( " close() first calling batch_abort() due to active batch transaction " ) ;
batch_abort ( ) ;
}
this - > sync ( ) ;
2014-10-28 02:45:33 +02:00
// FIXME: not yet thread safe!!! Use with care.
mdb_env_close ( m_env ) ;
2014-10-21 23:33:43 +03:00
}
void BlockchainLMDB : : sync ( )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2015-02-19 06:52:44 +02:00
check_open ( ) ;
2015-02-12 01:55:53 +02:00
// Does nothing unless LMDB environment was opened with MDB_NOSYNC or in part
// MDB_NOMETASYNC. Force flush to be synchronous.
if ( auto result = mdb_env_sync ( m_env , true ) )
{
throw0 ( DB_ERROR ( std : : string ( " Failed to sync database " ) . append ( mdb_strerror ( result ) ) . c_str ( ) ) ) ;
}
2014-10-21 23:33:43 +03:00
}
void BlockchainLMDB : : reset ( )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-28 02:45:33 +02:00
// TODO: this
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
std : : vector < std : : string > BlockchainLMDB : : get_filenames ( ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
std : : vector < std : : string > filenames ;
boost : : filesystem : : path datafile ( m_folder ) ;
datafile / = " data.mdb " ;
boost : : filesystem : : path lockfile ( m_folder ) ;
lockfile / = " lock.mdb " ;
filenames . push_back ( datafile . string ( ) ) ;
filenames . push_back ( lockfile . string ( ) ) ;
return filenames ;
2014-10-21 23:33:43 +03:00
}
2015-03-14 03:39:27 +02:00
std : : string BlockchainLMDB : : get_db_name ( ) const
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
return std : : string ( " lmdb " ) ;
}
2014-10-28 02:45:33 +02:00
// TODO: this?
2014-10-21 23:33:43 +03:00
bool BlockchainLMDB : : lock ( )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-21 23:33:43 +03:00
return false ;
}
2014-10-28 02:45:33 +02:00
// TODO: this?
2014-10-21 23:33:43 +03:00
void BlockchainLMDB : : unlock ( )
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
bool BlockchainLMDB : : block_exists ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-23 22:37:10 +03:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-23 22:37:10 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > key ( h ) ;
2014-10-23 22:37:10 +03:00
MDB_val result ;
2014-10-28 02:45:33 +02:00
auto get_result = mdb_get ( txn , m_block_heights , & key , & result ) ;
2014-10-23 22:37:10 +03:00
if ( get_result = = MDB_NOTFOUND )
{
2014-10-24 02:47:36 +03:00
txn . commit ( ) ;
2015-02-24 01:28:20 +02:00
LOG_PRINT_L3 ( " Block with hash " < < epee : : string_tools : : pod_to_hex ( h ) < < " not found in db " ) ;
2014-10-23 22:37:10 +03:00
return false ;
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to fetch block index from hash " ) ) ;
2014-10-23 22:37:10 +03:00
2014-10-24 02:47:36 +03:00
txn . commit ( ) ;
2014-10-23 22:37:10 +03:00
return true ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
block BlockchainLMDB : : get_block ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
return get_block_from_height ( get_block_height ( h ) ) ;
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_block_height ( const crypto : : hash & h ) const
2014-10-28 02:45:33 +02:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-28 02:45:33 +02:00
check_open ( ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-23 22:37:10 +03:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-23 22:37:10 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > key ( h ) ;
2014-10-23 22:37:10 +03:00
MDB_val result ;
2014-10-28 02:45:33 +02:00
auto get_result = mdb_get ( txn , m_block_heights , & key , & result ) ;
2014-10-23 22:37:10 +03:00
if ( get_result = = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw1 ( BLOCK_DNE ( " Attempted to retrieve non-existent block height " ) ) ;
2014-10-23 22:37:10 +03:00
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve a block height from the db " ) ) ;
2014-10-23 22:37:10 +03:00
txn . commit ( ) ;
2014-12-11 21:30:55 +02:00
return * ( const uint64_t * ) result . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
block_header BlockchainLMDB : : get_block_header ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
// block_header object is automatically cast from block object
return get_block ( h ) ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
block BlockchainLMDB : : get_block_from_height ( const uint64_t & height ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-23 22:37:10 +03:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-23 22:37:10 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > key ( height ) ;
2014-10-23 22:37:10 +03:00
MDB_val result ;
auto get_result = mdb_get ( txn , m_blocks , & key , & result ) ;
if ( get_result = = MDB_NOTFOUND )
{
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( std : : string ( " Attempt to get block from height " ) . append ( boost : : lexical_cast < std : : string > ( height ) ) . append ( " failed -- block not in db " ) . c_str ( ) ) ) ;
2014-10-23 22:37:10 +03:00
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve a block from the db " ) ) ;
2014-10-23 22:37:10 +03:00
txn . commit ( ) ;
blobdata bd ;
bd . assign ( reinterpret_cast < char * > ( result . mv_data ) , result . mv_size ) ;
2014-10-21 23:33:43 +03:00
block b ;
2014-10-23 22:37:10 +03:00
if ( ! parse_and_validate_block_from_blob ( bd , b ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to parse block from blob retrieved from the db " ) ) ;
2014-10-23 22:37:10 +03:00
2014-10-21 23:33:43 +03:00
return b ;
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_block_timestamp ( const uint64_t & height ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > key ( height ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_block_timestamps , & key , & result ) ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
{
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( std : : string ( " Attempt to get timestamp from height " ) . append ( boost : : lexical_cast < std : : string > ( height ) ) . append ( " failed -- timestamp not in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve a timestamp from the db " ) ) ;
2014-10-28 02:45:33 +02:00
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-12-11 21:30:55 +02:00
return * ( const uint64_t * ) result . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_top_block_timestamp ( ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
// if no blocks, return 0
if ( m_height = = 0 )
{
return 0 ;
}
return get_block_timestamp ( m_height - 1 ) ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
size_t BlockchainLMDB : : get_block_size ( const uint64_t & height ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > key ( height ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_block_sizes , & key , & result ) ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
{
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( std : : string ( " Attempt to get block size from height " ) . append ( boost : : lexical_cast < std : : string > ( height ) ) . append ( " failed -- block size not in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve a block size from the db " ) ) ;
2014-10-28 02:45:33 +02:00
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-12-11 21:30:55 +02:00
return * ( const size_t * ) result . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
difficulty_type BlockchainLMDB : : get_block_cumulative_difficulty ( const uint64_t & height ) const
2014-10-21 23:33:43 +03:00
{
2015-02-12 01:55:53 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ < < " height: " < < height ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > key ( height ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_block_diffs , & key , & result ) ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
{
2015-01-09 14:29:05 +02:00
throw0 ( DB_ERROR ( std : : string ( " Attempt to get cumulative difficulty from height " ) . append ( boost : : lexical_cast < std : : string > ( height ) ) . append ( " failed -- difficulty not in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve a cumulative difficulty from the db " ) ) ;
2014-10-28 02:45:33 +02:00
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-10-28 02:45:33 +02:00
return * ( difficulty_type * ) result . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
difficulty_type BlockchainLMDB : : get_block_difficulty ( const uint64_t & height ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
difficulty_type diff1 = 0 ;
difficulty_type diff2 = 0 ;
diff1 = get_block_cumulative_difficulty ( height ) ;
if ( height ! = 0 )
{
diff2 = get_block_cumulative_difficulty ( height - 1 ) ;
}
return diff1 - diff2 ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_block_already_generated_coins ( const uint64_t & height ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > key ( height ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_block_coins , & key , & result ) ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
{
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( std : : string ( " Attempt to get generated coins from height " ) . append ( boost : : lexical_cast < std : : string > ( height ) ) . append ( " failed -- block size not in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve a total generated coins from the db " ) ) ;
2014-10-28 02:45:33 +02:00
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-12-11 21:30:55 +02:00
return * ( const uint64_t * ) result . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
crypto : : hash BlockchainLMDB : : get_block_hash_from_height ( const uint64_t & height ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-21 23:33:43 +03:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > key ( height ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_block_hashes , & key , & result ) ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
{
2014-12-12 23:34:45 +02:00
throw0 ( BLOCK_DNE ( std : : string ( " Attempt to get hash from height " ) . append ( boost : : lexical_cast < std : : string > ( height ) ) . append ( " failed -- hash not in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
}
else if ( get_result )
2015-02-12 01:55:53 +02:00
throw0 ( DB_ERROR ( std : : string ( " Error attempting to retrieve a block hash from the db: " ) .
append ( mdb_strerror ( get_result ) ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-10-28 02:45:33 +02:00
return * ( crypto : : hash * ) result . mv_data ;
}
2014-12-06 23:37:22 +02:00
std : : vector < block > BlockchainLMDB : : get_blocks_range ( const uint64_t & h1 , const uint64_t & h2 ) const
2014-10-28 02:45:33 +02:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-21 23:33:43 +03:00
std : : vector < block > v ;
2014-10-28 02:45:33 +02:00
for ( uint64_t height = h1 ; height < = h2 ; + + height )
{
v . push_back ( get_block_from_height ( height ) ) ;
}
2014-10-21 23:33:43 +03:00
return v ;
}
2014-12-06 23:37:22 +02:00
std : : vector < crypto : : hash > BlockchainLMDB : : get_hashes_range ( const uint64_t & h1 , const uint64_t & h2 ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-21 23:33:43 +03:00
std : : vector < crypto : : hash > v ;
2014-10-28 02:45:33 +02:00
for ( uint64_t height = h1 ; height < = h2 ; + + height )
{
v . push_back ( get_block_hash_from_height ( height ) ) ;
}
2014-10-21 23:33:43 +03:00
return v ;
}
2014-12-06 23:37:22 +02:00
crypto : : hash BlockchainLMDB : : top_block_hash ( ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
if ( m_height ! = 0 )
{
return get_block_hash_from_height ( m_height - 1 ) ;
}
return null_hash ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
block BlockchainLMDB : : get_top_block ( ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
if ( m_height ! = 0 )
{
return get_block_from_height ( m_height - 1 ) ;
}
2014-10-21 23:33:43 +03:00
block b ;
return b ;
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : height ( ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-24 02:47:36 +03:00
2015-02-22 20:31:11 +02:00
return m_height ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
bool BlockchainLMDB : : tx_exists ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
2015-02-11 06:01:02 +02:00
txn_ptr = m_write_txn ;
else
2015-02-12 01:55:53 +02:00
{
2015-02-11 06:01:02 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2015-02-12 01:55:53 +02:00
}
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > key ( h ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-11 06:01:02 +02:00
TIME_MEASURE_START ( time1 ) ;
2015-02-11 06:01:02 +02:00
auto get_result = mdb_get ( * txn_ptr , m_txs , & key , & result ) ;
2015-02-11 06:01:02 +02:00
TIME_MEASURE_FINISH ( time1 ) ;
time_tx_exists + = time1 ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
{
2015-02-11 06:01:02 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2015-02-12 01:55:53 +02:00
LOG_PRINT_L1 ( " transaction with hash " < < epee : : string_tools : : pod_to_hex ( h ) < < " not found in db " ) ;
2014-10-28 02:45:33 +02:00
return false ;
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to fetch transaction from hash " ) ) ;
2014-10-28 02:45:33 +02:00
return true ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_tx_unlock_time ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-28 02:45:33 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > key ( h ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
auto get_result = mdb_get ( txn , m_tx_unlocks , & key , & result ) ;
if ( get_result = = MDB_NOTFOUND )
2015-02-12 01:55:53 +02:00
throw1 ( TX_DNE ( std : : string ( " tx unlock time with hash " ) . append ( epee : : string_tools : : pod_to_hex ( h ) ) . append ( " not found in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to fetch tx unlock time from hash " ) ) ;
2014-10-28 02:45:33 +02:00
2014-12-11 21:30:55 +02:00
return * ( const uint64_t * ) result . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
transaction BlockchainLMDB : : get_tx ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > key ( h ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_txs , & key , & result ) ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
2015-02-12 01:55:53 +02:00
throw1 ( TX_DNE ( std : : string ( " tx with hash " ) . append ( epee : : string_tools : : pod_to_hex ( h ) ) . append ( " not found in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to fetch tx from hash " ) ) ;
2014-10-28 02:45:33 +02:00
blobdata bd ;
bd . assign ( reinterpret_cast < char * > ( result . mv_data ) , result . mv_size ) ;
transaction tx ;
if ( ! parse_and_validate_tx_from_blob ( bd , tx ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to parse tx from blob retrieved from the db " ) ) ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-10-28 02:45:33 +02:00
return tx ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_tx_count ( ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-28 02:45:33 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-28 02:45:33 +02:00
MDB_stat db_stats ;
if ( mdb_stat ( txn , m_txs , & db_stats ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to query m_txs " ) ) ;
2014-10-28 02:45:33 +02:00
txn . commit ( ) ;
return db_stats . ms_entries ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
std : : vector < transaction > BlockchainLMDB : : get_tx_list ( const std : : vector < crypto : : hash > & hlist ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-21 23:33:43 +03:00
std : : vector < transaction > v ;
2014-10-28 02:45:33 +02:00
for ( auto & h : hlist )
{
v . push_back ( get_tx ( h ) ) ;
}
2014-10-21 23:33:43 +03:00
return v ;
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_tx_block_height ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > key ( h ) ;
2014-10-28 02:45:33 +02:00
MDB_val result ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_tx_heights , & key , & result ) ;
2014-10-28 02:45:33 +02:00
if ( get_result = = MDB_NOTFOUND )
{
2015-02-12 01:55:53 +02:00
throw1 ( TX_DNE ( std : : string ( " tx height with hash " ) . append ( epee : : string_tools : : pod_to_hex ( h ) ) . append ( " not found in db " ) . c_str ( ) ) ) ;
2014-10-28 02:45:33 +02:00
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to fetch tx height from hash " ) ) ;
2014-10-28 02:45:33 +02:00
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-12-11 21:30:55 +02:00
return * ( const uint64_t * ) result . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-10-28 02:45:33 +02:00
//FIXME: make sure the random method used here is appropriate
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_random_output ( const uint64_t & amount ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
uint64_t num_outputs = get_num_outputs ( amount ) ;
if ( num_outputs = = 0 )
2014-12-12 23:34:45 +02:00
throw1 ( OUTPUT_DNE ( " Attempting to get a random output for an amount, but none exist " ) ) ;
2014-10-28 02:45:33 +02:00
return crypto : : rand < uint64_t > ( ) % num_outputs ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
uint64_t BlockchainLMDB : : get_num_outputs ( const uint64_t & amount ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-28 02:45:33 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-28 02:45:33 +02:00
lmdb_cur cur ( txn , m_output_amounts ) ;
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > k ( amount ) ;
2014-10-28 02:45:33 +02:00
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
{
return 0 ;
}
else if ( result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to get number of outputs of an amount " ) ) ;
2014-10-28 02:45:33 +02:00
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
txn . commit ( ) ;
return num_elems ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
crypto : : public_key BlockchainLMDB : : get_output_key ( const uint64_t & amount , const uint64_t & index ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-01-09 23:01:22 +02:00
uint64_t glob_index = get_output_global_index ( amount , index ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-31 23:34:15 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-28 02:45:33 +02:00
2015-01-09 23:01:22 +02:00
MDB_val_copy < uint64_t > k ( glob_index ) ;
2014-10-31 23:34:15 +02:00
MDB_val v ;
auto get_result = mdb_get ( txn , m_output_keys , & k , & v ) ;
if ( get_result = = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Attempting to get output pubkey by global index, but key does not exist " ) ) ;
2014-10-31 23:34:15 +02:00
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve an output pubkey from the db " ) ) ;
2014-10-28 02:45:33 +02:00
2014-10-31 23:34:15 +02:00
return * ( crypto : : public_key * ) v . mv_data ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
tx_out BlockchainLMDB : : get_output ( const crypto : : hash & h , const uint64_t & index ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-28 02:45:33 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-28 02:45:33 +02:00
lmdb_cur cur ( txn , m_tx_outputs ) ;
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > k ( h ) ;
2014-10-28 02:45:33 +02:00
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw1 ( OUTPUT_DNE ( " Attempting to get an output by tx hash and tx index, but output not found " ) ) ;
2014-10-28 02:45:33 +02:00
else if ( result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to get an output " ) ) ;
2014-10-28 02:45:33 +02:00
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
if ( num_elems < = index )
2014-12-12 23:34:45 +02:00
throw1 ( OUTPUT_DNE ( " Attempting to get an output by tx hash and tx index, but output not found " ) ) ;
2014-10-28 02:45:33 +02:00
mdb_cursor_get ( cur , & k , & v , MDB_FIRST_DUP ) ;
2014-12-20 20:35:54 +02:00
for ( uint64_t i = 0 ; i < index ; + + i )
2014-10-28 02:45:33 +02:00
{
2014-12-20 20:35:54 +02:00
mdb_cursor_get ( cur , & k , & v , MDB_NEXT_DUP ) ;
2014-10-28 02:45:33 +02:00
}
mdb_cursor_get ( cur , & k , & v , MDB_GET_CURRENT ) ;
blobdata b ;
b = * ( blobdata * ) v . mv_data ;
cur . close ( ) ;
txn . commit ( ) ;
return output_from_blob ( b ) ;
}
2014-10-31 00:33:35 +02:00
// As this is not used, its return is now a blank output.
// This will save on space in the db.
2014-12-06 23:37:22 +02:00
tx_out BlockchainLMDB : : get_output ( const uint64_t & index ) const
2014-10-28 02:45:33 +02:00
{
2014-10-31 00:33:35 +02:00
return tx_out ( ) ;
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-28 02:45:33 +02:00
check_open ( ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-28 02:45:33 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > k ( index ) ;
2014-10-28 02:45:33 +02:00
MDB_val v ;
auto get_result = mdb_get ( txn , m_outputs , & k , & v ) ;
if ( get_result = = MDB_NOTFOUND )
{
2014-12-12 23:34:45 +02:00
throw OUTPUT_DNE ( " Attempting to get output by global index, but output does not exist " ) ;
2014-10-28 02:45:33 +02:00
}
else if ( get_result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Error attempting to retrieve an output from the db " ) ) ;
2014-10-28 02:45:33 +02:00
blobdata b = * ( blobdata * ) v . mv_data ;
return output_from_blob ( b ) ;
2014-10-21 23:33:43 +03:00
}
2014-12-14 22:20:41 +02:00
tx_out_index BlockchainLMDB : : get_output_tx_and_index_from_global ( const uint64_t & index ) const
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
check_open ( ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
2014-12-14 22:20:41 +02:00
MDB_val_copy < uint64_t > k ( index ) ;
MDB_val v ;
2015-02-12 01:55:53 +02:00
auto get_result = mdb_get ( * txn_ptr , m_output_txs , & k , & v ) ;
2014-12-14 22:20:41 +02:00
if ( get_result = = MDB_NOTFOUND )
throw1 ( OUTPUT_DNE ( " output with given index not in db " ) ) ;
else if ( get_result )
throw0 ( DB_ERROR ( " DB error attempting to fetch output tx hash " ) ) ;
crypto : : hash tx_hash = * ( crypto : : hash * ) v . mv_data ;
2015-02-12 01:55:53 +02:00
get_result = mdb_get ( * txn_ptr , m_output_indices , & k , & v ) ;
2014-12-14 22:20:41 +02:00
if ( get_result = = MDB_NOTFOUND )
throw1 ( OUTPUT_DNE ( " output with given index not in db " ) ) ;
else if ( get_result )
throw0 ( DB_ERROR ( " DB error attempting to fetch output tx index " ) ) ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-12-14 22:20:41 +02:00
return tx_out_index ( tx_hash , * ( const uint64_t * ) v . mv_data ) ;
}
2014-12-06 23:37:22 +02:00
tx_out_index BlockchainLMDB : : get_output_tx_and_index ( const uint64_t & amount , const uint64_t & index ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
mdb_txn_safe * txn_ptr = & txn ;
2015-02-12 01:55:53 +02:00
if ( m_batch_active )
txn_ptr = m_write_txn ;
else
{
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
}
lmdb_cur cur ( * txn_ptr , m_output_amounts ) ;
2014-10-28 02:45:33 +02:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < uint64_t > k ( amount ) ;
2014-10-28 02:45:33 +02:00
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw1 ( OUTPUT_DNE ( " Attempting to get an output index by amount and amount index, but amount not found " ) ) ;
2014-10-28 02:45:33 +02:00
else if ( result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to get an output " ) ) ;
2014-10-28 02:45:33 +02:00
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
if ( num_elems < = index )
2014-12-12 23:34:45 +02:00
throw1 ( OUTPUT_DNE ( " Attempting to get an output index by amount and amount index, but output not found " ) ) ;
2014-10-28 02:45:33 +02:00
mdb_cursor_get ( cur , & k , & v , MDB_FIRST_DUP ) ;
2014-12-20 20:35:54 +02:00
for ( uint64_t i = 0 ; i < index ; + + i )
2014-10-28 02:45:33 +02:00
{
2014-12-20 20:35:54 +02:00
mdb_cursor_get ( cur , & k , & v , MDB_NEXT_DUP ) ;
2014-10-28 02:45:33 +02:00
}
mdb_cursor_get ( cur , & k , & v , MDB_GET_CURRENT ) ;
2014-12-11 21:30:55 +02:00
uint64_t glob_index = * ( const uint64_t * ) v . mv_data ;
2014-10-28 02:45:33 +02:00
cur . close ( ) ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
txn . commit ( ) ;
2014-10-28 02:45:33 +02:00
2014-12-14 22:20:41 +02:00
return get_output_tx_and_index_from_global ( glob_index ) ;
2014-10-21 23:33:43 +03:00
}
2014-12-06 23:37:22 +02:00
std : : vector < uint64_t > BlockchainLMDB : : get_tx_output_indices ( const crypto : : hash & h ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-28 02:45:33 +02:00
std : : vector < uint64_t > index_vec ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-28 02:45:33 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-28 02:45:33 +02:00
lmdb_cur cur ( txn , m_tx_outputs ) ;
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : hash > k ( h ) ;
2014-10-28 02:45:33 +02:00
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
2014-12-12 23:34:45 +02:00
throw1 ( OUTPUT_DNE ( " Attempting to get an output by tx hash and tx index, but output not found " ) ) ;
2014-10-28 02:45:33 +02:00
else if ( result )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " DB error attempting to get an output " ) ) ;
2014-10-28 02:45:33 +02:00
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
mdb_cursor_get ( cur , & k , & v , MDB_FIRST_DUP ) ;
for ( uint64_t i = 0 ; i < num_elems ; + + i )
{
mdb_cursor_get ( cur , & k , & v , MDB_GET_CURRENT ) ;
2014-12-11 21:30:55 +02:00
index_vec . push_back ( * ( const uint64_t * ) v . mv_data ) ;
2014-12-20 20:36:22 +02:00
mdb_cursor_get ( cur , & k , & v , MDB_NEXT_DUP ) ;
2014-10-28 02:45:33 +02:00
}
cur . close ( ) ;
txn . commit ( ) ;
return index_vec ;
2014-10-21 23:33:43 +03:00
}
2015-01-09 22:57:33 +02:00
std : : vector < uint64_t > BlockchainLMDB : : get_tx_amount_output_indices ( const crypto : : hash & h ) const
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
check_open ( ) ;
std : : vector < uint64_t > index_vec ;
std : : vector < uint64_t > index_vec2 ;
// get the transaction's global output indices first
index_vec = get_tx_output_indices ( h ) ;
// these are next used to obtain the amount output indices
transaction tx = get_tx ( h ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2015-01-09 22:57:33 +02:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
uint64_t i = 0 ;
uint64_t global_index ;
BOOST_FOREACH ( const auto & vout , tx . vout )
{
uint64_t amount = vout . amount ;
global_index = index_vec [ i ] ;
lmdb_cur cur ( txn , m_output_amounts ) ;
MDB_val_copy < uint64_t > k ( amount ) ;
MDB_val v ;
auto result = mdb_cursor_get ( cur , & k , & v , MDB_SET ) ;
if ( result = = MDB_NOTFOUND )
throw1 ( OUTPUT_DNE ( " Attempting to get an output index by amount and amount index, but amount not found " ) ) ;
else if ( result )
throw0 ( DB_ERROR ( " DB error attempting to get an output " ) ) ;
size_t num_elems = 0 ;
mdb_cursor_count ( cur , & num_elems ) ;
mdb_cursor_get ( cur , & k , & v , MDB_FIRST_DUP ) ;
uint64_t amount_output_index = 0 ;
uint64_t output_index = 0 ;
bool found_index = false ;
for ( uint64_t j = 0 ; j < num_elems ; + + j )
{
mdb_cursor_get ( cur , & k , & v , MDB_GET_CURRENT ) ;
output_index = * ( const uint64_t * ) v . mv_data ;
if ( output_index = = global_index )
{
amount_output_index = j ;
found_index = true ;
break ;
}
mdb_cursor_get ( cur , & k , & v , MDB_NEXT_DUP ) ;
}
if ( found_index )
{
index_vec2 . push_back ( amount_output_index ) ;
}
else
{
// not found
cur . close ( ) ;
txn . commit ( ) ;
throw1 ( OUTPUT_DNE ( " specified output not found in db " ) ) ;
}
cur . close ( ) ;
+ + i ;
}
txn . commit ( ) ;
return index_vec2 ;
}
2014-12-06 23:37:22 +02:00
bool BlockchainLMDB : : has_key_image ( const crypto : : key_image & img ) const
2014-10-21 23:33:43 +03:00
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2014-10-24 02:47:36 +03:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2014-10-24 02:47:36 +03:00
if ( mdb_txn_begin ( m_env , NULL , MDB_RDONLY , txn ) )
2014-12-12 23:34:45 +02:00
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
2014-10-24 02:47:36 +03:00
2014-12-12 15:27:05 +02:00
MDB_val_copy < crypto : : key_image > val_key ( img ) ;
2014-10-24 02:47:36 +03:00
MDB_val unused ;
if ( mdb_get ( txn , m_spent_keys , & val_key , & unused ) = = 0 )
{
txn . commit ( ) ;
return true ;
}
txn . commit ( ) ;
2014-10-21 23:33:43 +03:00
return false ;
}
2015-02-12 01:55:53 +02:00
void BlockchainLMDB : : batch_start ( )
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
if ( ! m_batch_transactions )
throw0 ( DB_ERROR ( " batch transactions not enabled " ) ) ;
if ( m_batch_active )
throw0 ( DB_ERROR ( " batch transaction already in progress " ) ) ;
if ( m_write_txn )
throw0 ( DB_ERROR ( " batch transaction attempted, but m_write_txn already in use " ) ) ;
check_open ( ) ;
// NOTE: need to make sure it's destroyed properly when done
if ( mdb_txn_begin ( m_env , NULL , 0 , m_write_batch_txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
// indicates this transaction is for batch transactions, but not whether it's
// active
m_write_batch_txn . m_batch_txn = true ;
m_write_txn = & m_write_batch_txn ;
m_batch_active = true ;
LOG_PRINT_L3 ( " batch transaction: begin " ) ;
}
void BlockchainLMDB : : batch_commit ( )
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
if ( ! m_batch_transactions )
throw0 ( DB_ERROR ( " batch transactions not enabled " ) ) ;
if ( ! m_batch_active )
throw0 ( DB_ERROR ( " batch transaction not in progress " ) ) ;
check_open ( ) ;
LOG_PRINT_L3 ( " batch transaction: committing... " ) ;
2015-02-12 01:55:53 +02:00
TIME_MEASURE_START ( time1 ) ;
2015-02-12 01:55:53 +02:00
m_write_txn - > commit ( ) ;
2015-02-12 01:55:53 +02:00
TIME_MEASURE_FINISH ( time1 ) ;
time_commit1 + = time1 ;
2015-02-12 01:55:53 +02:00
LOG_PRINT_L3 ( " batch transaction: committed " ) ;
if ( mdb_txn_begin ( m_env , NULL , 0 , m_write_batch_txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
if ( ! m_write_batch_txn . m_batch_txn )
throw0 ( DB_ERROR ( " m_write_batch_txn not marked as a batch transaction " ) ) ;
m_write_txn = & m_write_batch_txn ;
}
void BlockchainLMDB : : batch_stop ( )
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
if ( ! m_batch_transactions )
throw0 ( DB_ERROR ( " batch transactions not enabled " ) ) ;
if ( ! m_batch_active )
throw0 ( DB_ERROR ( " batch transaction not in progress " ) ) ;
check_open ( ) ;
LOG_PRINT_L3 ( " batch transaction: committing... " ) ;
2015-02-12 01:55:53 +02:00
TIME_MEASURE_START ( time1 ) ;
2015-02-12 01:55:53 +02:00
m_write_txn - > commit ( ) ;
2015-02-12 01:55:53 +02:00
TIME_MEASURE_FINISH ( time1 ) ;
time_commit1 + = time1 ;
2015-02-12 01:55:53 +02:00
// for destruction of batch transaction
m_write_txn = nullptr ;
m_batch_active = false ;
LOG_PRINT_L3 ( " batch transaction: end " ) ;
}
void BlockchainLMDB : : batch_abort ( )
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
if ( ! m_batch_transactions )
throw0 ( DB_ERROR ( " batch transactions not enabled " ) ) ;
if ( ! m_batch_active )
throw0 ( DB_ERROR ( " batch transaction not in progress " ) ) ;
check_open ( ) ;
// for destruction of batch transaction
m_write_txn = nullptr ;
// explicitly call in case mdb_env_close() (BlockchainLMDB::close()) called before BlockchainLMDB destructor called.
m_write_batch_txn . abort ( ) ;
m_batch_active = false ;
LOG_PRINT_L3 ( " batch transaction: aborted " ) ;
}
void BlockchainLMDB : : set_batch_transactions ( bool batch_transactions )
{
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
m_batch_transactions = batch_transactions ;
LOG_PRINT_L3 ( " batch transactions " < < ( m_batch_transactions ? " enabled " : " disabled " ) ) ;
}
2014-10-23 22:37:10 +03:00
uint64_t BlockchainLMDB : : add_block ( const block & blk
, const size_t & block_size
, const difficulty_type & cumulative_difficulty
, const uint64_t & coins_generated
, const std : : vector < transaction > & txs
)
{
2014-10-30 06:58:14 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2014-10-23 22:37:10 +03:00
check_open ( ) ;
2015-02-12 01:55:53 +02:00
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
{
if ( mdb_txn_begin ( m_env , NULL , 0 , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
m_write_txn = & txn ;
}
2014-10-23 22:37:10 +03:00
2014-10-24 02:47:36 +03:00
uint64_t num_outputs = m_num_outputs ;
try
{
BlockchainDB : : add_block ( blk , block_size , cumulative_difficulty , coins_generated , txs ) ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
{
m_write_txn = NULL ;
2014-10-23 22:37:10 +03:00
2015-02-12 01:55:53 +02:00
TIME_MEASURE_START ( time1 ) ;
2015-02-12 01:55:53 +02:00
txn . commit ( ) ;
2015-02-12 01:55:53 +02:00
TIME_MEASURE_FINISH ( time1 ) ;
time_commit1 + = time1 ;
2015-02-12 01:55:53 +02:00
}
2014-10-24 02:47:36 +03:00
}
catch ( . . . )
{
m_num_outputs = num_outputs ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
m_write_txn = NULL ;
2014-10-24 02:47:36 +03:00
throw ;
}
2014-10-23 22:37:10 +03:00
2014-10-24 02:47:36 +03:00
return + + m_height ;
2014-10-23 22:37:10 +03:00
}
2014-10-31 00:33:35 +02:00
void BlockchainLMDB : : pop_block ( block & blk , std : : vector < transaction > & txs )
{
2015-02-12 01:55:53 +02:00
LOG_PRINT_L3 ( " BlockchainLMDB:: " < < __func__ ) ;
2015-02-19 06:52:44 +02:00
check_open ( ) ;
2015-03-14 23:24:51 +02:00
mdb_txn_safe txn ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
{
if ( mdb_txn_begin ( m_env , NULL , 0 , txn ) )
throw0 ( DB_ERROR ( " Failed to create a transaction for the db " ) ) ;
m_write_txn = & txn ;
}
2014-10-31 00:33:35 +02:00
uint64_t num_outputs = m_num_outputs ;
try
{
BlockchainDB : : pop_block ( blk , txs ) ;
2015-02-12 01:55:53 +02:00
if ( ! m_batch_active )
{
m_write_txn = NULL ;
2014-10-31 00:33:35 +02:00
2015-02-12 01:55:53 +02:00
txn . commit ( ) ;
}
2014-10-31 00:33:35 +02:00
}
catch ( . . . )
{
m_num_outputs = num_outputs ;
2014-12-07 13:36:44 +02:00
m_write_txn = NULL ;
2014-10-31 00:33:35 +02:00
throw ;
}
- - m_height ;
}
2014-10-21 23:33:43 +03:00
} // namespace cryptonote