diff options
Diffstat (limited to 'src/simplewallet/simplewallet.cpp')
-rw-r--r-- | src/simplewallet/simplewallet.cpp | 225 |
1 files changed, 144 insertions, 81 deletions
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4acf30b27..5576a024f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -90,8 +90,6 @@ typedef cryptonote::simple_wallet sw; #define MIN_RING_SIZE 7 // Used to inform user about min ring size -- does not track actual protocol -#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" - #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ m_auto_refresh_enabled.store(false, std::memory_order_relaxed); \ @@ -508,7 +506,7 @@ namespace } if (warn_of_possible_attack) - fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a tranasction immediately. Alternatively, connect to another node so the original node cannot correlate information."); + fail_msg_writer() << tr("There was an error, which could mean the node may be trying to get you to retry creating a transaction, and zero in on which outputs you own. Or it could be a bona fide error. It may be prudent to disconnect from this node, and not try to send a transaction immediately. Alternatively, connect to another node so the original node cannot correlate information."); } bool check_file_overwrite(const std::string &filename) @@ -1371,9 +1369,117 @@ bool simple_wallet::print_ring(const std::vector<std::string> &args) bool simple_wallet::set_ring(const std::vector<std::string> &args) { crypto::key_image key_image; + + // try filename first + if (args.size() == 1) + { + if (!epee::file_io_utils::is_file_exist(args[0])) + { + fail_msg_writer() << tr("File doesn't exist"); + return true; + } + + char str[4096]; + 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; + char key_image_str[65], type_str[9]; + int read_after_key_image = 0, read = 0; + int fields = sscanf(str, "%64[abcdefABCDEF0123456789] %n%8s %n", key_image_str, &read_after_key_image, type_str, &read); + if (fields != 2) + { + fail_msg_writer() << tr("Invalid ring specification: ") << str; + continue; + } + key_image_str[64] = 0; + type_str[8] = 0; + crypto::key_image key_image; + if (read_after_key_image == 0 || !epee::string_tools::hex_to_pod(key_image_str, key_image)) + { + fail_msg_writer() << tr("Invalid key image: ") << str; + continue; + } + if (read == read_after_key_image+8 || (strcmp(type_str, "absolute") && strcmp(type_str, "relative"))) + { + fail_msg_writer() << tr("Invalid ring type, expected relative or abosolute: ") << str; + continue; + } + bool relative = !strcmp(type_str, "relative"); + if (read < 0 || (size_t)read > strlen(str)) + { + fail_msg_writer() << tr("Error reading line: ") << str; + continue; + } + bool valid = true; + std::vector<uint64_t> ring; + const char *ptr = str + read; + while (*ptr) + { + unsigned long offset; + int elements = sscanf(ptr, "%lu %n", &offset, &read); + if (elements == 0 || read <= 0 || (size_t)read > strlen(str)) + { + fail_msg_writer() << tr("Error reading line: ") << str; + valid = false; + break; + } + ring.push_back(offset); + ptr += read; + MGINFO("read offset: " << offset); + } + if (!valid) + continue; + if (ring.empty()) + { + fail_msg_writer() << tr("Invalid ring: ") << str; + continue; + } + if (relative) + { + for (size_t n = 1; n < ring.size(); ++n) + { + if (ring[n] <= 0) + { + fail_msg_writer() << tr("Invalid relative ring: ") << str; + valid = false; + break; + } + } + } + else + { + for (size_t n = 1; n < ring.size(); ++n) + { + if (ring[n] <= ring[n-1]) + { + fail_msg_writer() << tr("Invalid absolute ring: ") << str; + valid = false; + break; + } + } + } + if (!valid) + continue; + if (!m_wallet->set_ring(key_image, ring, relative)) + fail_msg_writer() << tr("Failed to set ring for key image: ") << key_image << ". " << tr("Continuing."); + } + f.reset(); + } + return true; + } + if (args.size() < 3) { - fail_msg_writer() << tr("usage: set_ring <key_image> absolute|relative <index> [<index>...]"); + fail_msg_writer() << tr("usage: set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )"); return true; } @@ -2082,8 +2188,8 @@ simple_wallet::simple_wallet() tr("Donate <amount> to the development team (donate.getmonero.org).")); m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), - tr("sign_transfer [export]"), - tr("Sign a transaction from a file.")); + tr("sign_transfer [export_raw]"), + tr("Sign a transaction from a file. If the parameter \"export_raw\" is specified, transaction raw hex data suitable for the daemon RPC /sendrawtransaction is exported.")); m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file.")); @@ -2325,7 +2431,7 @@ simple_wallet::simple_wallet() tr("Print the ring(s) used to spend a given key image or transaction (if the ring size is > 1)")); m_cmd_binder.set_handler("set_ring", boost::bind(&simple_wallet::set_ring, this, _1), - tr("set_ring <key_image> absolute|relative <index> [<index>...]"), + tr("set_ring <filename> | ( <key_image> absolute|relative <index> [<index>...] )"), tr("Set the ring used for a given key image, so it can be reused in a fork")); m_cmd_binder.set_handler("save_known_rings", boost::bind(&simple_wallet::save_known_rings, this, _1), @@ -2451,8 +2557,24 @@ bool simple_wallet::set_log(const std::vector<std::string> &args) fail_msg_writer() << tr("usage: set_log <log_level_number_0-4> | <categories>"); return true; } - if (!args.empty()) - mlog_set_log(args[0].c_str()); + if(!args.empty()) + { + uint16_t level = 0; + if(epee::string_tools::get_xtype_from_string(level, args[0])) + { + if(4 < level) + { + fail_msg_writer() << tr("wrong number range, use: set_log <log_level_number_0-4> | <categories>"); + return true; + } + mlog_set_log_level(level); + } + else + { + mlog_set_log(args[0].c_str()); + } + } + success_msg_writer() << "New log categories: " << mlog_get_categories(); return true; } @@ -3968,6 +4090,8 @@ bool simple_wallet::show_balance_unlocked(bool detailed) std::string extra; if (m_wallet->has_multisig_partial_key_images()) extra = tr(" (Some owned outputs have partial key images - import_multisig_info needed)"); + else if (m_wallet->has_unknown_key_images()) + extra += tr(" (Some owned outputs have missing key images - import_key_images needed)"); success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0}); const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account]; success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag); @@ -5496,9 +5620,9 @@ bool simple_wallet::sign_transfer(const std::vector<std::string> &args_) fail_msg_writer() << tr("This is a watch only wallet"); return true; } - if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export")) + if (args_.size() > 1 || (args_.size() == 1 && args_[0] != "export_raw")) { - fail_msg_writer() << tr("usage: sign_transfer [export]"); + fail_msg_writer() << tr("usage: sign_transfer [export_raw]"); return true; } if (m_wallet->ask_password() && !get_and_verify_password()) { return true; } @@ -6043,10 +6167,7 @@ static std::string get_human_readable_timestamp(uint64_t ts) #endif uint64_t now = time(NULL); uint64_t diff = ts > now ? ts - now : now - ts; - if (diff > 24*3600) - strftime(buffer, sizeof(buffer), "%Y-%m-%d", &tm); - else - strftime(buffer, sizeof(buffer), "%I:%M:%S %p", &tm); + strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm); return std::string(buffer); } //---------------------------------------------------------------------------------------------------- @@ -6158,7 +6279,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(pd.m_tx_hash); - output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%16.16s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); + output.insert(std::make_pair(pd.m_block_height, std::make_pair(true, (boost::format("%25.25s %20.20s %s %s %d %s %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note).str()))); } } @@ -6191,7 +6312,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); std::string note = m_wallet->get_tx_note(i->first); - output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%16.16s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); + output.insert(std::make_pair(pd.m_block_height, std::make_pair(false, (boost::format("%25.25s %20.20s %s %s %14.14s %s %s - %s") % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount_in - change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % dests % print_subaddr_indices(pd.m_subaddr_indices) % note).str()))); } } @@ -6217,7 +6338,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) std::string double_spend_note; if (i->second.m_double_spend_seen) double_spend_note = tr("[Double spend seen on the network: this transaction may or may not end up being mined] "); - message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); + message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %d %s %s%s") % "pool" % "in" % get_human_readable_timestamp(pd.m_timestamp) % print_money(pd.m_amount) % string_tools::pod_to_hex(pd.m_tx_hash) % payment_id % pd.m_subaddr_index.minor % "-" % note % double_spend_note).str(); } } catch (const std::exception& e) @@ -6240,7 +6361,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_) std::string note = m_wallet->get_tx_note(i->first); bool is_failed = pd.m_state == tools::wallet2::unconfirmed_transfer_details::failed; if ((failed && is_failed) || (!is_failed && pending)) { - message_writer() << (boost::format("%8.8s %6.6s %16.16s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str(); + message_writer() << (boost::format("%8.8s %6.6s %25.25s %20.20s %s %s %14.14s %s - %s") % (is_failed ? tr("failed") : tr("pending")) % tr("out") % get_human_readable_timestamp(pd.m_timestamp) % print_money(amount - pd.m_change - fee) % string_tools::pod_to_hex(i->first) % payment_id % print_money(fee) % print_subaddr_indices(pd.m_subaddr_indices) % note).str(); } } } @@ -7209,19 +7330,8 @@ bool simple_wallet::export_outputs(const std::vector<std::string> &args) LOCK_IDLE_SCOPE(); try { - std::vector<tools::wallet2::transfer_details> outs = m_wallet->export_outputs(); - - std::stringstream oss; - boost::archive::portable_binary_oarchive ar(oss); - ar << outs; - - std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); - const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; - std::string header; - header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); - header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); - std::string ciphertext = m_wallet->encrypt_with_view_secret_key(header + oss.str()); - bool r = epee::file_io_utils::save_string_to_file(filename, magic + ciphertext); + std::string data = m_wallet->export_outputs_to_str(); + bool r = epee::file_io_utils::save_string_to_file(filename, data); if (!r) { fail_msg_writer() << tr("failed to save file ") << filename; @@ -7260,63 +7370,16 @@ bool simple_wallet::import_outputs(const std::vector<std::string> &args) fail_msg_writer() << tr("failed to read file ") << filename; return true; } - const size_t magiclen = strlen(OUTPUT_EXPORT_FILE_MAGIC); - if (data.size() < magiclen || memcmp(data.data(), OUTPUT_EXPORT_FILE_MAGIC, magiclen)) - { - fail_msg_writer() << "Bad output export file magic in " << filename; - return true; - } try { - data = m_wallet->decrypt_with_view_secret_key(std::string(data, magiclen)); - } - catch (const std::exception &e) - { - fail_msg_writer() << "Failed to decrypt " << filename << ": " << e.what(); - return true; - } - - const size_t headerlen = 2 * sizeof(crypto::public_key); - if (data.size() < headerlen) - { - fail_msg_writer() << "Bad data size from file " << filename; - return true; - } - const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[0]; - const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[sizeof(crypto::public_key)]; - const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; - if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) - { - fail_msg_writer() << "Outputs from " << filename << " are for a different account"; - return true; - } - - try - { - std::string body(data, headerlen); - std::stringstream iss; - iss << body; - std::vector<tools::wallet2::transfer_details> outputs; - try - { - boost::archive::portable_binary_iarchive ar(iss); - ar >> outputs; - } - catch (...) - { - iss.str(""); - iss << body; - boost::archive::binary_iarchive ar(iss); - ar >> outputs; - } LOCK_IDLE_SCOPE(); - size_t n_outputs = m_wallet->import_outputs(outputs); + size_t n_outputs = m_wallet->import_outputs_from_str(data); success_msg_writer() << boost::lexical_cast<std::string>(n_outputs) << " outputs imported"; } catch (const std::exception &e) { - fail_msg_writer() << "Failed to import outputs: " << e.what(); + fail_msg_writer() << "Failed to import outputs " << filename << ": " << e.what(); return true; } |