Merge pull request #296
a62daeb
wallet_rpc_server: add a sweep_dust RPC command as well (moneromooo-monero)3204f0d
wallet: add a sweep_dust command (moneromooo-monero)
This commit is contained in:
commit
72248cfe6f
7 changed files with 408 additions and 1 deletions
|
@ -295,6 +295,7 @@ simple_wallet::simple_wallet()
|
||||||
m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments <payment_id_1> [<payment_id_2> ... <payment_id_N>] - Show payments <payment_id_1>, ... <payment_id_N>");
|
m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), "payments <payment_id_1> [<payment_id_2> ... <payment_id_N>] - Show payments <payment_id_1>, ... <payment_id_N>");
|
||||||
m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height");
|
m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), "Show blockchain height");
|
||||||
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)");
|
m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer, this, _1), "transfer [<mixin_count>] <addr_1> <amount_1> [<addr_2> <amount_2> ... <addr_N> <amount_N>] [payment_id] - Transfer <amount_1>,... <amount_N> to <address_1>,... <address_N>, respectively. <mixin_count> is the number of transactions yours is indistinguishable from (from 0 to maximum available)");
|
||||||
|
m_cmd_binder.set_handler("sweep_dust", boost::bind(&simple_wallet::sweep_dust, this, _1), "Send all dust outputs to the same address with mixin 0");
|
||||||
m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log <level> - Change current log detalization level, <level> is a number 0-4");
|
m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), "set_log <level> - Change current log detalization level, <level> is a number 0-4");
|
||||||
m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address");
|
m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), "Show current wallet public address");
|
||||||
m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data");
|
m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), "Save wallet synchronized data");
|
||||||
|
@ -942,7 +943,8 @@ bool simple_wallet::refresh(const std::vector<std::string>& args)
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/)
|
bool simple_wallet::show_balance(const std::vector<std::string>& args/* = std::vector<std::string>()*/)
|
||||||
{
|
{
|
||||||
success_msg_writer() << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance());
|
success_msg_writer() << "balance: " << print_money(m_wallet->balance()) << ", unlocked balance: " << print_money(m_wallet->unlocked_balance())
|
||||||
|
<< ", including unlocked dust: " << print_money(m_wallet->unlocked_dust_balance(tools::tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
@ -1307,6 +1309,131 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
bool simple_wallet::sweep_dust(const std::vector<std::string> &args_)
|
||||||
|
{
|
||||||
|
if (!try_connect_to_daemon())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
uint64_t total_dust = m_wallet->unlocked_dust_balance(tools::tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD));
|
||||||
|
|
||||||
|
// figure out what tx will be necessary
|
||||||
|
auto ptx_vector = m_wallet->create_dust_sweep_transactions();
|
||||||
|
|
||||||
|
// give user total and fee, and prompt to confirm
|
||||||
|
uint64_t total_fee = 0;
|
||||||
|
for (size_t n = 0; n < ptx_vector.size(); ++n)
|
||||||
|
{
|
||||||
|
total_fee += ptx_vector[n].fee;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string prompt_str = "Sweeping " + print_money(total_dust);
|
||||||
|
if (ptx_vector.size() > 1) {
|
||||||
|
prompt_str += " in ";
|
||||||
|
prompt_str += std::to_string(ptx_vector.size());
|
||||||
|
prompt_str += " transactions";
|
||||||
|
}
|
||||||
|
prompt_str += " for a total fee of " + print_money(total_fee);
|
||||||
|
prompt_str += ". Is this okay? (Y/Yes/N/No)";
|
||||||
|
std::string accepted = command_line::input_line(prompt_str);
|
||||||
|
if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes")
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "Transaction cancelled.";
|
||||||
|
|
||||||
|
// would like to return false, because no tx made, but everything else returns true
|
||||||
|
// and I don't know what returning false might adversely affect. *sigh*
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually commit the transactions
|
||||||
|
while (!ptx_vector.empty())
|
||||||
|
{
|
||||||
|
auto & ptx = ptx_vector.back();
|
||||||
|
m_wallet->commit_tx(ptx);
|
||||||
|
success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(ptx.tx);
|
||||||
|
|
||||||
|
// if no exception, remove element from vector
|
||||||
|
ptx_vector.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const tools::error::daemon_busy&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "daemon is busy. Please try later";
|
||||||
|
}
|
||||||
|
catch (const tools::error::no_connection_to_daemon&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "no connection to daemon. Please, make sure daemon is running.";
|
||||||
|
}
|
||||||
|
catch (const tools::error::wallet_rpc_error& e)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unknown RPC error: " << e.to_string());
|
||||||
|
fail_msg_writer() << "RPC error \"" << e.what() << '"';
|
||||||
|
}
|
||||||
|
catch (const tools::error::get_random_outs_error&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "failed to get random outputs to mix";
|
||||||
|
}
|
||||||
|
catch (const tools::error::not_enough_money& e)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "not enough money to transfer, available only " << print_money(e.available()) <<
|
||||||
|
", transaction amount " << print_money(e.tx_amount() + e.fee()) << " = " << print_money(e.tx_amount()) <<
|
||||||
|
" + " << print_money(e.fee()) << " (fee)";
|
||||||
|
}
|
||||||
|
catch (const tools::error::not_enough_outs_to_mix& e)
|
||||||
|
{
|
||||||
|
auto writer = fail_msg_writer();
|
||||||
|
writer << "not enough outputs for specified mixin_count = " << e.mixin_count() << ":";
|
||||||
|
for (const cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& outs_for_amount : e.scanty_outs())
|
||||||
|
{
|
||||||
|
writer << "\noutput amount = " << print_money(outs_for_amount.amount) << ", fount outputs to mix = " << outs_for_amount.outs.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const tools::error::tx_not_constructed&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "transaction was not constructed";
|
||||||
|
}
|
||||||
|
catch (const tools::error::tx_rejected& e)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "transaction " << get_transaction_hash(e.tx()) << " was rejected by daemon with status \"" << e.status() << '"';
|
||||||
|
}
|
||||||
|
catch (const tools::error::tx_sum_overflow& e)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << e.what();
|
||||||
|
}
|
||||||
|
catch (const tools::error::zero_destination&)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "one of destinations is zero";
|
||||||
|
}
|
||||||
|
catch (const tools::error::tx_too_big& e)
|
||||||
|
{
|
||||||
|
fail_msg_writer() << "Failed to find a suitable way to split transactions";
|
||||||
|
}
|
||||||
|
catch (const tools::error::transfer_error& e)
|
||||||
|
{
|
||||||
|
LOG_ERROR("unknown transfer error: " << e.to_string());
|
||||||
|
fail_msg_writer() << "unknown transfer error: " << e.what();
|
||||||
|
}
|
||||||
|
catch (const tools::error::wallet_internal_error& e)
|
||||||
|
{
|
||||||
|
LOG_ERROR("internal error: " << e.to_string());
|
||||||
|
fail_msg_writer() << "internal error: " << e.what();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
LOG_ERROR("unexpected error: " << e.what());
|
||||||
|
fail_msg_writer() << "unexpected error: " << e.what();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG_ERROR("Unknown error");
|
||||||
|
fail_msg_writer() << "unknown error";
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::run()
|
bool simple_wallet::run()
|
||||||
{
|
{
|
||||||
|
|
|
@ -103,6 +103,7 @@ namespace cryptonote
|
||||||
bool show_payments(const std::vector<std::string> &args);
|
bool show_payments(const std::vector<std::string> &args);
|
||||||
bool show_blockchain_height(const std::vector<std::string> &args);
|
bool show_blockchain_height(const std::vector<std::string> &args);
|
||||||
bool transfer(const std::vector<std::string> &args);
|
bool transfer(const std::vector<std::string> &args);
|
||||||
|
bool sweep_dust(const std::vector<std::string> &args);
|
||||||
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
|
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
|
||||||
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
|
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
|
||||||
);
|
);
|
||||||
|
|
|
@ -1202,6 +1202,217 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t wallet2::unlocked_dust_balance(const tx_dust_policy &dust_policy) const
|
||||||
|
{
|
||||||
|
uint64_t money = 0;
|
||||||
|
std::list<transfer_container::iterator> selected_transfers;
|
||||||
|
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
|
||||||
|
{
|
||||||
|
const transfer_details& td = *i;
|
||||||
|
if (!td.m_spent && td.amount() < dust_policy.dust_threshold && is_transfer_unlocked(td))
|
||||||
|
{
|
||||||
|
money += td.amount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return money;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
using namespace cryptonote;
|
||||||
|
|
||||||
|
// select all dust inputs for transaction
|
||||||
|
// throw if there are none
|
||||||
|
uint64_t money = 0;
|
||||||
|
std::list<transfer_container::iterator> selected_transfers;
|
||||||
|
for (transfer_container::iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
|
||||||
|
{
|
||||||
|
const transfer_details& td = *i;
|
||||||
|
if (!td.m_spent && td.amount() < dust_policy.dust_threshold && is_transfer_unlocked(td))
|
||||||
|
{
|
||||||
|
selected_transfers.push_back (i);
|
||||||
|
money += td.amount();
|
||||||
|
if (selected_transfers.size() >= num_outputs)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
|
||||||
|
|
||||||
|
//prepare inputs
|
||||||
|
size_t i = 0;
|
||||||
|
std::vector<cryptonote::tx_source_entry> sources;
|
||||||
|
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
|
||||||
|
{
|
||||||
|
sources.resize(sources.size()+1);
|
||||||
|
cryptonote::tx_source_entry& src = sources.back();
|
||||||
|
transfer_details& td = *it;
|
||||||
|
src.amount = td.amount();
|
||||||
|
|
||||||
|
//paste real transaction to the random index
|
||||||
|
auto it_to_insert = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
|
||||||
|
{
|
||||||
|
return a.first >= td.m_global_output_index;
|
||||||
|
});
|
||||||
|
tx_output_entry real_oe;
|
||||||
|
real_oe.first = td.m_global_output_index;
|
||||||
|
real_oe.second = boost::get<txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
|
||||||
|
auto interted_it = src.outputs.insert(it_to_insert, real_oe);
|
||||||
|
src.real_out_tx_key = get_tx_pub_key_from_extra(td.m_tx);
|
||||||
|
src.real_output = interted_it - src.outputs.begin();
|
||||||
|
src.real_output_in_tx_index = td.m_internal_output_index;
|
||||||
|
detail::print_source_entry(src);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptonote::tx_destination_entry change_dts = AUTO_VAL_INIT(change_dts);
|
||||||
|
|
||||||
|
std::vector<cryptonote::tx_destination_entry> dsts;
|
||||||
|
uint64_t money_back = money - needed_fee;
|
||||||
|
if (dust_policy.dust_threshold > 0)
|
||||||
|
money_back = money_back - money_back % dust_policy.dust_threshold;
|
||||||
|
dsts.push_back(cryptonote::tx_destination_entry(money_back, m_account_public_address));
|
||||||
|
uint64_t dust = 0;
|
||||||
|
std::vector<cryptonote::tx_destination_entry> splitted_dsts;
|
||||||
|
destination_split_strategy(dsts, change_dts, dust_policy.dust_threshold, splitted_dsts, dust);
|
||||||
|
THROW_WALLET_EXCEPTION_IF(dust_policy.dust_threshold < dust, error::wallet_internal_error, "invalid dust value: dust = " +
|
||||||
|
std::to_string(dust) + ", dust_threshold = " + std::to_string(dust_policy.dust_threshold));
|
||||||
|
|
||||||
|
bool r = cryptonote::construct_tx(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time);
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, splitted_dsts, unlock_time, m_testnet);
|
||||||
|
THROW_WALLET_EXCEPTION_IF(m_upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, m_upper_transaction_size_limit);
|
||||||
|
|
||||||
|
std::string key_images;
|
||||||
|
bool all_are_txin_to_key = std::all_of(tx.vin.begin(), tx.vin.end(), [&](const txin_v& s_e) -> bool
|
||||||
|
{
|
||||||
|
CHECKED_GET_SPECIFIC_VARIANT(s_e, const txin_to_key, in, false);
|
||||||
|
key_images += boost::to_string(in.k_image) + " ";
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!all_are_txin_to_key, error::unexpected_txin_type, tx);
|
||||||
|
|
||||||
|
ptx.key_images = key_images;
|
||||||
|
ptx.fee = money - money_back;
|
||||||
|
ptx.dust = dust;
|
||||||
|
ptx.tx = tx;
|
||||||
|
ptx.change_dts = change_dts;
|
||||||
|
ptx.selected_transfers = selected_transfers;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
std::vector<wallet2::pending_tx> wallet2::create_dust_sweep_transactions()
|
||||||
|
{
|
||||||
|
tx_dust_policy dust_policy(::config::DEFAULT_DUST_THRESHOLD);
|
||||||
|
|
||||||
|
size_t num_dust_outputs = 0;
|
||||||
|
for (transfer_container::const_iterator i = m_transfers.begin(); i != m_transfers.end(); ++i)
|
||||||
|
{
|
||||||
|
const transfer_details& td = *i;
|
||||||
|
if (!td.m_spent && td.amount() < dust_policy.dust_threshold && is_transfer_unlocked(td))
|
||||||
|
{
|
||||||
|
num_dust_outputs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// failsafe split attempt counter
|
||||||
|
size_t attempt_count = 0;
|
||||||
|
|
||||||
|
for(attempt_count = 1; ;attempt_count++)
|
||||||
|
{
|
||||||
|
size_t num_outputs_per_tx = (num_dust_outputs + attempt_count - 1) / attempt_count;
|
||||||
|
|
||||||
|
std::vector<pending_tx> ptx_vector;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// for each new tx
|
||||||
|
for (size_t i=0; i<attempt_count;++i)
|
||||||
|
{
|
||||||
|
cryptonote::transaction tx;
|
||||||
|
pending_tx ptx;
|
||||||
|
std::vector<uint8_t> extra;
|
||||||
|
|
||||||
|
// loop until fee is met without increasing tx size to next KB boundary.
|
||||||
|
uint64_t needed_fee = 0;
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
transfer_dust(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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
txBlob = t_serializable_object_to_blob(ptx.tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptx_vector.push_back(ptx);
|
||||||
|
|
||||||
|
// mark transfers to be used as "spent"
|
||||||
|
BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers)
|
||||||
|
it->m_spent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we made it this far, we've selected our transactions. committing them will mark them spent,
|
||||||
|
// so this is a failsafe in case they don't go through
|
||||||
|
// unmark pending tx transfers as spent
|
||||||
|
for (auto & ptx : ptx_vector)
|
||||||
|
{
|
||||||
|
// mark transfers to be used as not spent
|
||||||
|
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
|
||||||
|
it2->m_spent = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we made it this far, we're OK to actually send the transactions
|
||||||
|
return ptx_vector;
|
||||||
|
|
||||||
|
}
|
||||||
|
// only catch this here, other exceptions need to pass through to the calling function
|
||||||
|
catch (const tools::error::tx_too_big& e)
|
||||||
|
{
|
||||||
|
|
||||||
|
// unmark pending tx transfers as spent
|
||||||
|
for (auto & ptx : ptx_vector)
|
||||||
|
{
|
||||||
|
// mark transfers to be used as not spent
|
||||||
|
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
|
||||||
|
it2->m_spent = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt_count >= MAX_SPLIT_ATTEMPTS)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// in case of some other exception, make sure any tx in queue are marked unspent again
|
||||||
|
|
||||||
|
// unmark pending tx transfers as spent
|
||||||
|
for (auto & ptx : ptx_vector)
|
||||||
|
{
|
||||||
|
// mark transfers to be used as not spent
|
||||||
|
BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers)
|
||||||
|
it2->m_spent = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::generate_genesis(cryptonote::block& b) {
|
void wallet2::generate_genesis(cryptonote::block& b) {
|
||||||
if (m_testnet)
|
if (m_testnet)
|
||||||
|
|
|
@ -201,15 +201,19 @@ namespace tools
|
||||||
|
|
||||||
uint64_t balance() const;
|
uint64_t balance() const;
|
||||||
uint64_t unlocked_balance() const;
|
uint64_t unlocked_balance() const;
|
||||||
|
uint64_t unlocked_dust_balance(const tx_dust_policy &dust_policy) const;
|
||||||
template<typename T>
|
template<typename T>
|
||||||
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, T destination_split_strategy, const tx_dust_policy& dust_policy);
|
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, T destination_split_strategy, const tx_dust_policy& dust_policy);
|
||||||
template<typename T>
|
template<typename T>
|
||||||
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, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx);
|
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, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx);
|
||||||
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);
|
||||||
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);
|
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 commit_tx(pending_tx& ptx_vector);
|
void commit_tx(pending_tx& ptx_vector);
|
||||||
void commit_tx(std::vector<pending_tx>& ptx_vector);
|
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<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<pending_tx> create_dust_sweep_transactions();
|
||||||
bool check_connection();
|
bool check_connection();
|
||||||
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
|
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;
|
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0) const;
|
||||||
|
|
|
@ -273,6 +273,50 @@ namespace tools
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool wallet_rpc_server::on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er)
|
||||||
|
{
|
||||||
|
if (m_wallet.restricted())
|
||||||
|
{
|
||||||
|
er.code = WALLET_RPC_ERROR_CODE_DENIED;
|
||||||
|
er.message = "Command unavailable in restricted mode.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_dust_sweep_transactions();
|
||||||
|
|
||||||
|
m_wallet.commit_tx(ptx_vector);
|
||||||
|
|
||||||
|
// populate response with tx hashes
|
||||||
|
for (auto & ptx : ptx_vector)
|
||||||
|
{
|
||||||
|
res.tx_hash_list.push_back(boost::lexical_cast<std::string>(cryptonote::get_transaction_hash(ptx.tx)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (const tools::error::daemon_busy& e)
|
||||||
|
{
|
||||||
|
er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY;
|
||||||
|
er.message = e.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
|
||||||
|
er.message = e.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
|
||||||
|
er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er)
|
bool wallet_rpc_server::on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er)
|
||||||
{
|
{
|
||||||
if (m_wallet.restricted())
|
if (m_wallet.restricted())
|
||||||
|
|
|
@ -65,6 +65,7 @@ namespace tools
|
||||||
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
|
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
|
||||||
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
|
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
|
||||||
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
|
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
|
||||||
|
MAP_JON_RPC_WE("sweep_dust", on_sweep_dust, wallet_rpc::COMMAND_RPC_SWEEP_DUST)
|
||||||
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
|
MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE)
|
||||||
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
|
MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS)
|
||||||
MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS)
|
MAP_JON_RPC_WE("get_bulk_payments", on_get_bulk_payments, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS)
|
||||||
|
@ -79,6 +80,7 @@ namespace tools
|
||||||
bool validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, const std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
|
bool validate_transfer(const std::list<wallet_rpc::transfer_destination> destinations, const std::string payment_id, std::vector<cryptonote::tx_destination_entry>& dsts, std::vector<uint8_t>& extra, epee::json_rpc::error& er);
|
||||||
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
|
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er);
|
||||||
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
|
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er);
|
||||||
|
bool on_sweep_dust(const wallet_rpc::COMMAND_RPC_SWEEP_DUST::request& req, wallet_rpc::COMMAND_RPC_SWEEP_DUST::response& res, epee::json_rpc::error& er);
|
||||||
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er);
|
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er);
|
||||||
bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er);
|
bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er);
|
||||||
bool on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er);
|
bool on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er);
|
||||||
|
|
|
@ -146,6 +146,24 @@ namespace wallet_rpc
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct COMMAND_RPC_SWEEP_DUST
|
||||||
|
{
|
||||||
|
struct request
|
||||||
|
{
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response
|
||||||
|
{
|
||||||
|
std::list<std::string> tx_hash_list;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(tx_hash_list)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
struct COMMAND_RPC_STORE
|
struct COMMAND_RPC_STORE
|
||||||
{
|
{
|
||||||
struct request
|
struct request
|
||||||
|
|
Loading…
Reference in a new issue