aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
authorIlya Kitaev <mbg033@gmail.com>2016-04-26 11:08:40 +0300
committerIlya Kitaev <mbg033@gmail.com>2016-04-26 11:08:40 +0300
commitdce5374660cc5c6fcf806fd6198f7da4ec5cbe5a (patch)
tree134ef267ac273d3f8cda304a1d876502c655372a /src/wallet/wallet2.cpp
parentWallet API: transaction history in progress (diff)
parentMerge pull request #803 (diff)
downloadmonero-dce5374660cc5c6fcf806fd6198f7da4ec5cbe5a.tar.xz
Merge remote-tracking branch 'upstream/master' into develop
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp152
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