wallet RPC converted to use new transaction semantics
wallet RPC now uses wallet2::create_transactions and wallet2::commit_tx instead of wallet2::transfer. This made it possible to add the RPC call /transfer_split, which will split transactions automatically if they are too large. The old call to /transfer will return an error stating to use /transfer_split if multiple transactions are needed to fulfill the request.
This commit is contained in:
parent
2011b50280
commit
d433a696e5
|
@ -786,169 +786,6 @@ bool simple_wallet::show_blockchain_height(const std::vector<std::string>& args)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
|
|
||||||
//
|
|
||||||
// split amount for each dst in dsts into num_splits parts
|
|
||||||
// and make num_splits new vector<crypt...> instances to hold these new amounts
|
|
||||||
std::vector<std::vector<cryptonote::tx_destination_entry>> simple_wallet::split_amounts(
|
|
||||||
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
|
|
||||||
{
|
|
||||||
std::vector<std::vector<cryptonote::tx_destination_entry>> retVal;
|
|
||||||
|
|
||||||
if (num_splits <= 1)
|
|
||||||
{
|
|
||||||
retVal.push_back(dsts);
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for each split required
|
|
||||||
for (size_t i=0; i < num_splits; i++)
|
|
||||||
{
|
|
||||||
std::vector<cryptonote::tx_destination_entry> new_dsts;
|
|
||||||
|
|
||||||
// for each destination
|
|
||||||
for (size_t j=0; j < dsts.size(); j++)
|
|
||||||
{
|
|
||||||
cryptonote::tx_destination_entry de;
|
|
||||||
uint64_t amount;
|
|
||||||
|
|
||||||
amount = dsts[j].amount;
|
|
||||||
amount = amount / num_splits;
|
|
||||||
|
|
||||||
// if last split, add remainder
|
|
||||||
if (i + 1 == num_splits)
|
|
||||||
{
|
|
||||||
amount += dsts[j].amount % num_splits;
|
|
||||||
}
|
|
||||||
|
|
||||||
de.addr = dsts[j].addr;
|
|
||||||
de.amount = amount;
|
|
||||||
|
|
||||||
new_dsts.push_back(de);
|
|
||||||
}
|
|
||||||
|
|
||||||
retVal.push_back(new_dsts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
|
||||||
// separated the call(s) to wallet2::transfer into their own function
|
|
||||||
//
|
|
||||||
// this function will make multiple calls to wallet2::transfer if multiple
|
|
||||||
// transactions will be required
|
|
||||||
void simple_wallet::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)
|
|
||||||
{
|
|
||||||
// for now, limit to 30 attempts. TODO: discuss a good number to limit to.
|
|
||||||
const size_t MAX_ATTEMPTS = 30;
|
|
||||||
|
|
||||||
// failsafe split attempt counter
|
|
||||||
size_t attempt_count = 0;
|
|
||||||
|
|
||||||
for(attempt_count = 1; ;attempt_count++)
|
|
||||||
{
|
|
||||||
auto split_values = split_amounts(dsts, attempt_count);
|
|
||||||
|
|
||||||
// Throw if split_amounts comes back with a vector of size different than it should
|
|
||||||
if (split_values.size() != attempt_count)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<tools::wallet2::pending_tx> ptx_vector;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// for each new destination vector (i.e. for each new tx)
|
|
||||||
for (auto & dst_vector : split_values)
|
|
||||||
{
|
|
||||||
cryptonote::transaction tx;
|
|
||||||
tools::wallet2::pending_tx ptx;
|
|
||||||
m_wallet->transfer(dst_vector, fake_outs_count, unlock_time, fee, extra, tx, ptx);
|
|
||||||
ptx_vector.push_back(ptx);
|
|
||||||
|
|
||||||
// mark transfers to be used as "spent"
|
|
||||||
BOOST_FOREACH(tools::wallet2::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(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers)
|
|
||||||
it2->m_spent = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// prompt user to confirm splits (if needed)
|
|
||||||
if (attempt_count > 1)
|
|
||||||
{
|
|
||||||
std::string prompt_str = "Your transaction needs to be split into ";
|
|
||||||
prompt_str += std::to_string(attempt_count);
|
|
||||||
prompt_str += " transactions. This will result in a fee of ";
|
|
||||||
prompt_str += print_money(attempt_count * DEFAULT_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.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we made it this far, we're OK to actually send 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
// 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(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers)
|
|
||||||
it2->m_spent = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attempt_count >= MAX_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(tools::wallet2::transfer_container::iterator it2, ptx.selected_transfers)
|
|
||||||
it2->m_spent = false;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//success_msg_writer(true) << "Money successfully sent, transaction " << get_transaction_hash(tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
||||||
{
|
{
|
||||||
|
@ -1015,7 +852,38 @@ bool simple_wallet::transfer(const std::vector<std::string> &args_)
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, DEFAULT_FEE, extra);
|
// figure out what tx will be necessary
|
||||||
|
auto ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, DEFAULT_FEE, extra);
|
||||||
|
|
||||||
|
// if more than one tx necessary, prompt user to confirm
|
||||||
|
if (ptx_vector.size() > 1)
|
||||||
|
{
|
||||||
|
std::string prompt_str = "Your transaction needs to be split into ";
|
||||||
|
prompt_str += std::to_string(ptx_vector.size());
|
||||||
|
prompt_str += " transactions. This will result in a fee of ";
|
||||||
|
prompt_str += print_money(ptx_vector.size() * DEFAULT_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&)
|
catch (const tools::error::daemon_busy&)
|
||||||
{
|
{
|
||||||
|
|
|
@ -57,7 +57,6 @@ namespace cryptonote
|
||||||
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
|
||||||
);
|
);
|
||||||
void 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);
|
|
||||||
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
|
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
|
||||||
bool save(const std::vector<std::string> &args);
|
bool save(const std::vector<std::string> &args);
|
||||||
bool set_log(const std::vector<std::string> &args);
|
bool set_log(const std::vector<std::string> &args);
|
||||||
|
|
|
@ -43,6 +43,9 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
|
||||||
|
|
||||||
namespace tools
|
namespace tools
|
||||||
{
|
{
|
||||||
|
// for now, limit to 30 attempts. TODO: discuss a good number to limit to.
|
||||||
|
const size_t MAX_SPLIT_ATTEMPTS = 30;
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit)
|
void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit)
|
||||||
{
|
{
|
||||||
|
@ -697,6 +700,56 @@ void wallet2::transfer(const std::vector<cryptonote::tx_destination_entry>& dsts
|
||||||
pending_tx ptx;
|
pending_tx ptx;
|
||||||
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx, ptx);
|
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx, ptx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// split_amounts(vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
|
||||||
|
//
|
||||||
|
// split amount for each dst in dsts into num_splits parts
|
||||||
|
// and make num_splits new vector<crypt...> instances to hold these new amounts
|
||||||
|
std::vector<std::vector<cryptonote::tx_destination_entry>> split_amounts(
|
||||||
|
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits)
|
||||||
|
{
|
||||||
|
std::vector<std::vector<cryptonote::tx_destination_entry>> retVal;
|
||||||
|
|
||||||
|
if (num_splits <= 1)
|
||||||
|
{
|
||||||
|
retVal.push_back(dsts);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for each split required
|
||||||
|
for (size_t i=0; i < num_splits; i++)
|
||||||
|
{
|
||||||
|
std::vector<cryptonote::tx_destination_entry> new_dsts;
|
||||||
|
|
||||||
|
// for each destination
|
||||||
|
for (size_t j=0; j < dsts.size(); j++)
|
||||||
|
{
|
||||||
|
cryptonote::tx_destination_entry de;
|
||||||
|
uint64_t amount;
|
||||||
|
|
||||||
|
amount = dsts[j].amount;
|
||||||
|
amount = amount / num_splits;
|
||||||
|
|
||||||
|
// if last split, add remainder
|
||||||
|
if (i + 1 == num_splits)
|
||||||
|
{
|
||||||
|
amount += dsts[j].amount % num_splits;
|
||||||
|
}
|
||||||
|
|
||||||
|
de.addr = dsts[j].addr;
|
||||||
|
de.amount = amount;
|
||||||
|
|
||||||
|
new_dsts.push_back(de);
|
||||||
|
}
|
||||||
|
|
||||||
|
retVal.push_back(new_dsts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
// take a pending tx and actually send it to the daemon
|
// take a pending tx and actually send it to the daemon
|
||||||
void wallet2::commit_tx(pending_tx& ptx)
|
void wallet2::commit_tx(pending_tx& ptx)
|
||||||
|
@ -724,4 +777,99 @@ void wallet2::commit_tx(pending_tx& ptx)
|
||||||
<< "Please, wait for confirmation for your balance to be unlocked.");
|
<< "Please, wait for confirmation for your balance to be unlocked.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wallet2::commit_tx(std::vector<pending_tx>& ptx_vector)
|
||||||
|
{
|
||||||
|
for (auto & ptx : ptx_vector)
|
||||||
|
{
|
||||||
|
commit_tx(ptx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
// separated the call(s) to wallet2::transfer into their own function
|
||||||
|
//
|
||||||
|
// 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, const std::vector<uint8_t> extra)
|
||||||
|
{
|
||||||
|
|
||||||
|
// failsafe split attempt counter
|
||||||
|
size_t attempt_count = 0;
|
||||||
|
|
||||||
|
for(attempt_count = 1; ;attempt_count++)
|
||||||
|
{
|
||||||
|
auto split_values = split_amounts(dsts, attempt_count);
|
||||||
|
|
||||||
|
// Throw if split_amounts comes back with a vector of size different than it should
|
||||||
|
if (split_values.size() != attempt_count)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<pending_tx> ptx_vector;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// for each new destination vector (i.e. for each new tx)
|
||||||
|
for (auto & dst_vector : split_values)
|
||||||
|
{
|
||||||
|
cryptonote::transaction tx;
|
||||||
|
pending_tx ptx;
|
||||||
|
transfer(dst_vector, fake_outs_count, unlock_time, fee, extra, tx, ptx);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,9 @@ 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, 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);
|
||||||
void commit_tx(pending_tx& ptx);
|
void commit_tx(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);
|
||||||
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) const;
|
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments) const;
|
||||||
|
|
|
@ -10,6 +10,7 @@ using namespace epee;
|
||||||
#include "common/command_line.h"
|
#include "common/command_line.h"
|
||||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||||
#include "cryptonote_core/account.h"
|
#include "cryptonote_core/account.h"
|
||||||
|
#include "wallet_rpc_server_commands_defs.h"
|
||||||
#include "misc_language.h"
|
#include "misc_language.h"
|
||||||
#include "string_tools.h"
|
#include "string_tools.h"
|
||||||
#include "crypto/hash.h"
|
#include "crypto/hash.h"
|
||||||
|
@ -85,12 +86,11 @@ namespace tools
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
|
||||||
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx)
|
|
||||||
{
|
|
||||||
|
|
||||||
std::vector<cryptonote::tx_destination_entry> dsts;
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
for (auto it = req.destinations.begin(); it != req.destinations.end(); it++)
|
bool wallet_rpc_server::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)
|
||||||
|
{
|
||||||
|
for (auto it = destinations.begin(); it != destinations.end(); it++)
|
||||||
{
|
{
|
||||||
cryptonote::tx_destination_entry de;
|
cryptonote::tx_destination_entry de;
|
||||||
if(!get_account_address_from_str(de.addr, it->address))
|
if(!get_account_address_from_str(de.addr, it->address))
|
||||||
|
@ -103,11 +103,11 @@ namespace tools
|
||||||
dsts.push_back(de);
|
dsts.push_back(de);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> extra;
|
if (!payment_id.empty())
|
||||||
if (!req.payment_id.empty()) {
|
{
|
||||||
|
|
||||||
/* Just to clarify */
|
/* Just to clarify */
|
||||||
const std::string& payment_id_str = req.payment_id;
|
const std::string& payment_id_str = payment_id;
|
||||||
|
|
||||||
crypto::hash payment_id;
|
crypto::hash payment_id;
|
||||||
/* Parse payment ID */
|
/* Parse payment ID */
|
||||||
|
@ -128,13 +128,85 @@ namespace tools
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<cryptonote::tx_destination_entry> dsts;
|
||||||
|
std::vector<uint8_t> extra;
|
||||||
|
|
||||||
|
// validate the transfer requested and populate dsts & extra
|
||||||
|
if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
cryptonote::transaction tx;
|
std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra);
|
||||||
wallet2::pending_tx ptx;
|
|
||||||
m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx, ptx);
|
// reject proposed transactions if there are more than one. see on_transfer_split below.
|
||||||
res.tx_hash = boost::lexical_cast<std::string>(cryptonote::get_transaction_hash(tx));
|
if (ptx_vector.size() != 1)
|
||||||
|
{
|
||||||
|
er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR;
|
||||||
|
er.message = "Transaction would be too large. try /transfer_split.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wallet.commit_tx(ptx_vector);
|
||||||
|
|
||||||
|
// populate response with tx hash
|
||||||
|
res.tx_hash = boost::lexical_cast<std::string>(cryptonote::get_transaction_hash(ptx_vector.back().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_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, connection_context& cntx)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<cryptonote::tx_destination_entry> dsts;
|
||||||
|
std::vector<uint8_t> extra;
|
||||||
|
|
||||||
|
// validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types.
|
||||||
|
if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<wallet2::pending_tx> ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra);
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
catch (const tools::error::daemon_busy& e)
|
catch (const tools::error::daemon_busy& e)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <boost/program_options/options_description.hpp>
|
#include <boost/program_options/options_description.hpp>
|
||||||
#include <boost/program_options/variables_map.hpp>
|
#include <boost/program_options/variables_map.hpp>
|
||||||
#include "net/http_server_impl_base.h"
|
#include "net/http_server_impl_base.h"
|
||||||
#include "wallet_rpc_server_commans_defs.h"
|
#include "wallet_rpc_server_commands_defs.h"
|
||||||
#include "wallet2.h"
|
#include "wallet2.h"
|
||||||
#include "common/command_line.h"
|
#include "common/command_line.h"
|
||||||
namespace tools
|
namespace tools
|
||||||
|
@ -38,6 +38,7 @@ namespace tools
|
||||||
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
|
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
|
||||||
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("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("incoming_transfers", on_incoming_transfers, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS)
|
MAP_JON_RPC_WE("incoming_transfers", on_incoming_transfers, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS)
|
||||||
|
@ -47,7 +48,9 @@ namespace tools
|
||||||
//json_rpc
|
//json_rpc
|
||||||
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||||
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||||
|
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, connection_context& cntx);
|
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||||
|
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, connection_context& cntx);
|
||||||
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||||
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, connection_context& cntx);
|
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, connection_context& cntx);
|
||||||
bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx);
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace wallet_rpc
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct trnsfer_destination
|
struct transfer_destination
|
||||||
{
|
{
|
||||||
uint64_t amount;
|
uint64_t amount;
|
||||||
std::string address;
|
std::string address;
|
||||||
|
@ -66,7 +66,7 @@ namespace wallet_rpc
|
||||||
{
|
{
|
||||||
struct request
|
struct request
|
||||||
{
|
{
|
||||||
std::list<trnsfer_destination> destinations;
|
std::list<transfer_destination> destinations;
|
||||||
uint64_t fee;
|
uint64_t fee;
|
||||||
uint64_t mixin;
|
uint64_t mixin;
|
||||||
uint64_t unlock_time;
|
uint64_t unlock_time;
|
||||||
|
@ -91,6 +91,35 @@ namespace wallet_rpc
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct COMMAND_RPC_TRANSFER_SPLIT
|
||||||
|
{
|
||||||
|
struct request
|
||||||
|
{
|
||||||
|
std::list<transfer_destination> destinations;
|
||||||
|
uint64_t fee;
|
||||||
|
uint64_t mixin;
|
||||||
|
uint64_t unlock_time;
|
||||||
|
std::string payment_id;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(destinations)
|
||||||
|
KV_SERIALIZE(fee)
|
||||||
|
KV_SERIALIZE(mixin)
|
||||||
|
KV_SERIALIZE(unlock_time)
|
||||||
|
KV_SERIALIZE(payment_id)
|
||||||
|
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 New Issue