mirror of
https://github.com/monero-project/monero.git
synced 2024-10-01 11:49:47 -04:00
wallet: add an output blackball list to avoid using those in rings
This commit is contained in:
parent
18eaf19489
commit
d29ea0455a
@ -1332,6 +1332,135 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::blackball(const std::vector<std::string> &args)
|
||||
{
|
||||
crypto::public_key output;
|
||||
if (args.size() == 0)
|
||||
{
|
||||
fail_msg_writer() << tr("usage: blackball <output_public_key> | <filename> [add]");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (epee::string_tools::hex_to_pod(args[0], output))
|
||||
{
|
||||
m_wallet->blackball_output(output);
|
||||
}
|
||||
else if (epee::file_io_utils::is_file_exist(args[0]))
|
||||
{
|
||||
std::vector<crypto::public_key> outputs;
|
||||
char str[65];
|
||||
|
||||
std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(), "r"));
|
||||
if (f)
|
||||
{
|
||||
while (!feof(f.get()))
|
||||
{
|
||||
if (!fgets(str, sizeof(str), f.get()))
|
||||
break;
|
||||
const size_t len = strlen(str);
|
||||
if (len > 0 && str[len - 1] == '\n')
|
||||
str[len - 1] = 0;
|
||||
if (!str[0])
|
||||
continue;
|
||||
outputs.push_back(crypto::public_key());
|
||||
if (!epee::string_tools::hex_to_pod(str, outputs.back()))
|
||||
{
|
||||
fail_msg_writer() << tr("Invalid public key: ") << str;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
f.reset();
|
||||
bool add = false;
|
||||
if (args.size() > 1)
|
||||
{
|
||||
if (args[1] != "add")
|
||||
{
|
||||
fail_msg_writer() << tr("Bad argument: ") + args[1] + ": " + tr("should be \"add\"");
|
||||
return true;
|
||||
}
|
||||
add = true;
|
||||
}
|
||||
m_wallet->set_blackballed_outputs(outputs, add);
|
||||
}
|
||||
else
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to open file");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fail_msg_writer() << tr("Invalid public key, and file doesn't exist");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to blackball output: ") << e.what();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::unblackball(const std::vector<std::string> &args)
|
||||
{
|
||||
crypto::public_key output;
|
||||
if (args.size() != 1)
|
||||
{
|
||||
fail_msg_writer() << tr("usage: unblackball <output_public_key>");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!epee::string_tools::hex_to_pod(args[0], output))
|
||||
{
|
||||
fail_msg_writer() << tr("Invalid public key");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_wallet->unblackball_output(output);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to unblackball output: ") << e.what();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::blackballed(const std::vector<std::string> &args)
|
||||
{
|
||||
crypto::public_key output;
|
||||
if (args.size() != 1)
|
||||
{
|
||||
fail_msg_writer() << tr("usage: blackballed <output_public_key>");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!epee::string_tools::hex_to_pod(args[0], output))
|
||||
{
|
||||
fail_msg_writer() << tr("Invalid public key");
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (m_wallet->is_output_blackballed(output))
|
||||
message_writer() << tr("Blackballed: ") << output;
|
||||
else
|
||||
message_writer() << tr("not blackballed: ") << output;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
fail_msg_writer() << tr("Failed to unblackball output: ") << e.what();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool simple_wallet::save_known_rings(const std::vector<std::string> &args)
|
||||
{
|
||||
try
|
||||
@ -2041,6 +2170,18 @@ simple_wallet::simple_wallet()
|
||||
boost::bind(&simple_wallet::save_known_rings, this, _1),
|
||||
tr("save_known_rings"),
|
||||
tr("Save known rings to the shared rings database"));
|
||||
m_cmd_binder.set_handler("blackball",
|
||||
boost::bind(&simple_wallet::blackball, this, _1),
|
||||
tr("blackball <output public key> | <filename> [add]"),
|
||||
tr("Blackball output(s) so they never get selected as fake outputs in a ring"));
|
||||
m_cmd_binder.set_handler("unblackball",
|
||||
boost::bind(&simple_wallet::unblackball, this, _1),
|
||||
tr("unblackball <output public key>"),
|
||||
tr("Unblackballs an output so it may get selected as a fake output in a ring"));
|
||||
m_cmd_binder.set_handler("blackballed",
|
||||
boost::bind(&simple_wallet::blackballed, this, _1),
|
||||
tr("blackballed <output public key>"),
|
||||
tr("Checks whether an output is blackballed"));
|
||||
m_cmd_binder.set_handler("help",
|
||||
boost::bind(&simple_wallet::help, this, _1),
|
||||
tr("help [<command>]"),
|
||||
|
@ -212,6 +212,9 @@ namespace cryptonote
|
||||
bool export_raw_multisig(const std::vector<std::string>& args);
|
||||
bool print_ring(const std::vector<std::string>& args);
|
||||
bool save_known_rings(const std::vector<std::string>& args);
|
||||
bool blackball(const std::vector<std::string>& args);
|
||||
bool unblackball(const std::vector<std::string>& args);
|
||||
bool blackballed(const std::vector<std::string>& args);
|
||||
|
||||
uint64_t get_daemon_blockchain_height(std::string& err);
|
||||
bool try_connect_to_daemon(bool silent = false, uint32_t* version = nullptr);
|
||||
|
@ -38,6 +38,9 @@
|
||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.ringdb"
|
||||
|
||||
static const char zerokey[8] = {0};
|
||||
static const MDB_val zerokeyval = { sizeof(zerokey), (void *)zerokey };
|
||||
|
||||
static int compare_hash32(const MDB_val *a, const MDB_val *b)
|
||||
{
|
||||
uint32_t *va = (uint32_t*) a->mv_data;
|
||||
@ -123,12 +126,14 @@ static std::string decrypt(const std::string &ciphertext, const crypto::key_imag
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
static int resize_env(MDB_env *env, const char *db_path, size_t n_entries)
|
||||
static int resize_env(MDB_env *env, const char *db_path, size_t needed)
|
||||
{
|
||||
MDB_envinfo mei;
|
||||
MDB_stat mst;
|
||||
int ret;
|
||||
|
||||
needed = std::max(needed, (size_t)(2ul * 1024 * 1024)); // at least 2 MB
|
||||
|
||||
ret = mdb_env_info(env, &mei);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -136,7 +141,6 @@ static int resize_env(MDB_env *env, const char *db_path, size_t n_entries)
|
||||
if (ret)
|
||||
return ret;
|
||||
uint64_t size_used = mst.ms_psize * mei.me_last_pgno;
|
||||
const size_t needed = n_entries * (32 + 1024); // highball 1kB for the ring data to make sure
|
||||
uint64_t mapsize = mei.me_mapsize;
|
||||
if (size_used + needed > mei.me_mapsize)
|
||||
{
|
||||
@ -161,6 +165,90 @@ static int resize_env(MDB_env *env, const char *db_path, size_t n_entries)
|
||||
return mdb_env_set_mapsize(env, mapsize);
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
static bool blackball_worker(const std::string &filename, const crypto::public_key &output, int op)
|
||||
{
|
||||
MDB_env *env;
|
||||
MDB_dbi dbi;
|
||||
MDB_txn *txn;
|
||||
MDB_cursor *cursor;
|
||||
int dbr;
|
||||
bool tx_active = false;
|
||||
bool ret = true;
|
||||
|
||||
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(), 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;
|
||||
dbr = mdb_dbi_open(txn, "blackballs", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &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_dupsort(txn, dbi, compare_hash32);
|
||||
|
||||
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, &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);
|
||||
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;
|
||||
}
|
||||
|
||||
namespace tools { namespace ringdb
|
||||
{
|
||||
|
||||
@ -183,8 +271,8 @@ bool add_rings(const std::string &filename, const crypto::chacha_key &chacha_key
|
||||
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(), tx.vin.size());
|
||||
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
|
||||
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");
|
||||
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);});
|
||||
@ -242,8 +330,8 @@ bool remove_rings(const std::string &filename, const crypto::chacha_key &chacha_
|
||||
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(), tx.vin.size());
|
||||
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
|
||||
dbr = resize_env(env, filename.c_str(), 0);
|
||||
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size");
|
||||
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);});
|
||||
@ -337,4 +425,24 @@ bool get_ring(const std::string &filename, const crypto::chacha_key &chacha_key,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool blackball(const std::string &filename, const crypto::public_key &output)
|
||||
{
|
||||
return blackball_worker(filename, output, BLACKBALL_BLACKBALL);
|
||||
}
|
||||
|
||||
bool unblackball(const std::string &filename, const crypto::public_key &output)
|
||||
{
|
||||
return blackball_worker(filename, output, BLACKBALL_UNBLACKBALL);
|
||||
}
|
||||
|
||||
bool blackballed(const std::string &filename, const crypto::public_key &output)
|
||||
{
|
||||
return blackball_worker(filename, output, BLACKBALL_QUERY);
|
||||
}
|
||||
|
||||
bool clear_blackballs(const std::string &filename)
|
||||
{
|
||||
return blackball_worker(filename, crypto::public_key(), BLACKBALL_CLEAR);
|
||||
}
|
||||
|
||||
}}
|
||||
|
@ -41,5 +41,10 @@ namespace tools
|
||||
bool add_rings(const std::string &filename, const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
|
||||
bool remove_rings(const std::string &filename, const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
|
||||
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 blackball(const std::string &filename, const crypto::public_key &output);
|
||||
bool unblackball(const std::string &filename, const crypto::public_key &output);
|
||||
bool blackballed(const std::string &filename, const crypto::public_key &output);
|
||||
bool clear_blackballs(const std::string &filename);
|
||||
}
|
||||
}
|
||||
|
@ -5541,16 +5541,43 @@ bool wallet2::find_and_save_rings(const std::string &filename, bool force)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const
|
||||
bool wallet2::blackball_output(const crypto::public_key &output)
|
||||
{
|
||||
return ringdb::blackball(get_ring_database(), output);
|
||||
}
|
||||
|
||||
bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add)
|
||||
{
|
||||
bool ret = true;
|
||||
if (!add)
|
||||
ret &= ringdb::clear_blackballs(get_ring_database());
|
||||
for (const auto &output: outputs)
|
||||
ret &= ringdb::blackball(get_ring_database(), output);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool wallet2::unblackball_output(const crypto::public_key &output)
|
||||
{
|
||||
return ringdb::unblackball(get_ring_database(), output);
|
||||
}
|
||||
|
||||
bool wallet2::is_output_blackballed(const crypto::public_key &output) const
|
||||
{
|
||||
return ringdb::blackballed(get_ring_database(), 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
|
||||
{
|
||||
if (!unlocked) // don't add locked outs
|
||||
return false;
|
||||
if (global_index == real_index) // don't re-add real one
|
||||
return false;
|
||||
auto item = std::make_tuple(global_index, tx_public_key, mask);
|
||||
auto item = std::make_tuple(global_index, output_public_key, mask);
|
||||
CHECK_AND_ASSERT_MES(!outs.empty(), false, "internal error: outs is empty");
|
||||
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
|
||||
return false;
|
||||
if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
|
||||
return false;
|
||||
outs.back().push_back(item);
|
||||
return true;
|
||||
}
|
||||
|
@ -1058,6 +1058,11 @@ namespace tools
|
||||
bool get_ring(const std::string &filename, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
|
||||
bool find_and_save_rings(const std::string &filename, bool force = true);
|
||||
|
||||
bool blackball_output(const crypto::public_key &output);
|
||||
bool set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add = false);
|
||||
bool unblackball_output(const crypto::public_key &output);
|
||||
bool is_output_blackballed(const crypto::public_key &output) const;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief Stores wallet information to wallet file.
|
||||
|
Loading…
Reference in New Issue
Block a user