wallet: make ringdb an object with database state

This commit is contained in:
moneromooo-monero 2018-02-27 08:30:59 +00:00
parent df6fad4c62
commit db10dd6d83
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
6 changed files with 189 additions and 172 deletions

View file

@ -310,6 +310,7 @@ int main(int argc, char* argv[])
std::unordered_map<crypto::key_image, std::vector<uint64_t>> relative_rings; std::unordered_map<crypto::key_image, std::vector<uint64_t>> relative_rings;
std::unordered_map<output_data, std::unordered_set<crypto::key_image>> outputs; std::unordered_map<output_data, std::unordered_set<crypto::key_image>> outputs;
std::unordered_set<output_data> spent, newly_spent; std::unordered_set<output_data> spent, newly_spent;
tools::ringdb ringdb(output_file_path.string());
for (size_t n = 0; n < inputs.size(); ++n) for (size_t n = 0; n < inputs.size(); ++n)
{ {
@ -335,7 +336,7 @@ int main(int argc, char* argv[])
{ {
const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, txin.key_offsets[0]); const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, txin.key_offsets[0]);
MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring");
tools::ringdb::blackball(output_file_path.string(), pkey); ringdb.blackball(pkey);
newly_spent.insert(output_data(txin.amount, txin.key_offsets[0])); newly_spent.insert(output_data(txin.amount, txin.key_offsets[0]));
spent.insert(output_data(txin.amount, txin.key_offsets[0])); spent.insert(output_data(txin.amount, txin.key_offsets[0]));
} }
@ -363,7 +364,7 @@ int main(int argc, char* argv[])
{ {
const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, common[0]); const crypto::public_key pkey = core_storage[n]->get_output_key(txin.amount, common[0]);
MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element");
tools::ringdb::blackball(output_file_path.string(), pkey); ringdb.blackball(pkey);
newly_spent.insert(output_data(txin.amount, common[0])); newly_spent.insert(output_data(txin.amount, common[0]));
spent.insert(output_data(txin.amount, common[0])); spent.insert(output_data(txin.amount, common[0]));
} }
@ -407,8 +408,9 @@ int main(int argc, char* argv[])
if (known == absolute.size() - 1) if (known == absolute.size() - 1)
{ {
const crypto::public_key pkey = core_storage[0]->get_output_key(od.amount, last_unknown); const crypto::public_key pkey = core_storage[0]->get_output_key(od.amount, last_unknown);
MINFO("Blackballing output " << pkey << ", due to being used in rings where all other outputs are known to be spent"); MINFO("Blackballing output " << pkey << ", due to being used in a " <<
tools::ringdb::blackball(output_file_path.string(), pkey); absolute.size() << "-ring where all other outputs are known to be spent");
ringdb.blackball(pkey);
newly_spent.insert(output_data(od.amount, last_unknown)); newly_spent.insert(output_data(od.amount, last_unknown));
spent.insert(output_data(od.amount, last_unknown)); spent.insert(output_data(od.amount, last_unknown));
} }

View file

@ -1312,7 +1312,7 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args)
std::vector<uint64_t> ring; std::vector<uint64_t> ring;
try try
{ {
if (m_wallet->get_ring(m_wallet->get_ring_database(), key_image, ring)) if (m_wallet->get_ring(key_image, ring))
{ {
std::stringstream str; std::stringstream str;
for (const auto &x: ring) for (const auto &x: ring)
@ -1466,7 +1466,7 @@ bool simple_wallet::save_known_rings(const std::vector<std::string> &args)
try try
{ {
LOCK_IDLE_SCOPE(); LOCK_IDLE_SCOPE();
m_wallet->find_and_save_rings(m_wallet->get_ring_database()); m_wallet->find_and_save_rings();
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {

View file

@ -172,115 +172,64 @@ static size_t get_ring_data_size(size_t n_entries)
enum { BLACKBALL_BLACKBALL, BLACKBALL_UNBLACKBALL, BLACKBALL_QUERY, BLACKBALL_CLEAR}; enum { BLACKBALL_BLACKBALL, BLACKBALL_UNBLACKBALL, BLACKBALL_QUERY, BLACKBALL_CLEAR};
static bool blackball_worker(const std::string &filename, const crypto::public_key &output, int op) namespace tools
{ {
MDB_env *env;
MDB_dbi dbi;
MDB_txn *txn;
MDB_cursor *cursor;
int dbr;
bool tx_active = false;
bool ret = true;
if (filename.empty()) ringdb::ringdb(std::string filename):
return true; filename(filename)
{
MDB_txn *txn;
bool tx_active = false;
int dbr;
tools::create_directories_if_necessary(filename); tools::create_directories_if_necessary(filename);
dbr = mdb_env_create(&env); 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))); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LDMB environment: " + std::string(mdb_strerror(dbr)));
dbr = mdb_env_set_maxdbs(env, 1); dbr = mdb_env_set_maxdbs(env, 2);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr))); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
dbr = mdb_env_open(env, get_rings_filename(filename).c_str(), 0, 0664); const std::string actual_filename = get_rings_filename(filename);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open rings database file: " + std::string(mdb_strerror(dbr))); dbr = mdb_env_open(env, actual_filename.c_str(), 0, 0664);
epee::misc_utils::auto_scope_leave_caller env_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_env_close(env);}); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open rings database file '"
dbr = resize_env(env, filename.c_str(), 32 * 2); // a pubkey, and some slack + actual_filename + "': " + std::string(mdb_strerror(dbr)));
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); 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))); 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);}); 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; tx_active = true;
dbr = mdb_dbi_open(txn, "blackballs", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi);
dbr = mdb_dbi_open(txn, "rings", MDB_CREATE, &dbi_rings);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller dbi_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_dbi_close(env, dbi);}); mdb_set_compare(txn, dbi_rings, compare_hash32);
mdb_set_dupsort(txn, dbi, compare_hash32);
MDB_val key = zerokeyval; dbr = mdb_dbi_open(txn, "blackballs", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs);
MDB_val data; THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
data.mv_data = (void*)&output; mdb_set_dupsort(txn, dbi_blackballs, compare_hash32);
data.mv_size = sizeof(output);
switch (op)
{
case BLACKBALL_BLACKBALL:
MDEBUG("Blackballing output " << output);
dbr = mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA);
if (dbr == MDB_KEYEXIST)
dbr = 0;
break;
case BLACKBALL_UNBLACKBALL:
MDEBUG("Unblackballing output " << output);
dbr = mdb_del(txn, dbi, &key, &data);
if (dbr == MDB_NOTFOUND)
dbr = 0;
break;
case BLACKBALL_QUERY:
MDEBUG("Querying blackball status for output " << output);
dbr = mdb_cursor_open(txn, dbi, &cursor);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create cursor for blackballs table: " + std::string(mdb_strerror(dbr)));
dbr = mdb_cursor_get(cursor, &key, &data, MDB_GET_BOTH);
MDEBUG("Querying blackball status for output " << output << ": " << std::string(mdb_strerror(dbr)));
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;
mdb_cursor_close(cursor);
break;
case BLACKBALL_CLEAR:
dbr = mdb_drop(txn, dbi, 0);
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)));
dbr = mdb_txn_commit(txn); 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))); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr)));
tx_active = false; tx_active = false;
return ret;
} }
namespace tools { namespace ringdb ringdb::~ringdb()
{ {
mdb_dbi_close(env, dbi_rings);
mdb_dbi_close(env, dbi_blackballs);
mdb_env_close(env);
}
bool add_rings(const std::string &filename, const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx) bool ringdb::add_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx)
{ {
MDB_env *env;
MDB_dbi dbi;
MDB_txn *txn; MDB_txn *txn;
int dbr; int dbr;
bool tx_active = false; bool tx_active = false;
if (filename.empty())
return true;
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)));
dbr = mdb_env_set_maxdbs(env, 1);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
dbr = mdb_env_open(env, get_rings_filename(filename).c_str(), 0, 0664);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open rings database file: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller env_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_env_close(env);});
dbr = resize_env(env, filename.c_str(), get_ring_data_size(tx.vin.size())); 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"); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size");
dbr = mdb_txn_begin(env, NULL, 0, &txn); 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))); 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);}); 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; tx_active = true;
dbr = mdb_dbi_open(txn, "rings", MDB_CREATE, &dbi);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller dbi_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_dbi_close(env, dbi);});
mdb_set_compare(txn, dbi, compare_hash32);
for (const auto &in: tx.vin) for (const auto &in: tx.vin)
{ {
@ -301,7 +250,7 @@ bool add_rings(const std::string &filename, const crypto::chacha_key &chacha_key
std::string data_ciphertext = encrypt(compressed_ring, txin.k_image, chacha_key); std::string data_ciphertext = encrypt(compressed_ring, txin.k_image, chacha_key);
data.mv_size = data_ciphertext.size(); data.mv_size = data_ciphertext.size();
data.mv_data = (void*)data_ciphertext.c_str(); data.mv_data = (void*)data_ciphertext.c_str();
dbr = mdb_put(txn, dbi, &key, &data, 0); dbr = mdb_put(txn, dbi_rings, &key, &data, 0);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to add ring to database: " + std::string(mdb_strerror(dbr))); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to add ring to database: " + std::string(mdb_strerror(dbr)));
} }
@ -311,35 +260,18 @@ bool add_rings(const std::string &filename, const crypto::chacha_key &chacha_key
return true; return true;
} }
bool remove_rings(const std::string &filename, const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx) bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx)
{ {
MDB_env *env;
MDB_dbi dbi;
MDB_txn *txn; MDB_txn *txn;
int dbr; int dbr;
bool tx_active = false; bool tx_active = false;
if (filename.empty())
return true;
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)));
dbr = mdb_env_set_maxdbs(env, 1);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
dbr = mdb_env_open(env, get_rings_filename(filename).c_str(), 0, 0664);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open rings database file: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller env_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_env_close(env);});
dbr = resize_env(env, filename.c_str(), 0); dbr = resize_env(env, filename.c_str(), 0);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size"); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size");
dbr = mdb_txn_begin(env, NULL, 0, &txn); 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))); 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);}); 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; tx_active = true;
dbr = mdb_dbi_open(txn, "rings", MDB_CREATE, &dbi);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller dbi_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_dbi_close(env, dbi);});
mdb_set_compare(txn, dbi, compare_hash32);
for (const auto &in: tx.vin) for (const auto &in: tx.vin)
{ {
@ -355,14 +287,14 @@ bool remove_rings(const std::string &filename, const crypto::chacha_key &chacha_
key.mv_data = (void*)key_ciphertext.data(); key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size(); key.mv_size = key_ciphertext.size();
dbr = mdb_get(txn, dbi, &key, &data); dbr = mdb_get(txn, dbi_rings, &key, &data);
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))); 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) if (dbr == MDB_NOTFOUND)
continue; continue;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size"); 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); MDEBUG("Removing ring data for key image " << txin.k_image);
dbr = mdb_del(txn, dbi, &key, NULL); dbr = mdb_del(txn, dbi_rings, &key, NULL);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to remove ring to database: " + std::string(mdb_strerror(dbr))); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to remove ring to database: " + std::string(mdb_strerror(dbr)));
} }
@ -372,41 +304,24 @@ bool remove_rings(const std::string &filename, const crypto::chacha_key &chacha_
return true; return true;
} }
bool get_ring(const std::string &filename, const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs) bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{ {
MDB_env *env;
MDB_dbi dbi;
MDB_txn *txn; MDB_txn *txn;
int dbr; int dbr;
bool tx_active = false; bool tx_active = false;
if (filename.empty())
return false;
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)));
dbr = mdb_env_set_maxdbs(env, 1);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set max env dbs: " + std::string(mdb_strerror(dbr)));
dbr = mdb_env_open(env, get_rings_filename(filename).c_str(), 0, 0664);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open rings database file: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller env_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_env_close(env);});
dbr = resize_env(env, filename.c_str(), 0); 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))); 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); 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))); 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);}); 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; tx_active = true;
dbr = mdb_dbi_open(txn, "rings", MDB_CREATE, &dbi);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller dbi_dtor = epee::misc_utils::create_scope_leave_handler([&](){mdb_dbi_close(env, dbi);});
mdb_set_compare(txn, dbi, compare_hash32);
MDB_val key, data; MDB_val key, data;
std::string key_ciphertext = encrypt(key_image, chacha_key); std::string key_ciphertext = encrypt(key_image, chacha_key);
key.mv_data = (void*)key_ciphertext.data(); key.mv_data = (void*)key_ciphertext.data();
key.mv_size = key_ciphertext.size(); key.mv_size = key_ciphertext.size();
dbr = mdb_get(txn, dbi, &key, &data); dbr = mdb_get(txn, dbi_rings, &key, &data);
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))); 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) if (dbr == MDB_NOTFOUND)
return false; return false;
@ -425,24 +340,82 @@ bool get_ring(const std::string &filename, const crypto::chacha_key &chacha_key,
return true; return true;
} }
bool blackball(const std::string &filename, const crypto::public_key &output) bool ringdb::blackball_worker(const crypto::public_key &output, int op)
{ {
return blackball_worker(filename, output, BLACKBALL_BLACKBALL); MDB_txn *txn;
MDB_cursor *cursor;
int dbr;
bool tx_active = false;
bool ret = true;
dbr = resize_env(env, filename.c_str(), 32 * 2); // a pubkey, and some slack
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 = zerokeyval;
MDB_val data;
data.mv_data = (void*)&output;
data.mv_size = sizeof(output);
switch (op)
{
case BLACKBALL_BLACKBALL:
MDEBUG("Blackballing output " << output);
dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA);
if (dbr == MDB_KEYEXIST)
dbr = 0;
break;
case BLACKBALL_UNBLACKBALL:
MDEBUG("Unblackballing output " << output);
dbr = mdb_del(txn, dbi_blackballs, &key, &data);
if (dbr == MDB_NOTFOUND)
dbr = 0;
break;
case BLACKBALL_QUERY:
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)));
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;
mdb_cursor_close(cursor);
break;
case BLACKBALL_CLEAR:
dbr = mdb_drop(txn, dbi_blackballs, 0);
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)));
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;
} }
bool unblackball(const std::string &filename, const crypto::public_key &output) bool ringdb::blackball(const crypto::public_key &output)
{ {
return blackball_worker(filename, output, BLACKBALL_UNBLACKBALL); return blackball_worker(output, BLACKBALL_BLACKBALL);
} }
bool blackballed(const std::string &filename, const crypto::public_key &output) bool ringdb::unblackball(const crypto::public_key &output)
{ {
return blackball_worker(filename, output, BLACKBALL_QUERY); return blackball_worker(output, BLACKBALL_UNBLACKBALL);
} }
bool clear_blackballs(const std::string &filename) bool ringdb::blackballed(const crypto::public_key &output)
{ {
return blackball_worker(filename, crypto::public_key(), BLACKBALL_CLEAR); return blackball_worker(output, BLACKBALL_QUERY);
} }
}} bool ringdb::clear_blackballs()
{
return blackball_worker(crypto::public_key(), BLACKBALL_CLEAR);
}
}

View file

@ -30,21 +30,35 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <lmdb.h>
#include "wipeable_string.h" #include "wipeable_string.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"
#include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/cryptonote_basic.h"
namespace tools namespace tools
{ {
namespace ringdb class ringdb
{ {
bool add_rings(const std::string &filename, const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx); public:
bool remove_rings(const std::string &filename, const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx); ringdb(std::string filename);
bool get_ring(const std::string &filename, const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs); ~ringdb();
bool blackball(const std::string &filename, const crypto::public_key &output); bool add_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
bool unblackball(const std::string &filename, const crypto::public_key &output); bool remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
bool blackballed(const std::string &filename, const crypto::public_key &output); bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool clear_blackballs(const std::string &filename);
} bool blackball(const crypto::public_key &output);
bool unblackball(const crypto::public_key &output);
bool blackballed(const crypto::public_key &output);
bool clear_blackballs();
private:
bool blackball_worker(const crypto::public_key &output, int op);
private:
std::string filename;
MDB_env *env;
MDB_dbi dbi_rings;
MDB_dbi dbi_blackballs;
};
} }

View file

@ -675,7 +675,12 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_light_wallet_balance(0), m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0), m_light_wallet_unlocked_balance(0),
m_key_on_device(false), m_key_on_device(false),
m_ring_history_saved(false) m_ring_history_saved(false),
m_ringdb()
{
}
wallet2::~wallet2()
{ {
} }
@ -1520,7 +1525,7 @@ void wallet2::process_outgoing(const crypto::hash &txid, const cryptonote::trans
entry.first->second.m_timestamp = ts; entry.first->second.m_timestamp = ts;
entry.first->second.m_unlock_time = tx.unlock_time; entry.first->second.m_unlock_time = tx.unlock_time;
add_rings(get_ring_database(), tx); add_rings(tx);
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices) void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices)
@ -1891,7 +1896,7 @@ void wallet2::update_pool_state(bool refreshed)
pit->second.m_state = wallet2::unconfirmed_transfer_details::failed; pit->second.m_state = wallet2::unconfirmed_transfer_details::failed;
// the inputs aren't spent anymore, since the tx failed // the inputs aren't spent anymore, since the tx failed
remove_rings(m_ring_database, pit->second.m_tx); remove_rings(pit->second.m_tx);
for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini) for (size_t vini = 0; vini < pit->second.m_tx.vin.size(); ++vini)
{ {
if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key)) if (pit->second.m_tx.vin[vini].type() == typeid(txin_to_key))
@ -3793,7 +3798,7 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
try try
{ {
find_and_save_rings(get_ring_database(), false); find_and_save_rings(false);
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
@ -4540,7 +4545,7 @@ void wallet2::commit_tx(pending_tx& ptx)
m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys)); m_additional_tx_keys.insert(std::make_pair(txid, ptx.additional_tx_keys));
} }
add_rings(m_ring_database, ptx.tx); add_rings(ptx.tx);
LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]"); LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]");
@ -5448,44 +5453,55 @@ void wallet2::set_ring_database(const std::string &filename)
{ {
m_ring_database = filename; m_ring_database = filename;
MINFO("ringdb path set to " << filename); MINFO("ringdb path set to " << filename);
m_ringdb.reset();
if (!m_ring_database.empty())
m_ringdb.reset(new tools::ringdb(m_ring_database));
} }
bool wallet2::add_rings(const std::string &filename, const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx) bool wallet2::add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx)
{ {
return ringdb::add_rings(filename, key, tx); if (!m_ringdb)
return true;
return m_ringdb->add_rings(key, tx);
} }
bool wallet2::add_rings(const std::string &filename, const cryptonote::transaction_prefix &tx) bool wallet2::add_rings(const cryptonote::transaction_prefix &tx)
{ {
crypto::chacha_key key; crypto::chacha_key key;
generate_chacha_key_from_secret_keys(key); generate_chacha_key_from_secret_keys(key);
return add_rings(filename, key, tx); return add_rings(key, tx);
} }
bool wallet2::remove_rings(const std::string &filename, const cryptonote::transaction_prefix &tx) bool wallet2::remove_rings(const cryptonote::transaction_prefix &tx)
{ {
if (!m_ringdb)
return true;
crypto::chacha_key key; crypto::chacha_key key;
generate_chacha_key_from_secret_keys(key); generate_chacha_key_from_secret_keys(key);
return ringdb::remove_rings(filename, key, tx); return m_ringdb->remove_rings(key, tx);
} }
bool wallet2::get_ring(const std::string &filename, const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs) bool wallet2::get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{ {
return ringdb::get_ring(filename, key, key_image, outs); if (!m_ringdb)
return true;
return m_ringdb->get_ring(key, key_image, outs);
} }
bool wallet2::get_ring(const std::string &filename, const crypto::key_image &key_image, std::vector<uint64_t> &outs) bool wallet2::get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs)
{ {
crypto::chacha_key key; crypto::chacha_key key;
generate_chacha_key_from_secret_keys(key); generate_chacha_key_from_secret_keys(key);
return get_ring(filename, key, key_image, outs); return get_ring(key, key_image, outs);
} }
bool wallet2::find_and_save_rings(const std::string &filename, bool force) bool wallet2::find_and_save_rings(bool force)
{ {
if (!force && m_ring_history_saved) if (!force && m_ring_history_saved)
return true; return true;
if (!m_ringdb)
return true;
COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req); COMMAND_RPC_GET_TRANSACTIONS::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res); COMMAND_RPC_GET_TRANSACTIONS::response res = AUTO_VAL_INIT(res);
@ -5533,7 +5549,7 @@ bool wallet2::find_and_save_rings(const std::string &filename, bool force)
crypto::hash tx_hash, tx_prefix_hash; crypto::hash tx_hash, tx_prefix_hash;
THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob"); THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash), error::wallet_internal_error, "failed to parse tx from blob");
THROW_WALLET_EXCEPTION_IF(epee::string_tools::pod_to_hex(tx_hash) != tx_info.tx_hash, error::wallet_internal_error, "txid mismatch"); THROW_WALLET_EXCEPTION_IF(epee::string_tools::pod_to_hex(tx_hash) != tx_info.tx_hash, error::wallet_internal_error, "txid mismatch");
THROW_WALLET_EXCEPTION_IF(!add_rings(filename, key, tx), error::wallet_internal_error, "Failed to save ring"); THROW_WALLET_EXCEPTION_IF(!add_rings(key, tx), error::wallet_internal_error, "Failed to save ring");
} }
MINFO("Found and saved rings for " << res.txs.size() << " transactions"); MINFO("Found and saved rings for " << res.txs.size() << " transactions");
@ -5543,27 +5559,35 @@ bool wallet2::find_and_save_rings(const std::string &filename, bool force)
bool wallet2::blackball_output(const crypto::public_key &output) bool wallet2::blackball_output(const crypto::public_key &output)
{ {
return ringdb::blackball(get_ring_database(), output); if (!m_ringdb)
return true;
return m_ringdb->blackball(output);
} }
bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add) bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add)
{ {
if (!m_ringdb)
return true;
bool ret = true; bool ret = true;
if (!add) if (!add)
ret &= ringdb::clear_blackballs(get_ring_database()); ret &= m_ringdb->clear_blackballs();
for (const auto &output: outputs) for (const auto &output: outputs)
ret &= ringdb::blackball(get_ring_database(), output); ret &= m_ringdb->blackball(output);
return ret; return ret;
} }
bool wallet2::unblackball_output(const crypto::public_key &output) bool wallet2::unblackball_output(const crypto::public_key &output)
{ {
return ringdb::unblackball(get_ring_database(), output); if (!m_ringdb)
return true;
return m_ringdb->unblackball(output);
} }
bool wallet2::is_output_blackballed(const crypto::public_key &output) const bool wallet2::is_output_blackballed(const crypto::public_key &output) const
{ {
return ringdb::blackballed(get_ring_database(), output); if (!m_ringdb)
return true;
return m_ringdb->blackballed(output);
} }
bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const
@ -5880,7 +5904,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (td.m_key_image_known && !td.m_key_image_partial) if (td.m_key_image_known && !td.m_key_image_partial)
{ {
std::vector<uint64_t> ring; std::vector<uint64_t> ring;
if (get_ring(get_ring_database(), key, td.m_key_image, ring)) if (get_ring(key, td.m_key_image, ring))
{ {
MINFO("This output has a known ring, reusing (size " << ring.size() << ")"); MINFO("This output has a known ring, reusing (size " << ring.size() << ")");
THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error, THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error,
@ -6072,7 +6096,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (td.m_key_image_known && !td.m_key_image_partial) if (td.m_key_image_known && !td.m_key_image_partial)
{ {
std::vector<uint64_t> ring; std::vector<uint64_t> ring;
if (get_ring(get_ring_database(), key, td.m_key_image, ring)) if (get_ring(key, td.m_key_image, ring))
{ {
for (uint64_t out: ring) for (uint64_t out: ring)
{ {

View file

@ -66,6 +66,8 @@ class Serialization_portability_wallet_Test;
namespace tools namespace tools
{ {
class ringdb;
class i_wallet2_callback class i_wallet2_callback
{ {
public: public:
@ -166,6 +168,7 @@ namespace tools
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev); static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev);
wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false); wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, bool restricted = false);
~wallet2();
struct multisig_info struct multisig_info
{ {
@ -1055,8 +1058,8 @@ namespace tools
void set_ring_database(const std::string &filename); void set_ring_database(const std::string &filename);
const std::string get_ring_database() const { return m_ring_database; } const std::string get_ring_database() const { return m_ring_database; }
bool get_ring(const std::string &filename, const crypto::key_image &key_image, std::vector<uint64_t> &outs); bool get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool find_and_save_rings(const std::string &filename, bool force = true); bool find_and_save_rings(bool force = true);
bool blackball_output(const crypto::public_key &output); bool blackball_output(const crypto::public_key &output);
bool set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add = false); bool set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add = false);
@ -1119,10 +1122,10 @@ namespace tools
rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const; rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const;
rct::key get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const; rct::key get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L) const;
void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n); void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n);
bool add_rings(const std::string &filename, const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx); bool add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx);
bool add_rings(const std::string &filename, const cryptonote::transaction_prefix &tx); bool add_rings(const cryptonote::transaction_prefix &tx);
bool remove_rings(const std::string &filename, const cryptonote::transaction_prefix &tx); bool remove_rings(const cryptonote::transaction_prefix &tx);
bool get_ring(const std::string &filename, const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs); bool get_ring(const crypto::chacha_key &key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution); bool get_output_distribution(uint64_t &start_height, std::vector<uint64_t> &distribution);
@ -1213,6 +1216,7 @@ namespace tools
std::string m_ring_database; std::string m_ring_database;
bool m_ring_history_saved; bool m_ring_history_saved;
std::unique_ptr<ringdb> m_ringdb;
}; };
} }
BOOST_CLASS_VERSION(tools::wallet2, 24) BOOST_CLASS_VERSION(tools::wallet2, 24)