Merge remote-tracking branch 'upstream/master'
This commit is contained in:
+108
-39
@@ -92,6 +92,13 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t calculate_fee(const cryptonote::blobdata &blob)
|
||||
{
|
||||
uint64_t bytes = blob.size();
|
||||
uint64_t kB = (bytes + 1023) / 1024;
|
||||
return kB * FEE_PER_KB;
|
||||
}
|
||||
|
||||
} //namespace
|
||||
|
||||
namespace tools
|
||||
@@ -490,7 +497,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry
|
||||
//handle transactions from new block
|
||||
|
||||
//optimization: seeking only for blocks that are not older then the wallet creation time plus 1 day. 1 day is for possible user incorrect time setup
|
||||
if(b.timestamp + 60*60*24 > m_account.get_createtime())
|
||||
if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height)
|
||||
{
|
||||
TIME_MEASURE_START(miner_tx_handle_time);
|
||||
process_new_transaction(b.miner_tx, height, true);
|
||||
@@ -1207,7 +1214,7 @@ void wallet2::generate(const std::string& wallet_, const std::string& password,
|
||||
m_account_public_address = account_public_address;
|
||||
m_watch_only = false;
|
||||
|
||||
bool r = store_keys(m_keys_file, password, true);
|
||||
bool r = store_keys(m_keys_file, password, false);
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::file_save_error, m_keys_file);
|
||||
|
||||
r = file_io_utils::save_string_to_file(m_wallet_file + ".address.txt", m_account.get_public_address_str(m_testnet));
|
||||
@@ -2014,13 +2021,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
|
||||
{
|
||||
transfer(dst_vector, fake_outs_count, unlock_time, needed_fee, extra, tx, ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(ptx.tx);
|
||||
uint64_t txSize = txBlob.size();
|
||||
uint64_t numKB = txSize / 1024;
|
||||
if (txSize % 1024)
|
||||
{
|
||||
numKB++;
|
||||
}
|
||||
needed_fee = numKB * FEE_PER_KB;
|
||||
needed_fee = calculate_fee(txBlob);
|
||||
} while (ptx.fee < needed_fee);
|
||||
|
||||
ptx_vector.push_back(ptx);
|
||||
@@ -2392,15 +2393,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
|
||||
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, unlock_time, needed_fee, extra,
|
||||
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
|
||||
uint64_t txSize = txBlob.size();
|
||||
uint64_t numKB = txSize / 1024;
|
||||
if (txSize % 1024)
|
||||
{
|
||||
numKB++;
|
||||
}
|
||||
needed_fee = numKB * FEE_PER_KB;
|
||||
needed_fee = calculate_fee(txBlob);
|
||||
available_for_fee = test_ptx.fee + test_ptx.change_dts.amount;
|
||||
LOG_PRINT_L2("Made a " << numKB << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
|
||||
LOG_PRINT_L2("Made a " << txBlob.size() << " kB tx, with " << print_money(available_for_fee) << " available for fee (" <<
|
||||
print_money(needed_fee) << " needed)");
|
||||
|
||||
if (needed_fee > available_for_fee && dsts[0].amount > 0)
|
||||
@@ -2497,7 +2492,7 @@ uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
|
||||
void wallet2::transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t> &extra, cryptonote::transaction& tx, pending_tx &ptx)
|
||||
{
|
||||
using namespace cryptonote;
|
||||
|
||||
@@ -2507,6 +2502,19 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
|
||||
// throw if there are none
|
||||
uint64_t money = 0;
|
||||
std::list<transfer_container::iterator> selected_transfers;
|
||||
#if 1
|
||||
for (size_t n = 0; n < outs.size(); ++n)
|
||||
{
|
||||
const transfer_details& td = m_transfers[outs[n]];
|
||||
if (!td.m_spent)
|
||||
{
|
||||
selected_transfers.push_back (m_transfers.begin() + outs[n]);
|
||||
money += td.amount();
|
||||
if (selected_transfers.size() >= num_outputs)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (transfer_container::iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
|
||||
{
|
||||
const transfer_details& td = *i;
|
||||
@@ -2518,6 +2526,7 @@ void wallet2::transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t n
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// we don't allow no output to self, easier, but one may want to burn the dust if = fee
|
||||
THROW_WALLET_EXCEPTION_IF(money <= needed_fee, error::not_enough_money, money, needed_fee, needed_fee);
|
||||
@@ -2611,8 +2620,8 @@ bool wallet2::use_fork_rules(uint8_t version)
|
||||
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();
|
||||
CHECK_AND_ASSERT_MES(r, false, "Failed to connect to daemon");
|
||||
CHECK_AND_ASSERT_MES(res.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
|
||||
CHECK_AND_ASSERT_MES(res.status == CORE_RPC_STATUS_OK, false, "Failed to get hard fork status");
|
||||
CHECK_AND_ASSERT_MES(resp_t.result.status != CORE_RPC_STATUS_BUSY, false, "Failed to connect to daemon");
|
||||
CHECK_AND_ASSERT_MES(resp_t.result.status == CORE_RPC_STATUS_OK, false, "Failed to get hard fork status");
|
||||
|
||||
bool close_enough = res.height >= resp_t.result.earliest_height - 10; // start using the rules a bit beforehand
|
||||
if (close_enough)
|
||||
@@ -2630,20 +2639,85 @@ uint64_t wallet2::get_upper_tranaction_size_limit()
|
||||
return ((full_reward_zone * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
|
||||
std::vector<size_t> wallet2::select_available_outputs(const std::function<bool(const transfer_details &td)> &f)
|
||||
{
|
||||
std::vector<size_t> outputs;
|
||||
size_t n = 0;
|
||||
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i, ++n)
|
||||
{
|
||||
if (i->m_spent)
|
||||
continue;
|
||||
if (!is_transfer_unlocked(*i))
|
||||
continue;
|
||||
if (f(*i))
|
||||
outputs.push_back(n);
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::vector<uint64_t> wallet2::get_unspent_amounts_vector()
|
||||
{
|
||||
std::set<uint64_t> set;
|
||||
for (const auto &td: m_transfers)
|
||||
{
|
||||
if (!td.m_spent)
|
||||
set.insert(td.amount());
|
||||
}
|
||||
std::vector<uint64_t> vector;
|
||||
vector.reserve(set.size());
|
||||
for (const auto &i: set)
|
||||
{
|
||||
vector.push_back(i);
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
std::vector<size_t> wallet2::select_available_unmixable_outputs(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();
|
||||
req_t.jsonrpc = "2.0";
|
||||
req_t.id = epee::serialization::storage_entry(0);
|
||||
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.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();
|
||||
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "select_available_unmixable_outputs");
|
||||
THROW_WALLET_EXCEPTION_IF(resp_t.result.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_output_histogram");
|
||||
THROW_WALLET_EXCEPTION_IF(resp_t.result.status != CORE_RPC_STATUS_OK, error::get_histogram_error, resp_t.result.status);
|
||||
|
||||
std::set<uint64_t> mixable;
|
||||
for (const auto &i: resp_t.result.histogram)
|
||||
{
|
||||
mixable.insert(i.amount);
|
||||
}
|
||||
|
||||
return select_available_outputs([mixable](const transfer_details &td) {
|
||||
const uint64_t amount = td.amount();
|
||||
if (mixable.find(amount) == mixable.end())
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
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
|
||||
const bool hf1_rules = use_fork_rules(2); // first hard fork has version 2
|
||||
tx_dust_policy dust_policy(hf1_rules ? 0 : ::config::DEFAULT_DUST_THRESHOLD);
|
||||
|
||||
size_t num_dust_outputs = 0;
|
||||
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
|
||||
// may throw
|
||||
std::vector<size_t> unmixable_outputs = select_available_unmixable_outputs(trusted_daemon);
|
||||
size_t num_dust_outputs = unmixable_outputs.size();
|
||||
|
||||
if (num_dust_outputs == 0)
|
||||
{
|
||||
const transfer_details& td = *i;
|
||||
if (!td.m_spent && (td.amount() < dust_policy.dust_threshold || !is_valid_decomposed_amount(td.amount())) && is_transfer_unlocked(td))
|
||||
{
|
||||
num_dust_outputs++;
|
||||
}
|
||||
return std::vector<wallet2::pending_tx>();
|
||||
}
|
||||
|
||||
// failsafe split attempt counter
|
||||
@@ -2666,23 +2740,18 @@ std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
|
||||
|
||||
// loop until fee is met without increasing tx size to next KB boundary.
|
||||
uint64_t needed_fee = 0;
|
||||
if (1)
|
||||
do
|
||||
{
|
||||
transfer_dust(num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, 0, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
auto txBlob = t_serializable_object_to_blob(ptx.tx);
|
||||
uint64_t txSize = txBlob.size();
|
||||
uint64_t numKB = txSize / 1024;
|
||||
if (txSize % 1024)
|
||||
{
|
||||
numKB++;
|
||||
}
|
||||
needed_fee = numKB * FEE_PER_KB;
|
||||
needed_fee = calculate_fee(txBlob);
|
||||
|
||||
// reroll the tx with the actual amount minus the fee
|
||||
// if there's not enough for the fee, it'll throw
|
||||
transfer_dust(num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
transfer_from(unmixable_outputs, num_outputs_per_tx, (uint64_t)0 /* unlock_time */, needed_fee, detail::digit_split_strategy, dust_policy, extra, tx, ptx);
|
||||
txBlob = t_serializable_object_to_blob(ptx.tx);
|
||||
}
|
||||
needed_fee = calculate_fee(txBlob);
|
||||
} while (ptx.fee < needed_fee);
|
||||
|
||||
ptx_vector.push_back(ptx);
|
||||
|
||||
|
||||
+14
-5
@@ -88,10 +88,10 @@ namespace tools
|
||||
};
|
||||
|
||||
private:
|
||||
wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true) {}
|
||||
wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false), m_always_confirm_transfers (false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0) {}
|
||||
|
||||
public:
|
||||
wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true) {}
|
||||
wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet), m_restricted(restricted), is_old_file_format(false), m_store_tx_info(true), m_default_mixin(0), m_refresh_type(RefreshOptimizeCoinbase), m_auto_refresh(true), m_refresh_from_block_height(0) {}
|
||||
struct transfer_details
|
||||
{
|
||||
uint64_t m_block_height;
|
||||
@@ -226,6 +226,8 @@ namespace tools
|
||||
cryptonote::account_base& get_account(){return m_account;}
|
||||
const cryptonote::account_base& get_account()const{return m_account;}
|
||||
|
||||
void set_refresh_from_block_height(uint64_t height) {m_refresh_from_block_height = height;}
|
||||
|
||||
// upper_transaction_size_limit as defined below is set to
|
||||
// approximately 125% of the fixed minimum allowable penalty
|
||||
// free block size. TODO: fix this so that it actually takes
|
||||
@@ -278,7 +280,7 @@ namespace tools
|
||||
void 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 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);
|
||||
template<typename T>
|
||||
void transfer_dust(size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
|
||||
void transfer_from(const std::vector<size_t> &outs, size_t num_outputs, uint64_t unlock_time, uint64_t needed_fee, T destination_split_strategy, const tx_dust_policy& dust_policy, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
|
||||
template<typename T>
|
||||
void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::list<transfer_container::iterator> selected_transfers, size_t fake_outputs_count,
|
||||
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
|
||||
@@ -287,7 +289,7 @@ namespace tools
|
||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
||||
std::vector<pending_tx> create_transactions(std::vector<cryptonote::tx_destination_entry> dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector<uint8_t> extra);
|
||||
std::vector<wallet2::pending_tx> 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<pending_tx> create_dust_sweep_transactions();
|
||||
std::vector<pending_tx> create_unmixable_sweep_transactions(bool trusted_daemon);
|
||||
bool check_connection();
|
||||
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
|
||||
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
|
||||
@@ -319,6 +321,9 @@ namespace tools
|
||||
if(ver < 9)
|
||||
return;
|
||||
a & m_confirmed_txs;
|
||||
if(ver < 11)
|
||||
return;
|
||||
a & m_refresh_from_block_height;
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -398,6 +403,9 @@ namespace tools
|
||||
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
|
||||
uint64_t get_upper_tranaction_size_limit();
|
||||
void check_pending_txes();
|
||||
std::vector<uint64_t> get_unspent_amounts_vector();
|
||||
std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
|
||||
std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
|
||||
|
||||
cryptonote::account_base m_account;
|
||||
std::string m_daemon_address;
|
||||
@@ -431,9 +439,10 @@ namespace tools
|
||||
uint32_t m_default_mixin;
|
||||
RefreshType m_refresh_type;
|
||||
bool m_auto_refresh;
|
||||
uint64_t m_refresh_from_block_height;
|
||||
};
|
||||
}
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 10)
|
||||
BOOST_CLASS_VERSION(tools::wallet2, 11)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 0)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 2)
|
||||
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 1)
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace tools
|
||||
// daemon_busy
|
||||
// no_connection_to_daemon
|
||||
// is_key_image_spent_error
|
||||
// get_histogram_error
|
||||
// wallet_files_doesnt_correspond
|
||||
//
|
||||
// * - class with protected ctor
|
||||
@@ -600,6 +601,14 @@ namespace tools
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct get_histogram_error : public wallet_rpc_error
|
||||
{
|
||||
explicit get_histogram_error(std::string&& loc, const std::string& request)
|
||||
: wallet_rpc_error(std::move(loc), "failed to get output histogram", request)
|
||||
{
|
||||
}
|
||||
};
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
struct wallet_files_doesnt_correspond : public wallet_logic_error
|
||||
{
|
||||
explicit wallet_files_doesnt_correspond(std::string&& loc, const std::string& keys_file, const std::string& wallet_file)
|
||||
|
||||
@@ -347,7 +347,7 @@ namespace tools
|
||||
|
||||
try
|
||||
{
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_dust_sweep_transactions();
|
||||
std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_unmixable_sweep_transactions(req.trusted_daemon);
|
||||
|
||||
m_wallet.commit_tx(ptx_vector);
|
||||
|
||||
|
||||
@@ -178,9 +178,11 @@ namespace wallet_rpc
|
||||
struct request
|
||||
{
|
||||
bool get_tx_keys;
|
||||
bool trusted_daemon;
|
||||
|
||||
BEGIN_KV_SERIALIZE_MAP()
|
||||
KV_SERIALIZE(get_tx_keys)
|
||||
KV_SERIALIZE(trusted_daemon)
|
||||
END_KV_SERIALIZE_MAP()
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user