diff options
author | Ilya Kitaev <mbg033@gmail.com> | 2016-04-26 11:08:40 +0300 |
---|---|---|
committer | Ilya Kitaev <mbg033@gmail.com> | 2016-04-26 11:08:40 +0300 |
commit | dce5374660cc5c6fcf806fd6198f7da4ec5cbe5a (patch) | |
tree | 134ef267ac273d3f8cda304a1d876502c655372a /src/wallet/wallet2.cpp | |
parent | Wallet API: transaction history in progress (diff) | |
parent | Merge pull request #803 (diff) | |
download | monero-dce5374660cc5c6fcf806fd6198f7da4ec5cbe5a.tar.xz |
Merge remote-tracking branch 'upstream/master' into develop
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r-- | src/wallet/wallet2.cpp | 152 |
1 files changed, 76 insertions, 76 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e704951e1..1be715dbd 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -52,6 +52,7 @@ using namespace epee; #include "rapidjson/document.h" #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" +#include "common/json_util.h" extern "C" { @@ -72,6 +73,7 @@ using namespace cryptonote; #define KILL_IOSERVICE() \ do { \ work.reset(); \ + while (!ioservice.stopped()) ioservice.poll(); \ threadpool.join_all(); \ ioservice.stop(); \ } while(0) @@ -1004,7 +1006,7 @@ namespace * \param keys_file_name Name of wallet file * \param password Password of wallet file */ -void wallet2::load_keys(const std::string& keys_file_name, const std::string& password) +bool wallet2::load_keys(const std::string& keys_file_name, const std::string& password) { wallet2::keys_file_data keys_file_data; std::string buf; @@ -1033,34 +1035,44 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa } else { - account_data = std::string(json["key_data"].GetString(), json["key_data"].GetString() + - json["key_data"].GetStringLength()); - if (json.HasMember("seed_language")) + if (!json.HasMember("key_data")) { - set_seed_language(std::string(json["seed_language"].GetString(), json["seed_language"].GetString() + - json["seed_language"].GetStringLength())); + LOG_ERROR("Field key_data not found in JSON"); + return false; } - if (json.HasMember("watch_only")) + if (!json["key_data"].IsString()) { - m_watch_only = json["watch_only"].GetInt() != 0; + LOG_ERROR("Field key_data found in JSON, but not String"); + return false; } - else + const char *field_key_data = json["key_data"].GetString(); + account_data = std::string(field_key_data, field_key_data + json["key_data"].GetStringLength()); + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, seed_language, std::string, String, false); + if (field_seed_language_found) { - m_watch_only = false; - } - m_always_confirm_transfers = json.HasMember("always_confirm_transfers") && (json["always_confirm_transfers"].GetInt() != 0); - m_store_tx_info = (json.HasMember("store_tx_keys") && (json["store_tx_keys"].GetInt() != 0)) - || (json.HasMember("store_tx_info") && (json["store_tx_info"].GetInt() != 0)); - m_default_mixin = json.HasMember("default_mixin") ? json["default_mixin"].GetUint() : 0; - m_auto_refresh = !json.HasMember("auto_refresh") || (json["auto_refresh"].GetInt() != 0); + set_seed_language(field_seed_language); + } + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, watch_only, int, Int, false); + m_watch_only = field_watch_only_found && field_watch_only; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, always_confirm_transfers, int, Int, false); + m_always_confirm_transfers = field_always_confirm_transfers_found && field_always_confirm_transfers; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_keys, int, Int, false); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, store_tx_info, int, Int, false); + m_store_tx_info = (field_store_tx_keys_found && (field_store_tx_keys != 0)) + || (field_store_tx_info_found && (field_store_tx_info != 0)); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, default_mixin, unsigned int, Uint, false); + m_default_mixin = field_default_mixin_found ? field_default_mixin : 0; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, auto_refresh, int, Int, false); + m_auto_refresh = !field_auto_refresh_found || (field_auto_refresh != 0); + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_type, int, Int, false); m_refresh_type = RefreshType::RefreshDefault; - if (json.HasMember("refresh_type")) + if (field_refresh_type_found) { - int type = json["refresh_type"].GetInt(); - if (type == RefreshFull || type == RefreshOptimizeCoinbase || type == RefreshNoCoinbase) - m_refresh_type = (RefreshType)type; + if (field_refresh_type == RefreshFull || field_refresh_type == RefreshOptimizeCoinbase || field_refresh_type == RefreshNoCoinbase) + m_refresh_type = (RefreshType)field_refresh_type; else - LOG_PRINT_L0("Unknown refresh-type value (" << type << "), using default"); + LOG_PRINT_L0("Unknown refresh-type value (" << field_refresh_type << "), using default"); } } @@ -1070,6 +1082,7 @@ void wallet2::load_keys(const std::string& keys_file_name, const std::string& pa if(!m_watch_only) r = r && verify_keys(keys.m_spend_secret_key, keys.m_account_address.m_spend_public_key); THROW_WALLET_EXCEPTION_IF(!r, error::invalid_password); + return true; } /*! @@ -1358,7 +1371,10 @@ void wallet2::load(const std::string& wallet_, const std::string& password) bool exists = boost::filesystem::exists(m_keys_file, e); THROW_WALLET_EXCEPTION_IF(e || !exists, error::file_not_found, m_keys_file); - load_keys(m_keys_file, password); + if (!load_keys(m_keys_file, password)) + { + THROW_WALLET_EXCEPTION_IF(true, error::file_read_error, m_keys_file); + } LOG_PRINT_L0("Loaded wallet keys file, with public address: " << m_account.get_public_address_str(m_testnet)); //keys loaded ok! @@ -1718,47 +1734,12 @@ namespace // returns: // direct return: amount of money found // modified reference: selected_transfers, a list of iterators/indices of input sources -uint64_t wallet2::select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, bool hf2_rules, std::list<transfer_container::iterator>& selected_transfers) +uint64_t wallet2::select_transfers(uint64_t needed_money, std::vector<size_t> unused_transfers_indices, std::list<transfer_container::iterator>& selected_transfers, bool trusted_daemon) { - std::vector<size_t> unused_transfers_indices; - std::vector<size_t> unused_dust_indices; - - // aggregate sources available for transfers - // if dust needed, take dust from only one source (so require source has at least dust amount) - for (size_t i = 0; i < m_transfers.size(); ++i) - { - const transfer_details& td = m_transfers[i]; - if (!td.m_spent && is_transfer_unlocked(td)) - { - if (dust < td.amount() && is_valid_decomposed_amount(td.amount())) - unused_transfers_indices.push_back(i); - else - { - // for hf2 rules, we disregard dust, which will be spendable only - // via sweep_dust. If we're asked to add dust, though, we still - // consider them, as this will be a mixin 0 tx (and thus we may - // end up with a tx with one mixable output and N dusty ones). - // This should be made better at some point... - if (!hf2_rules || add_dust) - unused_dust_indices.push_back(i); - } - } - } - - bool select_one_dust = add_dust && !unused_dust_indices.empty(); uint64_t found_money = 0; - while (found_money < needed_money && (!unused_transfers_indices.empty() || !unused_dust_indices.empty())) + while (found_money < needed_money && !unused_transfers_indices.empty()) { - size_t idx; - if (select_one_dust) - { - idx = pop_random_value(unused_dust_indices); - select_one_dust = false; - } - else - { - idx = !unused_transfers_indices.empty() ? pop_random_value(unused_transfers_indices) : pop_random_value(unused_dust_indices); - } + size_t idx = pop_random_value(unused_transfers_indices); transfer_container::iterator it = m_transfers.begin() + idx; selected_transfers.push_back(it); @@ -1780,18 +1761,18 @@ void wallet2::add_unconfirmed_tx(const cryptonote::transaction& tx, const std::v } //---------------------------------------------------------------------------------------------------- -void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx) +void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices, + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx& ptx, bool trusted_daemon) { - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx); + transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx, trusted_daemon); } //---------------------------------------------------------------------------------------------------- -void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, size_t fake_outputs_count, - uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra) +void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts, const size_t fake_outs_count, const std::vector<size_t> &unused_transfers_indices, + uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, bool trusted_daemon) { cryptonote::transaction tx; pending_tx ptx; - transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx, ptx); + transfer(dsts, fake_outs_count, unused_transfers_indices, unlock_time, fee, extra, tx, ptx, trusted_daemon); } namespace { @@ -1943,13 +1924,14 @@ void wallet2::commit_tx(pending_tx& ptx) COMMAND_RPC_SEND_RAW_TX::request req; req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); + req.do_not_relay = false; COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; m_daemon_rpc_mutex.lock(); bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status, daemon_send_resp.reason); txid = get_transaction_hash(ptx.tx); crypto::hash payment_id = cryptonote::null_hash; @@ -1988,8 +1970,9 @@ void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector) // // this function will make multiple calls to wallet2::transfer if multiple // transactions will be required -std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra) +std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra, bool trusted_daemon) { + const std::vector<size_t> unused_transfers_indices = select_available_outputs_from_histogram(fake_outs_count + 1, true, trusted_daemon); // failsafe split attempt counter size_t attempt_count = 0; @@ -2019,7 +2002,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto uint64_t needed_fee = 0; do { - transfer(dst_vector, fake_outs_count, unlock_time, needed_fee, extra, tx, ptx); + transfer(dst_vector, fake_outs_count, unused_transfers_indices, unlock_time, needed_fee, extra, tx, ptx, trusted_daemon); auto txBlob = t_serializable_object_to_blob(ptx.tx); needed_fee = calculate_fee(txBlob); } while (ptx.fee < needed_fee); @@ -2252,7 +2235,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent // This system allows for sending (almost) the entire balance, since it does // not generate spurious change in all txes, thus decreasing the instantaneous // usable balance. -std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra) +std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector<uint8_t> extra, bool trusted_daemon) { std::vector<size_t> unused_transfers_indices; std::vector<size_t> unused_dust_indices; @@ -2672,9 +2655,8 @@ std::vector<uint64_t> wallet2::get_unspent_amounts_vector() return vector; } //---------------------------------------------------------------------------------------------------- -std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon) +std::vector<size_t> wallet2::select_available_outputs_from_histogram(uint64_t count, bool atleast, bool trusted_daemon) { - // request all outputs with at least 3 instances, so we can use mixin 2 with epee::json_rpc::request<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request> req_t = AUTO_VAL_INIT(req_t); epee::json_rpc::response<cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response, std::string> resp_t = AUTO_VAL_INIT(resp_t); m_daemon_rpc_mutex.lock(); @@ -2683,7 +2665,7 @@ std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_dae req_t.method = "get_output_histogram"; if (trusted_daemon) req_t.params.amounts = get_unspent_amounts_vector(); - req_t.params.min_count = 3; + req_t.params.min_count = count; req_t.params.max_count = 0; bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/json_rpc", req_t, resp_t, m_http_client); m_daemon_rpc_mutex.unlock(); @@ -2697,14 +2679,32 @@ std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_dae mixable.insert(i.amount); } - return select_available_outputs([mixable](const transfer_details &td) { + return select_available_outputs([mixable, atleast](const transfer_details &td) { const uint64_t amount = td.amount(); - if (mixable.find(amount) == mixable.end()) - return true; + if (atleast) { + if (mixable.find(amount) != mixable.end()) + return true; + } + else { + if (mixable.find(amount) == mixable.end()) + return true; + } return false; }); } //---------------------------------------------------------------------------------------------------- +std::vector<size_t> wallet2::select_available_unmixable_outputs(bool trusted_daemon) +{ + // request all outputs with less than 3 instances + return select_available_outputs_from_histogram(3, false, trusted_daemon); +} +//---------------------------------------------------------------------------------------------------- +std::vector<size_t> wallet2::select_available_mixable_outputs(bool trusted_daemon) +{ + // request all outputs with at least 3 instances, so we can use mixin 2 with + return select_available_outputs_from_histogram(3, true, trusted_daemon); +} +//---------------------------------------------------------------------------------------------------- std::vector<wallet2::pending_tx> wallet2::create_unmixable_sweep_transactions(bool trusted_daemon) { // From hard fork 1, we don't consider small amounts to be dust anymore |