diff options
-rw-r--r-- | src/blockchain_utilities/blockchain_blackball.cpp | 241 | ||||
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 63 | ||||
-rw-r--r-- | src/wallet/ringdb.cpp | 46 | ||||
-rw-r--r-- | src/wallet/ringdb.h | 10 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 14 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 8 | ||||
-rw-r--r-- | tests/unit_tests/ringdb.cpp | 8 |
7 files changed, 287 insertions, 103 deletions
diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index e68de9dc4..9a31b95b3 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -66,17 +66,17 @@ static MDB_env *env = NULL; struct output_data { uint64_t amount; - uint64_t index; - output_data(): amount(0), index(0) {} - output_data(uint64_t a, uint64_t i): amount(a), index(i) {} - bool operator==(const output_data &other) const { return other.amount == amount && other.index == index; } + uint64_t offset; + output_data(): amount(0), offset(0) {} + output_data(uint64_t a, uint64_t i): amount(a), offset(i) {} + bool operator==(const output_data &other) const { return other.amount == amount && other.offset == offset; } }; // // relative_rings: key_image -> vector<uint64_t> // outputs: 128 bits -> set of key images // processed_txidx: string -> uint64_t -// spent: 128 bits, zerokval +// spent: amount -> offset // ring_instances: vector<uint64_t> -> uint64_t // stats: string -> arbitrary // @@ -263,7 +263,7 @@ static void init(std::string cache_filename) dbr = mdb_dbi_open(txn, "spent", MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_spent); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); - mdb_set_dupsort(txn, dbi_spent, compare_double64); + mdb_set_dupsort(txn, dbi_spent, compare_uint64); dbr = mdb_dbi_open(txn, "ring_instances", MDB_CREATE, &dbi_ring_instances); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); @@ -511,13 +511,19 @@ static uint64_t get_num_spent_outputs() dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open cursor for spent outputs: " + std::string(mdb_strerror(dbr))); MDB_val k, v; - mdb_size_t count = 0; - dbr = mdb_cursor_get(cur, &k, &v, MDB_FIRST); - if (dbr != MDB_NOTFOUND) + mdb_size_t count = 0, tmp; + + MDB_cursor_op op = MDB_FIRST; + while (1) { - CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get first spent output: " + std::string(mdb_strerror(dbr))); - dbr = mdb_cursor_count(cur, &count); + dbr = mdb_cursor_get(cur, &k, &v, op); + op = MDB_NEXT_NODUP; + if (dbr == MDB_NOTFOUND) + break; + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to get first/next spent output: " + std::string(mdb_strerror(dbr))); + dbr = mdb_cursor_count(cur, &tmp); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to count entries: " + std::string(mdb_strerror(dbr))); + count += tmp; } mdb_cursor_close(cur); @@ -530,15 +536,17 @@ static uint64_t get_num_spent_outputs() static void add_spent_output(MDB_cursor *cur, const output_data &od) { - MDB_val v = {sizeof(od), (void*)&od}; - int dbr = mdb_cursor_put(cur, (MDB_val *)&zerokval, &v, MDB_NODUPDATA); + MDB_val k = {sizeof(od.amount), (void*)&od.amount}; + MDB_val v = {sizeof(od.offset), (void*)&od.offset}; + int dbr = mdb_cursor_put(cur, &k, &v, 0); CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_KEYEXIST, "Failed to add spent output: " + std::string(mdb_strerror(dbr))); } static bool is_output_spent(MDB_cursor *cur, const output_data &od) { - MDB_val v = {sizeof(od), (void*)&od}; - int dbr = mdb_cursor_get(cur, (MDB_val *)&zerokval, &v, MDB_GET_BOTH); + MDB_val k = {sizeof(od.amount), (void*)&od.amount}; + MDB_val v = {sizeof(od.offset), (void*)&od.offset}; + int dbr = mdb_cursor_get(cur, &k, &v, MDB_GET_BOTH); CHECK_AND_ASSERT_THROW_MES(!dbr || dbr == MDB_NOTFOUND, "Failed to get spent output: " + std::string(mdb_strerror(dbr))); bool spent = dbr == 0; return spent; @@ -562,8 +570,7 @@ static std::vector<output_data> get_spent_outputs(MDB_txn *txn) outs.reserve(count); while (1) { - const output_data *od = (const output_data*)v.mv_data; - outs.push_back(*od); + outs.push_back({*(const uint64_t*)k.mv_data, *(const uint64_t*)v.mv_data}); dbr = mdb_cursor_get(cur, &k, &v, MDB_NEXT); if (dbr == MDB_NOTFOUND) break; @@ -815,14 +822,6 @@ static void close_db(MDB_env *env, MDB_txn *txn, MDB_cursor *cur, MDB_dbi dbi) mdb_env_close(env); } -static crypto::public_key get_output_key(MDB_cursor *cur, uint64_t amount, uint64_t offset) -{ - MDB_val k = { sizeof(amount), (void*)&amount }, v = { sizeof(offset), (void*)&offset }; - int dbr = mdb_cursor_get(cur, &k, &v, MDB_GET_BOTH); - if (dbr) throw std::runtime_error("Output key not found: " + std::string(mdb_strerror(dbr))); - return *(const crypto::public_key*)(((const char*)v.mv_data) + sizeof(uint64_t) * 2); -} - static void get_num_outputs(MDB_txn *txn, MDB_cursor *cur, MDB_dbi dbi, uint64_t &pre_rct, uint64_t &rct) { uint64_t amount = 0; @@ -884,6 +883,113 @@ static crypto::hash get_genesis_block_hash(const std::string &filename) return genesis_block_hash; } +static std::vector<std::pair<uint64_t, uint64_t>> load_outputs(const std::string &filename) +{ + std::vector<std::pair<uint64_t, uint64_t>> outputs; + uint64_t amount = std::numeric_limits<uint64_t>::max(); + FILE *f; + + f = fopen(filename.c_str(), "r"); + if (!f) + { + MERROR("Failed to load outputs from " << filename << ": " << strerror(errno)); + return {}; + } + while (1) + { + char s[256]; + fgets(s, sizeof(s), f); + if (feof(f)) + break; + const size_t len = strlen(s); + if (len > 0 && s[len - 1] == '\n') + s[len - 1] = 0; + if (!s[0]) + continue; + std::pair<uint64_t, uint64_t> output; + uint64_t offset, num_offsets; + if (sscanf(s, "@%" PRIu64, &amount) == 1) + { + continue; + } + if (amount == std::numeric_limits<uint64_t>::max()) + { + MERROR("Bad format in " << filename); + continue; + } + if (sscanf(s, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets < std::numeric_limits<uint64_t>::max() - offset) + { + while (num_offsets-- > 0) + outputs.push_back(std::make_pair(amount, offset++)); + } + else if (sscanf(s, "%" PRIu64, &offset) == 1) + { + outputs.push_back(std::make_pair(amount, offset)); + } + else + { + MERROR("Bad format in " << filename); + continue; + } + } + fclose(f); + return outputs; +} + +static bool export_spent_outputs(MDB_cursor *cur, const std::string &filename) +{ + FILE *f = fopen(filename.c_str(), "w"); + if (!f) + { + MERROR("Failed to open " << filename << ": " << strerror(errno)); + return false; + } + + uint64_t pending_amount = std::numeric_limits<uint64_t>::max(); + std::vector<uint64_t> pending_offsets; + MDB_val k, v; + MDB_cursor_op op = MDB_FIRST; + while (1) + { + int dbr = mdb_cursor_get(cur, &k, &v, op); + if (dbr == MDB_NOTFOUND) + break; + op = MDB_NEXT; + if (dbr) + { + fclose(f); + MERROR("Failed to enumerate spent outputs: " << mdb_strerror(dbr)); + return false; + } + const uint64_t amount = *(const uint64_t*)k.mv_data; + const uint64_t offset = *(const uint64_t*)v.mv_data; + if (!pending_offsets.empty() && (amount != pending_amount || pending_offsets.back()+1 != offset)) + { + if (pending_offsets.size() == 1) + fprintf(f, "%" PRIu64 "\n", pending_offsets.front()); + else + fprintf(f, "%" PRIu64 "*%" PRIu64 "\n", pending_offsets.front(), pending_offsets.size()); + pending_offsets.clear(); + } + if (pending_amount != amount) + { + fprintf(f, "@%" PRIu64 "\n", amount); + pending_amount = amount; + } + pending_offsets.push_back(offset); + } + if (!pending_offsets.empty()) + { + if (pending_offsets.size() == 1) + fprintf(f, "%" PRIu64 "\n", pending_offsets.front()); + else + fprintf(f, "%" PRIu64 "*%" PRIu64 "\n", pending_offsets.front(), pending_offsets.size()); + pending_offsets.clear(); + } + fclose(f); + return true; +} + int main(int argc, char* argv[]) { TRY_ENTRY(); @@ -920,6 +1026,8 @@ int main(int argc, char* argv[]) , "Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]." , "fast:1000" }; + const command_line::arg_descriptor<std::string> arg_extra_spent_list = {"extra-spent-list", "Optional list of known spent outputs",""}; + const command_line::arg_descriptor<std::string> arg_export = {"export", "Filename to export the backball list to"}; command_line::add_arg(desc_cmd_sett, arg_blackball_db_dir); command_line::add_arg(desc_cmd_sett, arg_log_level); @@ -928,6 +1036,8 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_check_subsets); command_line::add_arg(desc_cmd_sett, arg_verbose); command_line::add_arg(desc_cmd_sett, arg_db_sync_mode); + command_line::add_arg(desc_cmd_sett, arg_extra_spent_list); + command_line::add_arg(desc_cmd_sett, arg_export); command_line::add_arg(desc_cmd_sett, arg_inputs); command_line::add_arg(desc_cmd_only, command_line::arg_help); @@ -967,6 +1077,9 @@ int main(int argc, char* argv[]) bool opt_rct_only = command_line::get_arg(vm, arg_rct_only); bool opt_check_subsets = command_line::get_arg(vm, arg_check_subsets); bool opt_verbose = command_line::get_arg(vm, arg_verbose); + std::string opt_export = command_line::get_arg(vm, arg_export); + std::string extra_spent_list = command_line::get_arg(vm, arg_extra_spent_list); + std::vector<std::pair<uint64_t, uint64_t>> extra_spent_outputs = extra_spent_list.empty() ? std::vector<std::pair<uint64_t, uint64_t>>() : load_outputs(extra_spent_list); std::string db_type = command_line::get_arg(vm, arg_database); if (!cryptonote::blockchain_valid_db_type(db_type)) @@ -1015,6 +1128,36 @@ int main(int argc, char* argv[]) MDB_cursor *cur0; open_db(inputs[0], &env0, &txn0, &cur0, &dbi0); + if (!extra_spent_outputs.empty()) + { + MINFO("Adding " << extra_spent_outputs.size() << " extra spent outputs"); + MDB_txn *txn; + int dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + MDB_cursor *cur; + dbr = mdb_cursor_open(txn, dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); + + std::vector<std::pair<uint64_t, uint64_t>> blackballs; + for (const std::pair<uint64_t, uint64_t> &output: extra_spent_outputs) + { + if (!is_output_spent(cur, output_data(output.first, output.second))) + { + blackballs.push_back(output); + add_spent_output(cur, output_data(output.first, output.second)); + inc_stat(txn, output.first ? "pre-rct-extra" : "rct-ring-extra"); + } + } + if (!blackballs.empty()) + { + ringdb.blackball(blackballs); + blackballs.clear(); + } + mdb_cursor_close(cur); + dbr = mdb_txn_commit(txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); + } + for (size_t n = 0; n < inputs.size(); ++n) { const std::string canonical = boost::filesystem::canonical(inputs[n]).string(); @@ -1033,7 +1176,7 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); size_t records = 0; const std::string filename = inputs[n]; - std::vector<crypto::public_key> blackballs; + std::vector<std::pair<uint64_t, uint64_t>> blackballs; uint64_t n_txes; for_all_transactions(filename, start_idx, n_txes, [&](const cryptonote::transaction_prefix &tx)->bool { @@ -1057,13 +1200,13 @@ int main(int argc, char* argv[]) const uint64_t instances = inc_ring_instances(txn, txin.amount, new_ring); if (n == 0 && ring_size == 1) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[0]); + const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[0]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in a 1-ring"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a 1-ring"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, absolute[0])); inc_stat(txn, txin.amount ? "pre-rct-ring-size-1" : "rct-ring-size-1"); } @@ -1071,13 +1214,13 @@ int main(int argc, char* argv[]) { for (size_t o = 0; o < new_ring.size(); ++o) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); + const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " identical " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, absolute[o])); inc_stat(txn, txin.amount ? "pre-rct-duplicate-rings" : "rct-duplicate-rings"); } @@ -1086,13 +1229,13 @@ int main(int argc, char* argv[]) { for (size_t o = 0; o < new_ring.size(); ++o) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, absolute[o]); + const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, absolute[o]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in " << new_ring.size() << " subsets of " << new_ring.size() << "-rings"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, absolute[o])); inc_stat(txn, txin.amount ? "pre-rct-subset-rings" : "rct-subset-rings"); } @@ -1122,13 +1265,13 @@ int main(int argc, char* argv[]) } else if (common.size() == 1) { - const crypto::public_key pkey = get_output_key(cur0, txin.amount, common[0]); + const std::pair<uint64_t, uint64_t> output = std::make_pair(txin.amount, common[0]); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in rings with a single common element"); + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in rings with a single common element"); std::cout << "\r" << start_idx << "/" << n_txes << " \r" << std::flush; } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(txin.amount, common[0])); inc_stat(txn, txin.amount ? "pre-rct-key-image-attack" : "rct-key-image-attack"); } @@ -1211,7 +1354,7 @@ int main(int argc, char* argv[]) dbr = mdb_cursor_open(txn, dbi_spent, &cur); CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); - std::vector<crypto::public_key> blackballs; + std::vector<std::pair<uint64_t, uint64_t>> blackballs; std::vector<output_data> scan_spent = std::move(work_spent); work_spent.clear(); for (const output_data &od: scan_spent) @@ -1234,13 +1377,13 @@ int main(int argc, char* argv[]) } if (known == absolute.size() - 1) { - const crypto::public_key pkey = get_output_key(cur0, od.amount, last_unknown); + const std::pair<uint64_t, uint64_t> output = std::make_pair(od.amount, last_unknown); if (opt_verbose) { - MINFO("Blackballing output " << pkey << ", due to being used in a " << + MINFO("Blackballing output " << output.first << "/" << output.second << ", due to being used in a " << absolute.size() << "-ring where all other outputs are known to be spent"); } - blackballs.push_back(pkey); + blackballs.push_back(output); add_spent_output(cur, output_data(od.amount, last_unknown)); work_spent.push_back(output_data(od.amount, last_unknown)); inc_stat(txn, od.amount ? "pre-rct-chain-reaction" : "rct-chain-reaction"); @@ -1279,6 +1422,7 @@ skip_secondary_passes: { "pre-rct-duplicate-rings", pre_rct }, { "rct-duplicate-rings", rct }, { "pre-rct-subset-rings", pre_rct }, { "rct-subset-rings", rct }, { "pre-rct-key-image-attack", pre_rct }, { "rct-key-image-attack", rct }, + { "pre-rct-extra", pre_rct }, { "rct-ring-extra", rct }, { "pre-rct-chain-reaction", pre_rct }, { "rct-chain-reaction", rct }, }; for (const auto &key: stat_keys) @@ -1291,6 +1435,19 @@ skip_secondary_passes: } mdb_txn_abort(txn); + if (!opt_export.empty()) + { + MDB_txn *txn; + int dbr = mdb_txn_begin(env, NULL, 0, &txn); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); + MDB_cursor *cur; + dbr = mdb_cursor_open(txn, dbi_spent, &cur); + CHECK_AND_ASSERT_THROW_MES(!dbr, "Failed to open LMDB cursor: " + std::string(mdb_strerror(dbr))); + export_spent_outputs(cur, opt_export); + mdb_cursor_close(cur); + mdb_txn_abort(txn); + } + LOG_PRINT_L0("Blockchain blackball data exported OK"); close_db(env0, txn0, cur0, dbi0); close(); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f5aeabded..e0e0c441a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1624,23 +1624,23 @@ bool simple_wallet::set_ring(const std::vector<std::string> &args) bool simple_wallet::blackball(const std::vector<std::string> &args) { - crypto::public_key output; + uint64_t amount = std::numeric_limits<uint64_t>::max(), offset, num_offsets; if (args.size() == 0) { - fail_msg_writer() << tr("usage: blackball <output_public_key> | <filename> [add]"); + fail_msg_writer() << tr("usage: blackball <amount>/<offset> | <filename> [add]"); return true; } try { - if (epee::string_tools::hex_to_pod(args[0], output)) + if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &amount, &offset) == 2) { - m_wallet->blackball_output(output); + m_wallet->blackball_output(std::make_pair(amount, offset)); } else if (epee::file_io_utils::is_file_exist(args[0])) { - std::vector<crypto::public_key> outputs; - char str[65]; + std::vector<std::pair<uint64_t, uint64_t>> outputs; + char str[256]; std::unique_ptr<FILE, tools::close_file> f(fopen(args[0].c_str(), "r")); if (f) @@ -1654,10 +1654,27 @@ bool simple_wallet::blackball(const std::vector<std::string> &args) str[len - 1] = 0; if (!str[0]) continue; - outputs.push_back(crypto::public_key()); - if (!epee::string_tools::hex_to_pod(str, outputs.back())) + if (sscanf(str, "@%" PRIu64, &amount) == 1) { - fail_msg_writer() << tr("Invalid public key: ") << str; + continue; + } + if (amount == std::numeric_limits<uint64_t>::max()) + { + fail_msg_writer() << tr("First line is not an amount"); + return true; + } + if (sscanf(str, "%" PRIu64 "*%" PRIu64, &offset, &num_offsets) == 2 && num_offsets <= std::numeric_limits<uint64_t>::max() - offset) + { + while (num_offsets--) + outputs.push_back(std::make_pair(amount, offset++)); + } + else if (sscanf(str, "%" PRIu64, &offset) == 1) + { + outputs.push_back(std::make_pair(amount, offset)); + } + else + { + fail_msg_writer() << tr("Invalid output: ") << str; return true; } } @@ -1682,7 +1699,7 @@ bool simple_wallet::blackball(const std::vector<std::string> &args) } else { - fail_msg_writer() << tr("Invalid public key, and file doesn't exist"); + fail_msg_writer() << tr("Invalid output key, and file doesn't exist"); return true; } } @@ -1696,16 +1713,16 @@ bool simple_wallet::blackball(const std::vector<std::string> &args) bool simple_wallet::unblackball(const std::vector<std::string> &args) { - crypto::public_key output; + std::pair<uint64_t, uint64_t> output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: unblackball <output_public_key>"); + fail_msg_writer() << tr("usage: unblackball <amount>/<offset>"); return true; } - if (!epee::string_tools::hex_to_pod(args[0], output)) + if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2) { - fail_msg_writer() << tr("Invalid public key"); + fail_msg_writer() << tr("Invalid output"); return true; } @@ -1723,25 +1740,25 @@ bool simple_wallet::unblackball(const std::vector<std::string> &args) bool simple_wallet::blackballed(const std::vector<std::string> &args) { - crypto::public_key output; + std::pair<uint64_t, uint64_t> output; if (args.size() != 1) { - fail_msg_writer() << tr("usage: blackballed <output_public_key>"); + fail_msg_writer() << tr("usage: blackballed <amount>/<offset>"); return true; } - if (!epee::string_tools::hex_to_pod(args[0], output)) + if (sscanf(args[0].c_str(), "%" PRIu64 "/%" PRIu64, &output.first, &output.second) != 2) { - fail_msg_writer() << tr("Invalid public key"); + fail_msg_writer() << tr("Invalid output"); return true; } try { if (m_wallet->is_output_blackballed(output)) - message_writer() << tr("Blackballed: ") << output; + message_writer() << tr("Blackballed: ") << output.first << "/" << output.second; else - message_writer() << tr("not blackballed: ") << output; + message_writer() << tr("not blackballed: ") << output.first << "/" << output.second; } catch (const std::exception &e) { @@ -2554,15 +2571,15 @@ simple_wallet::simple_wallet() 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 <amount>/<offset> | <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("unblackball <amount>/<offset>"), 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("blackballed <amount>/<offset>"), tr("Checks whether an output is blackballed")); m_cmd_binder.set_handler("version", boost::bind(&simple_wallet::version, this, _1), diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index bf5478f5e..e9fc6866d 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -55,6 +55,13 @@ static int compare_hash32(const MDB_val *a, const MDB_val *b) return 0; } +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; +} + static std::string compress_ring(const std::vector<uint64_t> &ring) { std::string s; @@ -217,9 +224,9 @@ ringdb::ringdb(std::string filename, const std::string &genesis): 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); - dbr = mdb_dbi_open(txn, ("blackballs-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs); + dbr = mdb_dbi_open(txn, ("blackballs2-" + genesis).c_str(), MDB_CREATE | MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED, &dbi_blackballs); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to open LMDB dbi: " + std::string(mdb_strerror(dbr))); - mdb_set_dupsort(txn, dbi_blackballs, compare_hash32); + mdb_set_dupsort(txn, dbi_blackballs, compare_uint64); dbr = mdb_txn_commit(txn); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn creating/opening database: " + std::string(mdb_strerror(dbr))); @@ -374,7 +381,7 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return true; } -bool ringdb::blackball_worker(const std::vector<crypto::public_key> &outputs, int op) +bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op) { MDB_txn *txn; MDB_cursor *cursor; @@ -391,24 +398,25 @@ bool ringdb::blackball_worker(const std::vector<crypto::public_key> &outputs, in 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; - for (const crypto::public_key &output: outputs) + MDB_val key, data; + for (const std::pair<uint64_t, uint64_t> &output: outputs) { - data.mv_data = (void*)&output; - data.mv_size = sizeof(output); + key.mv_data = (void*)&output.first; + key.mv_size = sizeof(output.first); + data.mv_data = (void*)&output.second; + data.mv_size = sizeof(output.second); switch (op) { case BLACKBALL_BLACKBALL: - MDEBUG("Blackballing output " << output); - dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_NODUPDATA); + MDEBUG("Blackballing output " << output.first << "/" << output.second); + dbr = mdb_put(txn, dbi_blackballs, &key, &data, MDB_APPENDDUP); if (dbr == MDB_KEYEXIST) dbr = 0; break; case BLACKBALL_UNBLACKBALL: - MDEBUG("Unblackballing output " << output); + MDEBUG("Unblackballing output " << output.first << "/" << output.second); dbr = mdb_del(txn, dbi_blackballs, &key, &data); if (dbr == MDB_NOTFOUND) dbr = 0; @@ -443,32 +451,32 @@ bool ringdb::blackball_worker(const std::vector<crypto::public_key> &outputs, in return ret; } -bool ringdb::blackball(const std::vector<crypto::public_key> &outputs) +bool ringdb::blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs) { return blackball_worker(outputs, BLACKBALL_BLACKBALL); } -bool ringdb::blackball(const crypto::public_key &output) +bool ringdb::blackball(const std::pair<uint64_t, uint64_t> &output) { - std::vector<crypto::public_key> outputs(1, output); + std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output); return blackball_worker(outputs, BLACKBALL_BLACKBALL); } -bool ringdb::unblackball(const crypto::public_key &output) +bool ringdb::unblackball(const std::pair<uint64_t, uint64_t> &output) { - std::vector<crypto::public_key> outputs(1, output); + std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output); return blackball_worker(outputs, BLACKBALL_UNBLACKBALL); } -bool ringdb::blackballed(const crypto::public_key &output) +bool ringdb::blackballed(const std::pair<uint64_t, uint64_t> &output) { - std::vector<crypto::public_key> outputs(1, output); + std::vector<std::pair<uint64_t, uint64_t>> outputs(1, output); return blackball_worker(outputs, BLACKBALL_QUERY); } bool ringdb::clear_blackballs() { - return blackball_worker(std::vector<crypto::public_key>(), BLACKBALL_CLEAR); + return blackball_worker(std::vector<std::pair<uint64_t, uint64_t>>(), BLACKBALL_CLEAR); } } diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h index 4a78f61f8..7b448b0d7 100644 --- a/src/wallet/ringdb.h +++ b/src/wallet/ringdb.h @@ -49,14 +49,14 @@ namespace tools bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs); bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative); - bool blackball(const crypto::public_key &output); - bool blackball(const std::vector<crypto::public_key> &outputs); - bool unblackball(const crypto::public_key &output); - bool blackballed(const crypto::public_key &output); + bool blackball(const std::pair<uint64_t, uint64_t> &output); + bool blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs); + bool unblackball(const std::pair<uint64_t, uint64_t> &output); + bool blackballed(const std::pair<uint64_t, uint64_t> &output); bool clear_blackballs(); private: - bool blackball_worker(const std::vector<crypto::public_key> &outputs, int op); + bool blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op); private: std::string filename; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ffd7131cd..9866aa215 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6221,7 +6221,7 @@ bool wallet2::find_and_save_rings(bool force) return true; } -bool wallet2::blackball_output(const crypto::public_key &output) +bool wallet2::blackball_output(const std::pair<uint64_t, uint64_t> &output) { if (!m_ringdb) return false; @@ -6229,7 +6229,7 @@ bool wallet2::blackball_output(const crypto::public_key &output) catch (const std::exception &e) { return false; } } -bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &outputs, bool add) +bool wallet2::set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add) { if (!m_ringdb) return false; @@ -6244,7 +6244,7 @@ bool wallet2::set_blackballed_outputs(const std::vector<crypto::public_key> &out catch (const std::exception &e) { return false; } } -bool wallet2::unblackball_output(const crypto::public_key &output) +bool wallet2::unblackball_output(const std::pair<uint64_t, uint64_t> &output) { if (!m_ringdb) return false; @@ -6252,7 +6252,7 @@ bool wallet2::unblackball_output(const crypto::public_key &output) catch (const std::exception &e) { return false; } } -bool wallet2::is_output_blackballed(const crypto::public_key &output) const +bool wallet2::is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const { if (!m_ringdb) return false; @@ -6297,8 +6297,8 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out 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; +// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs +// return false; outs.back().push_back(item); return true; } @@ -6795,6 +6795,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> if (seen_indices.count(i)) continue; + if (is_output_blackballed(std::make_pair(amount, i))) // don't add blackballed outputs + continue; seen_indices.emplace(i); LOG_PRINT_L2("picking " << i << " as " << type); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 556679f51..f0ed1c0e8 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1161,10 +1161,10 @@ namespace tools bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative); bool find_and_save_rings(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; + bool blackball_output(const std::pair<uint64_t, uint64_t> &output); + bool set_blackballed_outputs(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, bool add = false); + bool unblackball_output(const std::pair<uint64_t, uint64_t> &output); + bool is_output_blackballed(const std::pair<uint64_t, uint64_t> &output) const; bool lock_keys_file(); bool unlock_keys_file(); diff --git a/tests/unit_tests/ringdb.cpp b/tests/unit_tests/ringdb.cpp index 9b842569a..416ae0890 100644 --- a/tests/unit_tests/ringdb.cpp +++ b/tests/unit_tests/ringdb.cpp @@ -59,17 +59,17 @@ static crypto::key_image generate_key_image() return key_image; } -static crypto::public_key generate_output() +static std::pair<uint64_t, uint64_t> generate_output() { - return rct::rct2pk(rct::scalarmultBase(rct::skGen())); + return std::make_pair(rand(), rand()); } static const crypto::chacha_key KEY_1 = generate_chacha_key(); static const crypto::chacha_key KEY_2 = generate_chacha_key(); static const crypto::key_image KEY_IMAGE_1 = generate_key_image(); -static const crypto::public_key OUTPUT_1 = generate_output(); -static const crypto::public_key OUTPUT_2 = generate_output(); +static const std::pair<uint64_t, uint64_t> OUTPUT_1 = generate_output(); +static const std::pair<uint64_t, uint64_t> OUTPUT_2 = generate_output(); class RingDB: public tools::ringdb { |