2018-02-18 10:47:25 +00:00
// Copyright (c) 2018, 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 <lmdb.h>
# include <boost/algorithm/string.hpp>
# include <boost/range/adaptor/transformed.hpp>
# include <boost/filesystem.hpp>
# include "misc_log_ex.h"
# include "misc_language.h"
# include "wallet_errors.h"
# include "ringdb.h"
# undef MONERO_DEFAULT_LOG_CATEGORY
# define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb"
2018-02-25 19:20:07 +00:00
static const char zerokey [ 8 ] = { 0 } ;
static const MDB_val zerokeyval = { sizeof ( zerokey ) , ( void * ) zerokey } ;
2018-02-18 10:47:25 +00:00
static int compare_hash32 ( const MDB_val * a , const MDB_val * b )
{
uint32_t * va = ( uint32_t * ) a - > mv_data ;
uint32_t * vb = ( uint32_t * ) b - > mv_data ;
for ( int n = 7 ; n > = 0 ; n - - )
{
if ( va [ n ] = = vb [ n ] )
continue ;
return va [ n ] < vb [ n ] ? - 1 : 1 ;
}
return 0 ;
}
2018-08-29 16:57:12 +00:00
static int compare_uint64 ( const MDB_val * a , const MDB_val * b )
{
const uint64_t va = * ( const uint64_t * ) a - > mv_data ;
const uint64_t vb = * ( const uint64_t * ) b - > mv_data ;
return va < vb ? - 1 : va > vb ;
}
2018-02-18 10:47:25 +00:00
static std : : string compress_ring ( const std : : vector < uint64_t > & ring )
{
std : : string s ;
for ( uint64_t out : ring )
s + = tools : : get_varint_data ( out ) ;
return s ;
}
static std : : vector < uint64_t > decompress_ring ( const std : : string & s )
{
std : : vector < uint64_t > ring ;
int read = 0 ;
for ( std : : string : : const_iterator i = s . begin ( ) ; i ! = s . cend ( ) ; std : : advance ( i , read ) )
{
uint64_t out ;
std : : string tmp ( i , s . cend ( ) ) ;
read = tools : : read_varint ( tmp . begin ( ) , tmp . end ( ) , out ) ;
THROW_WALLET_EXCEPTION_IF ( read < = 0 | | read > 256 , tools : : error : : wallet_internal_error , " Internal error decompressing ring " ) ;
ring . push_back ( out ) ;
}
return ring ;
}
std : : string get_rings_filename ( boost : : filesystem : : path filename )
{
if ( ! boost : : filesystem : : is_directory ( filename ) )
filename . remove_filename ( ) ;
return filename . string ( ) ;
}
static crypto : : chacha_iv make_iv ( const crypto : : key_image & key_image , const crypto : : chacha_key & key )
{
static const char salt [ ] = " ringdsb " ;
uint8_t buffer [ sizeof ( key_image ) + sizeof ( key ) + sizeof ( salt ) ] ;
memcpy ( buffer , & key_image , sizeof ( key_image ) ) ;
memcpy ( buffer + sizeof ( key_image ) , & key , sizeof ( key ) ) ;
memcpy ( buffer + sizeof ( key_image ) + sizeof ( key ) , salt , sizeof ( salt ) ) ;
crypto : : hash hash ;
crypto : : cn_fast_hash ( buffer , sizeof ( buffer ) , hash . data ) ;
static_assert ( sizeof ( hash ) > = CHACHA_IV_SIZE , " Incompatible hash and chacha IV sizes " ) ;
crypto : : chacha_iv iv ;
memcpy ( & iv , & hash , CHACHA_IV_SIZE ) ;
return iv ;
}
static std : : string encrypt ( const std : : string & plaintext , const crypto : : key_image & key_image , const crypto : : chacha_key & key )
{
const crypto : : chacha_iv iv = make_iv ( key_image , key ) ;
std : : string ciphertext ;
ciphertext . resize ( plaintext . size ( ) + sizeof ( iv ) ) ;
crypto : : chacha20 ( plaintext . data ( ) , plaintext . size ( ) , key , iv , & ciphertext [ sizeof ( iv ) ] ) ;
memcpy ( & ciphertext [ 0 ] , & iv , sizeof ( iv ) ) ;
return ciphertext ;
}
static std : : string encrypt ( const crypto : : key_image & key_image , const crypto : : chacha_key & key )
{
return encrypt ( std : : string ( ( const char * ) & key_image , sizeof ( key_image ) ) , key_image , key ) ;
}
static std : : string decrypt ( const std : : string & ciphertext , const crypto : : key_image & key_image , const crypto : : chacha_key & key )
{
const crypto : : chacha_iv iv = make_iv ( key_image , key ) ;
std : : string plaintext ;
THROW_WALLET_EXCEPTION_IF ( ciphertext . size ( ) < sizeof ( iv ) , tools : : error : : wallet_internal_error , " Bad ciphertext text " ) ;
plaintext . resize ( ciphertext . size ( ) - sizeof ( iv ) ) ;
crypto : : chacha20 ( ciphertext . data ( ) + sizeof ( iv ) , ciphertext . size ( ) - sizeof ( iv ) , key , iv , & plaintext [ 0 ] ) ;
return plaintext ;
}
2018-03-10 14:18:33 +00:00
static void store_relative_ring ( MDB_txn * txn , MDB_dbi & dbi , const crypto : : key_image & key_image , const std : : vector < uint64_t > & relative_ring , const crypto : : chacha_key & chacha_key )
{
MDB_val key , data ;
std : : string key_ciphertext = encrypt ( key_image , chacha_key ) ;
key . mv_data = ( void * ) key_ciphertext . data ( ) ;
key . mv_size = key_ciphertext . size ( ) ;
std : : string compressed_ring = compress_ring ( relative_ring ) ;
std : : string data_ciphertext = encrypt ( compressed_ring , key_image , chacha_key ) ;
data . mv_size = data_ciphertext . size ( ) ;
data . mv_data = ( void * ) data_ciphertext . c_str ( ) ;
int dbr = mdb_put ( txn , dbi , & key , & data , 0 ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to set ring for key image in LMDB table: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
}
2018-02-25 19:20:07 +00:00
static int resize_env ( MDB_env * env , const char * db_path , size_t needed )
2018-02-18 10:47:25 +00:00
{
MDB_envinfo mei ;
MDB_stat mst ;
int ret ;
2018-08-12 21:41:44 +00:00
needed = std : : max ( needed , ( size_t ) ( 100ul * 1024 * 1024 ) ) ; // at least 100 MB
2018-02-25 19:20:07 +00:00
2018-02-18 10:47:25 +00:00
ret = mdb_env_info ( env , & mei ) ;
if ( ret )
return ret ;
ret = mdb_env_stat ( env , & mst ) ;
if ( ret )
return ret ;
uint64_t size_used = mst . ms_psize * mei . me_last_pgno ;
uint64_t mapsize = mei . me_mapsize ;
if ( size_used + needed > mei . me_mapsize )
{
try
{
boost : : filesystem : : path path ( db_path ) ;
boost : : filesystem : : space_info si = boost : : filesystem : : space ( path ) ;
if ( si . available < needed )
{
MERROR ( " !! WARNING: Insufficient free space to extend database !!: " < < ( si . available > > 20L ) < < " MB available " ) ;
return ENOSPC ;
}
}
catch ( . . . )
{
// print something but proceed.
MWARNING ( " Unable to query free disk space. " ) ;
}
mapsize + = needed ;
}
return mdb_env_set_mapsize ( env , mapsize ) ;
}
2018-02-25 19:20:07 +00:00
static size_t get_ring_data_size ( size_t n_entries )
{
return n_entries * ( 32 + 1024 ) ; // highball 1kB for the ring data to make sure
}
enum { BLACKBALL_BLACKBALL , BLACKBALL_UNBLACKBALL , BLACKBALL_QUERY , BLACKBALL_CLEAR } ;
2018-02-27 08:30:59 +00:00
namespace tools
{
2018-03-04 13:30:40 +00:00
ringdb : : ringdb ( std : : string filename , const std : : string & genesis ) :
2018-04-09 17:42:22 +00:00
filename ( filename ) ,
env ( NULL )
2018-02-25 19:20:07 +00:00
{
MDB_txn * txn ;
bool tx_active = false ;
2018-02-27 08:30:59 +00:00
int dbr ;
2018-02-25 19:20:07 +00:00
tools : : create_directories_if_necessary ( filename ) ;
dbr = mdb_env_create ( & env ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create LDMB environment: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
2018-02-27 08:30:59 +00:00
dbr = mdb_env_set_maxdbs ( env , 2 ) ;
2018-02-25 19:20:07 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to set max env dbs: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
2018-02-27 08:30:59 +00:00
const std : : string actual_filename = get_rings_filename ( filename ) ;
dbr = mdb_env_open ( env , actual_filename . c_str ( ) , 0 , 0664 ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to open rings database file ' "
+ actual_filename + " ': " + std : : string ( mdb_strerror ( dbr ) ) ) ;
2018-02-25 19:20:07 +00:00
dbr = mdb_txn_begin ( env , NULL , 0 , & txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create LMDB transaction: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
epee : : misc_utils : : auto_scope_leave_caller txn_dtor = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { if ( tx_active ) mdb_txn_abort ( txn ) ; } ) ;
tx_active = true ;
2018-03-04 13:30:40 +00:00
dbr = mdb_dbi_open ( txn , ( " rings- " + genesis ) . c_str ( ) , MDB_CREATE , & dbi_rings ) ;
2018-02-27 08:30:59 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to open LMDB dbi: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
mdb_set_compare ( txn , dbi_rings , compare_hash32 ) ;
2018-02-25 19:20:07 +00:00
2018-08-29 16:57:12 +00:00
dbr = mdb_dbi_open ( txn , ( " blackballs2- " + genesis ) . c_str ( ) , MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED , & dbi_blackballs ) ;
2018-02-27 08:30:59 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to open LMDB dbi: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
2018-08-29 16:57:12 +00:00
mdb_set_dupsort ( txn , dbi_blackballs , compare_uint64 ) ;
2018-02-25 19:20:07 +00:00
dbr = mdb_txn_commit ( txn ) ;
2018-02-27 08:30:59 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to commit txn creating/opening database: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
2018-02-25 19:20:07 +00:00
tx_active = false ;
}
2018-02-27 08:30:59 +00:00
ringdb : : ~ ringdb ( )
2018-02-18 10:47:25 +00:00
{
2018-04-09 17:42:22 +00:00
close ( ) ;
}
void ringdb : : close ( )
{
if ( env )
{
mdb_dbi_close ( env , dbi_rings ) ;
mdb_dbi_close ( env , dbi_blackballs ) ;
mdb_env_close ( env ) ;
env = NULL ;
}
2018-02-27 08:30:59 +00:00
}
2018-02-18 10:47:25 +00:00
2018-02-27 08:30:59 +00:00
bool ringdb : : add_rings ( const crypto : : chacha_key & chacha_key , const cryptonote : : transaction_prefix & tx )
2018-02-18 10:47:25 +00:00
{
MDB_txn * txn ;
int dbr ;
bool tx_active = false ;
2018-02-25 19:20:07 +00:00
dbr = resize_env ( env , filename . c_str ( ) , get_ring_data_size ( tx . vin . size ( ) ) ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to set env map size " ) ;
2018-02-18 10:47:25 +00:00
dbr = mdb_txn_begin ( env , NULL , 0 , & txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create LMDB transaction: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
epee : : misc_utils : : auto_scope_leave_caller txn_dtor = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { if ( tx_active ) mdb_txn_abort ( txn ) ; } ) ;
tx_active = true ;
for ( const auto & in : tx . vin )
{
if ( in . type ( ) ! = typeid ( cryptonote : : txin_to_key ) )
continue ;
const auto & txin = boost : : get < cryptonote : : txin_to_key > ( in ) ;
const uint32_t ring_size = txin . key_offsets . size ( ) ;
if ( ring_size = = 1 )
continue ;
2018-03-10 14:18:33 +00:00
store_relative_ring ( txn , dbi_rings , txin . k_image , txin . key_offsets , chacha_key ) ;
2018-02-18 10:47:25 +00:00
}
dbr = mdb_txn_commit ( txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to commit txn adding ring to database: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
tx_active = false ;
return true ;
}
2018-02-27 08:30:59 +00:00
bool ringdb : : remove_rings ( const crypto : : chacha_key & chacha_key , const cryptonote : : transaction_prefix & tx )
2018-02-18 10:47:25 +00:00
{
MDB_txn * txn ;
int dbr ;
bool tx_active = false ;
2018-02-25 19:20:07 +00:00
dbr = resize_env ( env , filename . c_str ( ) , 0 ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to set env map size " ) ;
2018-02-18 10:47:25 +00:00
dbr = mdb_txn_begin ( env , NULL , 0 , & txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create LMDB transaction: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
epee : : misc_utils : : auto_scope_leave_caller txn_dtor = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { if ( tx_active ) mdb_txn_abort ( txn ) ; } ) ;
tx_active = true ;
for ( const auto & in : tx . vin )
{
if ( in . type ( ) ! = typeid ( cryptonote : : txin_to_key ) )
continue ;
const auto & txin = boost : : get < cryptonote : : txin_to_key > ( in ) ;
const uint32_t ring_size = txin . key_offsets . size ( ) ;
if ( ring_size = = 1 )
continue ;
MDB_val key , data ;
std : : string key_ciphertext = encrypt ( txin . k_image , chacha_key ) ;
key . mv_data = ( void * ) key_ciphertext . data ( ) ;
key . mv_size = key_ciphertext . size ( ) ;
2018-02-27 08:30:59 +00:00
dbr = mdb_get ( txn , dbi_rings , & key , & data ) ;
2018-02-18 10:47:25 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr & & dbr ! = MDB_NOTFOUND , tools : : error : : wallet_internal_error , " Failed to look for key image in LMDB table: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
if ( dbr = = MDB_NOTFOUND )
continue ;
THROW_WALLET_EXCEPTION_IF ( data . mv_size < = 0 , tools : : error : : wallet_internal_error , " Invalid ring data size " ) ;
MDEBUG ( " Removing ring data for key image " < < txin . k_image ) ;
2018-02-27 08:30:59 +00:00
dbr = mdb_del ( txn , dbi_rings , & key , NULL ) ;
2018-02-18 10:47:25 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to remove ring to database: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
}
dbr = mdb_txn_commit ( txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to commit txn removing ring to database: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
tx_active = false ;
return true ;
}
2018-02-27 08:30:59 +00:00
bool ringdb : : get_ring ( const crypto : : chacha_key & chacha_key , const crypto : : key_image & key_image , std : : vector < uint64_t > & outs )
2018-02-18 10:47:25 +00:00
{
MDB_txn * txn ;
int dbr ;
bool tx_active = false ;
dbr = resize_env ( env , filename . c_str ( ) , 0 ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to set env map size: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
dbr = mdb_txn_begin ( env , NULL , 0 , & txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create LMDB transaction: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
epee : : misc_utils : : auto_scope_leave_caller txn_dtor = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { if ( tx_active ) mdb_txn_abort ( txn ) ; } ) ;
tx_active = true ;
MDB_val key , data ;
std : : string key_ciphertext = encrypt ( key_image , chacha_key ) ;
key . mv_data = ( void * ) key_ciphertext . data ( ) ;
key . mv_size = key_ciphertext . size ( ) ;
2018-02-27 08:30:59 +00:00
dbr = mdb_get ( txn , dbi_rings , & key , & data ) ;
2018-02-18 10:47:25 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr & & dbr ! = MDB_NOTFOUND , tools : : error : : wallet_internal_error , " Failed to look for key image in LMDB table: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
if ( dbr = = MDB_NOTFOUND )
return false ;
THROW_WALLET_EXCEPTION_IF ( data . mv_size < = 0 , tools : : error : : wallet_internal_error , " Invalid ring data size " ) ;
std : : string data_plaintext = decrypt ( std : : string ( ( const char * ) data . mv_data , data . mv_size ) , key_image , chacha_key ) ;
outs = decompress_ring ( data_plaintext ) ;
MDEBUG ( " Found ring for key image " < < key_image < < " : " ) ;
MDEBUG ( " Relative: " < < boost : : join ( outs | boost : : adaptors : : transformed ( [ ] ( uint64_t out ) { return std : : to_string ( out ) ; } ) , " " ) ) ;
outs = cryptonote : : relative_output_offsets_to_absolute ( outs ) ;
MDEBUG ( " Absolute: " < < boost : : join ( outs | boost : : adaptors : : transformed ( [ ] ( uint64_t out ) { return std : : to_string ( out ) ; } ) , " " ) ) ;
dbr = mdb_txn_commit ( txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to commit txn getting ring from database: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
tx_active = false ;
return true ;
}
2018-02-28 18:26:06 +00:00
bool ringdb : : set_ring ( const crypto : : chacha_key & chacha_key , const crypto : : key_image & key_image , const std : : vector < uint64_t > & outs , bool relative )
{
MDB_txn * txn ;
int dbr ;
bool tx_active = false ;
dbr = resize_env ( env , filename . c_str ( ) , outs . size ( ) * 64 ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to set env map size: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
dbr = mdb_txn_begin ( env , NULL , 0 , & txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create LMDB transaction: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
epee : : misc_utils : : auto_scope_leave_caller txn_dtor = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { if ( tx_active ) mdb_txn_abort ( txn ) ; } ) ;
tx_active = true ;
2018-03-10 14:18:33 +00:00
store_relative_ring ( txn , dbi_rings , key_image , relative ? outs : cryptonote : : absolute_output_offsets_to_relative ( outs ) , chacha_key ) ;
2018-02-28 18:26:06 +00:00
dbr = mdb_txn_commit ( txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to commit txn setting ring to database: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
tx_active = false ;
return true ;
}
2018-08-29 16:57:12 +00:00
bool ringdb : : blackball_worker ( const std : : vector < std : : pair < uint64_t , uint64_t > > & outputs , int op )
2018-02-25 19:20:07 +00:00
{
2018-02-27 08:30:59 +00:00
MDB_txn * txn ;
MDB_cursor * cursor ;
int dbr ;
bool tx_active = false ;
bool ret = true ;
2018-08-12 21:41:44 +00:00
THROW_WALLET_EXCEPTION_IF ( outputs . size ( ) > 1 & & op = = BLACKBALL_QUERY , tools : : error : : wallet_internal_error , " Blackball query only makes sense for a single output " ) ;
dbr = resize_env ( env , filename . c_str ( ) , 32 * 2 * outputs . size ( ) ) ; // a pubkey, and some slack
2018-02-27 08:30:59 +00:00
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to set env map size: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
dbr = mdb_txn_begin ( env , NULL , 0 , & txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create LMDB transaction: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
epee : : misc_utils : : auto_scope_leave_caller txn_dtor = epee : : misc_utils : : create_scope_leave_handler ( [ & ] ( ) { if ( tx_active ) mdb_txn_abort ( txn ) ; } ) ;
tx_active = true ;
2018-09-29 20:10:48 +00:00
dbr = mdb_cursor_open ( txn , dbi_blackballs , & cursor ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to create cursor for blackballs table: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
2018-02-27 08:30:59 +00:00
2018-08-29 16:57:12 +00:00
MDB_val key , data ;
for ( const std : : pair < uint64_t , uint64_t > & output : outputs )
2018-08-12 21:41:44 +00:00
{
2018-08-29 16:57:12 +00:00
key . mv_data = ( void * ) & output . first ;
key . mv_size = sizeof ( output . first ) ;
data . mv_data = ( void * ) & output . second ;
data . mv_size = sizeof ( output . second ) ;
2018-08-12 21:41:44 +00:00
switch ( op )
{
case BLACKBALL_BLACKBALL :
2018-10-18 18:05:51 +00:00
MDEBUG ( " Marking output " < < output . first < < " / " < < output . second < < " as spent " ) ;
2018-09-29 20:10:48 +00:00
dbr = mdb_cursor_put ( cursor , & key , & data , MDB_APPENDDUP ) ;
2018-08-12 21:41:44 +00:00
if ( dbr = = MDB_KEYEXIST )
dbr = 0 ;
break ;
case BLACKBALL_UNBLACKBALL :
2018-10-18 18:05:51 +00:00
MDEBUG ( " Marking output " < < output . first < < " / " < < output . second < < " as unspent " ) ;
2018-09-29 20:10:48 +00:00
dbr = mdb_cursor_get ( cursor , & key , & data , MDB_GET_BOTH ) ;
if ( dbr = = 0 )
dbr = mdb_cursor_del ( cursor , 0 ) ;
2018-08-12 21:41:44 +00:00
break ;
case BLACKBALL_QUERY :
dbr = mdb_cursor_get ( cursor , & key , & data , MDB_GET_BOTH ) ;
THROW_WALLET_EXCEPTION_IF ( dbr & & dbr ! = MDB_NOTFOUND , tools : : error : : wallet_internal_error , " Failed to lookup in blackballs table: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
ret = dbr ! = MDB_NOTFOUND ;
if ( dbr = = MDB_NOTFOUND )
dbr = 0 ;
break ;
case BLACKBALL_CLEAR :
break ;
default :
THROW_WALLET_EXCEPTION ( tools : : error : : wallet_internal_error , " Invalid blackball op " ) ;
}
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to query blackballs table: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
}
2018-09-29 20:10:48 +00:00
mdb_cursor_close ( cursor ) ;
2018-08-12 21:41:44 +00:00
if ( op = = BLACKBALL_CLEAR )
2018-02-27 08:30:59 +00:00
{
2018-08-12 21:41:44 +00:00
dbr = mdb_drop ( txn , dbi_blackballs , 0 ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to clear blackballs table: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
2018-02-27 08:30:59 +00:00
}
dbr = mdb_txn_commit ( txn ) ;
THROW_WALLET_EXCEPTION_IF ( dbr , tools : : error : : wallet_internal_error , " Failed to commit txn blackballing output to database: " + std : : string ( mdb_strerror ( dbr ) ) ) ;
tx_active = false ;
return ret ;
2018-02-25 19:20:07 +00:00
}
2018-08-29 16:57:12 +00:00
bool ringdb : : blackball ( const std : : vector < std : : pair < uint64_t , uint64_t > > & outputs )
2018-08-12 21:41:44 +00:00
{
return blackball_worker ( outputs , BLACKBALL_BLACKBALL ) ;
}
2018-08-29 16:57:12 +00:00
bool ringdb : : blackball ( const std : : pair < uint64_t , uint64_t > & output )
2018-02-25 19:20:07 +00:00
{
2018-08-29 16:57:12 +00:00
std : : vector < std : : pair < uint64_t , uint64_t > > outputs ( 1 , output ) ;
2018-08-12 21:41:44 +00:00
return blackball_worker ( outputs , BLACKBALL_BLACKBALL ) ;
2018-02-25 19:20:07 +00:00
}
2018-08-29 16:57:12 +00:00
bool ringdb : : unblackball ( const std : : pair < uint64_t , uint64_t > & output )
2018-02-25 19:20:07 +00:00
{
2018-08-29 16:57:12 +00:00
std : : vector < std : : pair < uint64_t , uint64_t > > outputs ( 1 , output ) ;
2018-08-12 21:41:44 +00:00
return blackball_worker ( outputs , BLACKBALL_UNBLACKBALL ) ;
2018-02-25 19:20:07 +00:00
}
2018-08-29 16:57:12 +00:00
bool ringdb : : blackballed ( const std : : pair < uint64_t , uint64_t > & output )
2018-02-25 19:20:07 +00:00
{
2018-08-29 16:57:12 +00:00
std : : vector < std : : pair < uint64_t , uint64_t > > outputs ( 1 , output ) ;
2018-08-12 21:41:44 +00:00
return blackball_worker ( outputs , BLACKBALL_QUERY ) ;
2018-02-25 19:20:07 +00:00
}
2018-02-27 08:30:59 +00:00
bool ringdb : : clear_blackballs ( )
{
2018-08-29 16:57:12 +00:00
return blackball_worker ( std : : vector < std : : pair < uint64_t , uint64_t > > ( ) , BLACKBALL_CLEAR ) ;
2018-02-27 08:30:59 +00:00
}
}