30#include <boost/algorithm/string.hpp>
31#include <boost/range/adaptor/transformed.hpp>
32#include <boost/filesystem.hpp>
39#undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
40#define ELECTRONEUM_DEFAULT_LOG_CATEGORY "wallet.ringdb"
42static const char zerokey[8] = {0};
43static const MDB_val zerokeyval = {
sizeof(zerokey), (
void *)zerokey };
47 for (
int n = 7; n >= 0; n--)
54 return va < vb ? -1 : 1;
63 memcpy(&va,
a->mv_data,
sizeof(va));
65 return va < vb ? -1 : va > vb;
68static std::string compress_ring(
const std::vector<uint64_t> &ring)
76static std::vector<uint64_t> decompress_ring(
const std::string &s)
78 std::vector<uint64_t> ring;
80 for (std::string::const_iterator i = s.begin(); i != s.cend(); std::advance(i, read))
83 std::string tmp(i, s.cend());
93 if (!boost::filesystem::is_directory(filename))
94 filename.remove_filename();
95 return filename.string();
98static crypto::chacha_iv make_iv(
const crypto::key_image &key_image,
const crypto::chacha_key &
key)
100 static const char salt[] =
"ringdsb";
103 memcpy(buffer, &key_image,
sizeof(key_image));
105 memcpy(buffer +
sizeof(key_image) +
sizeof(
key), salt,
sizeof(salt));
108 static_assert(
sizeof(
hash) >=
CHACHA_IV_SIZE,
"Incompatible hash and chacha IV sizes");
109 crypto::chacha_iv iv;
114static std::string encrypt(
const std::string &plaintext,
const crypto::key_image &key_image,
const crypto::chacha_key &
key)
116 const crypto::chacha_iv iv = make_iv(key_image,
key);
117 std::string ciphertext;
118 ciphertext.resize(plaintext.size() +
sizeof(iv));
119 crypto::chacha20(plaintext.data(), plaintext.size(),
key, iv, &ciphertext[
sizeof(iv)]);
120 memcpy(&ciphertext[0], &iv,
sizeof(iv));
126 return encrypt(std::string((
const char*)&key_image,
sizeof(key_image)), key_image,
key);
129static std::string decrypt(
const std::string &ciphertext,
const crypto::key_image &key_image,
const crypto::chacha_key &
key)
131 const crypto::chacha_iv iv = make_iv(key_image,
key);
132 std::string plaintext;
134 plaintext.resize(ciphertext.size() -
sizeof(iv));
135 crypto::chacha20(ciphertext.data() +
sizeof(iv), ciphertext.size() -
sizeof(iv),
key, iv, &plaintext[0]);
139static 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)
142 std::string key_ciphertext = encrypt(key_image, chacha_key);
143 key.mv_data = (
void*)key_ciphertext.data();
144 key.mv_size = key_ciphertext.size();
145 std::string compressed_ring = compress_ring(relative_ring);
146 std::string data_ciphertext = encrypt(compressed_ring, key_image, chacha_key);
147 data.
mv_size = data_ciphertext.size();
148 data.
mv_data = (
void*)data_ciphertext.c_str();
153static int resize_env(
MDB_env *env,
const char *db_path,
size_t needed)
159 needed = std::max(needed, (
size_t)(100ul * 1024 * 1024));
173 boost::filesystem::path path(db_path);
174 boost::filesystem::space_info si = boost::filesystem::space(path);
175 if(si.available < needed)
177 MERROR(
"!! WARNING: Insufficient free space to extend database !!: " << (si.available >> 20L) <<
" MB available");
184 MWARNING(
"Unable to query free disk space.");
192static size_t get_ring_data_size(
size_t n_entries)
194 return n_entries * (32 + 1024);
207 bool tx_active =
false;
217 dbr =
mdb_env_open(env, actual_filename.c_str(), 0, 0664);
219 + actual_filename +
"': " + std::string(
mdb_strerror(dbr)));
259 bool tx_active =
false;
261 dbr = resize_env(env, filename.c_str(), get_ring_data_size(tx.
vin.size()));
268 for (
const auto &in: tx.
vin)
272 const auto &txin = boost::get<cryptonote::txin_to_key>(in);
273 const uint32_t ring_size = txin.key_offsets.size();
277 store_relative_ring(txn, dbi_rings, txin.k_image, txin.key_offsets, chacha_key);
286bool ringdb::remove_rings(
const crypto::chacha_key &chacha_key,
const std::vector<crypto::key_image> &key_images)
290 bool tx_active =
false;
292 dbr = resize_env(env, filename.c_str(), 0);
302 std::string key_ciphertext = encrypt(key_image, chacha_key);
303 key.mv_data = (
void*)key_ciphertext.data();
304 key.mv_size = key_ciphertext.size();
312 MDEBUG(
"Removing ring data for key image " << key_image);
325 std::vector<crypto::key_image> key_images;
326 key_images.reserve(tx.
vin.size());
327 for (
const auto &in: tx.
vin)
331 const auto &txin = boost::get<cryptonote::txin_to_key>(in);
332 const uint32_t ring_size = txin.key_offsets.size();
335 key_images.push_back(txin.k_image);
344 bool tx_active =
false;
346 dbr = resize_env(env, filename.c_str(), 0);
354 std::string key_ciphertext = encrypt(key_image, chacha_key);
355 key.mv_data = (
void*)key_ciphertext.data();
356 key.mv_size = key_ciphertext.size();
363 std::string data_plaintext = decrypt(std::string((
const char*)data.
mv_data, data.
mv_size), key_image, chacha_key);
364 outs = decompress_ring(data_plaintext);
365 MDEBUG(
"Found ring for key image " << key_image <<
":");
366 MDEBUG(
"Relative: " << boost::join(outs | boost::adaptors::transformed([](
uint64_t out){
return std::to_string(out);}),
" "));
368 MDEBUG(
"Absolute: " << boost::join(outs | boost::adaptors::transformed([](
uint64_t out){
return std::to_string(out);}),
" "));
380 bool tx_active =
false;
382 dbr = resize_env(env, filename.c_str(), outs.size() * 64);
397bool ringdb::blackball_worker(
const std::vector<std::pair<uint64_t, uint64_t>> &outputs,
int op)
402 bool tx_active =
false;
407 dbr = resize_env(env, filename.c_str(), 32 * 2 * outputs.size());
418 for (
const std::pair<uint64_t, uint64_t> &output: outputs)
420 key.mv_data = (
void*)&output.first;
421 key.mv_size =
sizeof(output.first);
422 data.
mv_data = (
void*)&output.second;
423 data.
mv_size =
sizeof(output.second);
428 MDEBUG(
"Marking output " << output.first <<
"/" << output.second <<
" as spent");
434 MDEBUG(
"Marking output " << output.first <<
"/" << output.second <<
" as unspent");
458 dbr =
mdb_drop(txn, dbi_blackballs, 0);
475 std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
481 std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
487 std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output);
493 return blackball_worker(std::vector<std::pair<uint64_t, uint64_t>>(),
BLACKBALL_CLEAR);
int compare_uint64(const MDB_val *a, const MDB_val *b)
std::vector< txin_v > vin
void * memcpy(void *a, const void *b, size_t c)
int mdb_env_info(MDB_env *env, MDB_envinfo *stat)
Return information about the LMDB environment.
int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data, unsigned int flags)
Store by cursor.
int mdb_cursor_del(MDB_cursor *cursor, unsigned int flags)
Delete current key/data pair.
int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
Open an environment handle.
void mdb_env_close(MDB_env *env)
Close the environment and release the memory map.
int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data, MDB_cursor_op op)
Retrieve by cursor.
int mdb_env_set_mapsize(MDB_env *env, mdb_size_t size)
Set the size of the memory map to use for this environment.
int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned int flags)
Store items into a database.
void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
Close a database handle. Normally unnecessary. Use with care:
int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
Set a custom key comparison function for a database.
void mdb_txn_abort(MDB_txn *txn)
Abandon all the operations of the transaction instead of saving them.
int mdb_txn_commit(MDB_txn *txn)
Commit all the operations of a transaction into the database.
int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
Get items from a database.
char * mdb_strerror(int err)
Return a string describing a given error code.
int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor)
Create a cursor handle.
int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
Set the maximum number of named databases for the environment.
int mdb_env_create(MDB_env **env)
Create an LMDB environment handle.
int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data)
Delete items from a database.
int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
Empty or delete+close a database.
int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi)
Open a database in the environment.
int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
Set a custom data comparison function for a MDB_DUPSORT database.
void mdb_cursor_close(MDB_cursor *cursor)
Close a cursor handle.
int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn)
Create a transaction for use with the environment.
int mdb_env_stat(MDB_env *env, MDB_stat *stat)
Return statistics about the LMDB environment.
struct MDB_env MDB_env
Opaque structure for a database environment.
struct MDB_txn MDB_txn
Opaque structure for a transaction handle.
unsigned int MDB_dbi
A handle for an individual database in the DB environment.
struct MDB_cursor MDB_cursor
Opaque structure for navigating through a database.
Lightning memory-mapped database library.
void cn_fast_hash(const void *data, size_t length, char *hash)
std::vector< uint64_t > absolute_output_offsets_to_relative(const std::vector< uint64_t > &off)
std::vector< uint64_t > relative_output_offsets_to_absolute(const std::vector< uint64_t > &off)
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
std::string get_rings_filename(boost::filesystem::path filename)
unsigned __int64 uint64_t
Information about the environment.
Statistics for a database in the environment.
Generic structure used for passing keys and data in and out of the database.
#define THROW_WALLET_EXCEPTION_IF(cond, err_type,...)
#define THROW_WALLET_EXCEPTION(err_type,...)