So I figured I would post about where I am at in the source code and give some perspective on what is going on and how complex it is (well to me it is):
The
transfer RPC command used in simplewallet.cpp references the following function:
transfer_main function specified in simplewallet.cpp
**you can see my commenting for my own understanding of what's going on**bool simple_wallet::transfer_main(bool new_algorithm, const std::vector &args_)
{
if (!try_connect_to_daemon())
return true;
std::vector local_args = args_;
size_t fake_outs_count;
if(local_args.size() > 0) {
if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0]))
{
fake_outs_count = DEFAULT_MIX;
}
else
{
local_args.erase(local_args.begin());
}
}
if(local_args.size() < 2) //parameter error checking
{
fail_msg_writer() << tr("wrong number of arguments");
return true;
}
if(m_wallet->watch_only()) //view only wallet?
{
fail_msg_writer() << tr("This is a watch only wallet");
return true;
}
std::vector extra; //used to store extra_nonce (random value?? or payment id?)
bool payment_id_seen = false;
if (1 == local_args.size() % 2) //if am using payment id argument (then amount of arguments is always odd)
{
std::string payment_id_str = local_args.back(); //get last argument payment_id as a STRING type
local_args.pop_back(); //delete it from local_args
crypto::hash payment_id; //of type POD_CLASS and has payment_id.DATA[32] member array
bool r = tools::wallet2::parse_long_payment_id(payment_id_str, payment_id);
if(r) //determine if payment_id is LONG (32 or 64 hex)
{
std::string extra_nonce; //of "blobdata" or string type
set_payment_id_to_tx_extra_nonce(extra_nonce, payment_id); //payment id here can be any 64 char hex
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
}
else //or SHORT (8 or 16 hex)
{
crypto::hash8 payment_id8;
r = tools::wallet2::parse_short_payment_id(payment_id_str, payment_id8);
if(r)
{
std::string extra_nonce;
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, payment_id8); // "ENCRYPTED PAYMENT ID"???? <---- can be any 16char hex
r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
}
}
if(!r) // if payment_id is not 16 hex or 16 hex characters then it is an invalid payment id
{
fail_msg_writer() << tr("payment id has invalid format, expected 16 or 64 character string: ") << payment_id_str;
return true;
}
payment_id_seen = true;
}
vector dsts; //tx_destinations (dynamic sized array)
for (size_t i = 0; i < local_args.size(); i += 2) //pairs of address and amounts (do all)
{
cryptonote::tx_destination_entry de; //structure to store 1) amount and 2) address
bool has_payment_id;
crypto::hash8 new_payment_id; //to hold 16 hex chars
if(!get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), local_args[i])) //if no payment id
{
// if treating as an address fails, try as url
bool dnssec_ok = false;
std::string url = local_args[i];
// attempt to get address from dns query
auto addresses_from_dns = tools::wallet2::addresses_from_url(url, dnssec_ok);
// for now, move on only if one address found
if (addresses_from_dns.size() == 1)
{
if (get_account_integrated_address_from_str(de.addr, has_payment_id, new_payment_id, m_wallet->testnet(), addresses_from_dns[0]))
{
// if it was an address, prompt user for confirmation.
// inform user of DNSSEC validation status as well.
std::string dnssec_str;
if (dnssec_ok)
{
dnssec_str = tr("DNSSEC validation passed");
}
else
{
dnssec_str = tr("WARNING: DNSSEC validation was unsuccessful, this address may not be correct!");
}
std::stringstream prompt;
prompt << tr("For URL: ") << url
<< ", " << dnssec_str << std::endl
<< tr(" Monero Address = ") << addresses_from_dns[0]
<< std::endl
<< tr("Is this OK? (Y/n) ")
;
// prompt the user for confirmation given the dns query and dnssec status
std::string confirm_dns_ok = command_line::input_line(prompt.str());
if (confirm_dns_ok != "Y" && confirm_dns_ok != "y" && confirm_dns_ok != "Yes" && confirm_dns_ok != "yes"
&& confirm_dns_ok != tr("yes") && confirm_dns_ok != tr("no"))
{
fail_msg_writer() << tr("You have cancelled the transfer request");
return true;
}
}
else
{
fail_msg_writer() << tr("Failed to get a Monero address from: ") << local_args[i];
return true;
}
}
else if (addresses_from_dns.size() > 1)
{
fail_msg_writer() << tr("Not yet supported: Multiple Monero addresses found for given URL: ") << url;
}
else
{
fail_msg_writer() << tr("Wrong address: ") << local_args[i];
return true;
}
}
if (has_payment_id) //if there is payment_id
{
if (payment_id_seen)
{
fail_msg_writer() << tr("A single transaction cannot use more than one payment id: ") << local_args[i];
return true;
}
std::string extra_nonce;
set_encrypted_payment_id_to_tx_extra_nonce(extra_nonce, new_payment_id);
bool r = add_extra_nonce_to_tx_extra(extra, extra_nonce);
if(!r)
{
fail_msg_writer() << tr("Failed to set up payment id, though it was decoded correctly");
return true;
}
}
bool ok = cryptonote::parse_amount(de.amount, local_args[i + 1]);
if(!ok || 0 == de.amount)
{
fail_msg_writer() << tr("amount is wrong: ") << local_args[i] << ' ' << local_args[i + 1] <<
", " << tr("expected number from 0 to ") << print_money(std::numeric_limits::max());
return true;
}
dsts.push_back(de); //add transaction to the queue/
}
try
{
// figure out what tx will be necessary
std::vector ptx_vector;
if (new_algorithm)
ptx_vector = m_wallet->create_transactions_2(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra);
else
ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra);
// if more than one tx necessary, prompt user to confirm
if (m_wallet->always_confirm_transfers() || ptx_vector.size() > 1)
{
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 = (boost::format(tr("Your transaction needs to be split into %llu transactions. "
"This will result in a transaction fee being applied to each transaction, for a total fee of %s. Is this okay? (Y/Yes/N/No)")) %
((unsigned long long)ptx_vector.size()) % print_money(total_fee)).str();
std::string accepted = command_line::input_line(prompt_str);
if (accepted != "Y" && accepted != "y" && accepted != "Yes" && accepted != "yes")
{
fail_msg_writer() << tr("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) << tr("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() << tr("daemon is busy. Please try later");
}
catch (const tools::error::no_connection_to_daemon&)
{
fail_msg_writer() << tr("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() << tr("RPC error: ") << e.what();
}
catch (const tools::error::get_random_outs_error&)
{
fail_msg_writer() << tr("failed to get random outputs to mix");
}
catch (const tools::error::not_enough_money& e)
{
fail_msg_writer() << boost::format(tr("not enough money to transfer, available only %s, transaction amount %s = %s + %s (fee)")) %
print_money(e.available()) %
print_money(e.tx_amount() + e.fee()) %
print_money(e.tx_amount()) %
print_money(e.fee());
}
catch (const tools::error::not_enough_outs_to_mix& e)
{
auto writer = fail_msg_writer();
writer << tr("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 << "\n" << tr("output amount") << " = " << print_money(outs_for_amount.amount) << ", " << tr("found outputs to mix") << " = " << outs_for_amount.outs.size();
}
}
catch (const tools::error::tx_not_constructed&)
{
fail_msg_writer() << tr("transaction was not constructed");
}
catch (const tools::error::tx_rejected& e)
{
fail_msg_writer() << (boost::format(tr("transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
}
catch (const tools::error::tx_sum_overflow& e)
{
fail_msg_writer() << e.what();
}
catch (const tools::error::zero_destination&)
{
fail_msg_writer() << tr("one of destinations is zero");
}
catch (const tools::error::tx_too_big& e)
{
fail_msg_writer() << tr("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() << tr("unknown transfer error: ") << e.what();
}
catch (const tools::error::wallet_internal_error& e)
{
LOG_ERROR("internal error: " << e.to_string());
fail_msg_writer() << tr("internal error: ") << e.what();
}
catch (const std::exception& e)
{
LOG_ERROR("unexpected error: " << e.what());
fail_msg_writer() << tr("unexpected error: ") << e.what();
}
catch (...)
{
LOG_ERROR("Unknown error");
fail_msg_writer() << tr("unknown error");
}
return true;
}
I'm on the line that calls
create_transactionsptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra);
Which calls the following code in wallet2.cpp:
std::vector
wallet2::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee_UNUSED, const std::vector extra)
{
// failsafe split attempt counter
size_t attempt_count = 0;
for(attempt_count = 1; ;attempt_count++) //this runs and runs until it has exhausted all possibilities with the blockchain
{ //this is where when I ran mixin of 200 on testnet it would take a while
size_t num_tx = 0.5 + pow(1.7,attempt_count-1); //
auto split_values = split_amounts(dsts, num_tx);
// Throw if split_amounts comes back with a vector of size different than it should
if (split_values.size() != num_tx)
{
throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested");
}
std::vector ptx_vector; //pending transaction
try
{
// for each new destination vector (i.e. for each new tx)
for (auto & dst_vector : split_values)
{
cryptonote::transaction tx; //tx
pending_tx ptx; //pending tx
// loop until fee is met without increasing tx size to next KB boundary.
uint64_t needed_fee = 0;
do
{
transfer(dst_vector, fake_outs_count, unlock_time, needed_fee, extra, tx, ptx); //does a dust conversion as opposed to original transfer function that gets called anyways
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;
} while (ptx.fee < needed_fee);
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;
}
}
}Within the create_transactions function I am on the line:
transfer(dst_vector, fake_outs_count, unlock_time, needed_fee, extra, tx, ptx);
Where another function called "transfer"(A) is called with 7 parameters is called repeatedly until the fee amount is met to satisfy the FEE_PER_KB * txsize amount "needed_fee".
This transfer(A) function then calls another function named "transfer"(B) is called with 9 parameters specifying the dust threshold which is 0.01XMR called in wallet2.h
transfer(dsts, fake_outputs_count, unlock_time, fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), tx, ptx);
The source of transfer(B) function is:
template
void wallet2::transfer(const std::vector& dsts, size_t fake_outputs_count,
uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
THROW_WALLET_EXCEPTION_IF(dsts.empty(), error::zero_destination);
uint64_t needed_money = fee;
// calculate total amount being sent to all destinations
// throw if total amount overflows uint64_t
BOOST_FOREACH(auto& dt, dsts) //copies all the contents from dsts to dt by reference temporarily for each interation of loop.
//BOOST_FOREACH(auto& , )
{
THROW_WALLET_EXCEPTION_IF(0 == dt.amount, error::zero_destination);
needed_money += dt.amount; //sum up each of the amounts of each address/amount pair
THROW_WALLET_EXCEPTION_IF(needed_money < dt.amount, error::tx_sum_overflow, dsts, fee, m_testnet);
}
// randomly select inputs for transaction
// throw if requested send amount is greater than amount available to send
std::list selected_transfers;
uint64_t found_money = select_transfers(needed_money, 0 == fake_outputs_count, dust_policy.dust_threshold, selected_transfers); //get found money based on existing inputs (reg & dust) - get input indices also
THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee);
typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry;
typedef cryptonote::tx_source_entry::output_entry tx_output_entry;
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
if(fake_outputs_count)
{
connect_to_daemon();
THROW_WALLET_EXCEPTION_IF(!check_connection(), error::no_connection_to_daemon, "get_random_outs");
uint64_t outs_count = fake_outputs_count + 1;
std::vector amounts;
BOOST_FOREACH(transfer_container::iterator it, selected_transfers)
{
THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error,
"m_internal_output_index = " + std::to_string(it->m_internal_output_index) +
" is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size()));
amounts.push_back(it->amount());
}
zframe_t *amounts_frame = zframe_new(&amounts[0], amounts.size() * sizeof(uint64_t));
int rc = wap_client_random_outs(ipc_client, outs_count, &amounts_frame);
uint64_t status = wap_client_status(ipc_client);
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getrandomouts");
// TODO: Use a code to string mapping of errors
THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_RANDOM_OUTS_FAILED, error::get_random_outs_error, "IPC::STATUS_RANDOM_OUTS_FAILED");
THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_random_outs_error, "!IPC:STATUS_OK");
// Convert ZMQ response back into RPC response object.
zframe_t *outputs_frame = wap_client_random_outputs(ipc_client);
uint64_t frame_size = zframe_size(outputs_frame);
char *frame_data = reinterpret_cast(zframe_data(outputs_frame));
rapidjson::Document json;
THROW_WALLET_EXCEPTION_IF(json.Parse(frame_data, frame_size).HasParseError(), error::get_random_outs_error, "Couldn't JSON parse random outputs.");
for (rapidjson::SizeType i = 0; i < json["outputs"].Size(); i++) {
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount output;
output.amount = json["outputs"][i]["amount"].GetInt64();
for (rapidjson::SizeType j = 0; j < json["outputs"][i]["outs"].Size(); j++) {
COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry entry;
entry.global_amount_index = json["outputs"][i]["outs"][j]["global_amount_index"].GetInt64();
std::string out_key(json["outputs"][i]["outs"][j]["out_key"].GetString(), json["outputs"][i]["outs"][j]["out_key"].GetStringLength());
memcpy(entry.out_key.data, out_key.c_str(), 32);
output.outs.push_back(entry);
}
daemon_resp.outs.push_back(output);
}
std::vector scanty_outs;
BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs)
{
if (amount_outs.outs.size() < fake_outputs_count)
{
scanty_outs.push_back(amount_outs);
}
}
THROW_WALLET_EXCEPTION_IF(!scanty_outs.empty(), error::not_enough_outs_to_mix, scanty_outs, fake_outputs_count);
}
//prepare inputs
size_t i = 0;
std::vector 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 mixin transaction
if(daemon_resp.outs.size())
{
daemon_resp.outs[i].outs.sort([](const out_entry& a, const out_entry& b){return a.global_amount_index < b.global_amount_index;});
BOOST_FOREACH(out_entry& daemon_oe, daemon_resp.outs[i].outs)
{
if(td.m_global_output_index == daemon_oe.global_amount_index)
continue;
tx_output_entry oe;
oe.first = daemon_oe.global_amount_index;
oe.second = daemon_oe.out_key;
src.outputs.push_back(oe);
if(src.outputs.size() >= fake_outputs_count)
break;
}
}
//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;
});
//size_t real_index = src.outputs.size() ? (rand() % src.outputs.size() ):0;
tx_output_entry real_oe;
real_oe.first = td.m_global_output_index;
real_oe.second = boost::get(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);
if (needed_money < found_money)
{
change_dts.addr = m_account.get_keys().m_account_address;
change_dts.amount = found_money - needed_money;
}
uint64_t dust = 0;
std::vector 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));
if (0 != dust && !dust_policy.add_to_fee)
{
splitted_dsts.push_back(cryptonote::tx_destination_entry(dust, dust_policy.addr_for_dust));
}
crypto::secret_key tx_key;
bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), sources, splitted_dsts, extra, tx, unlock_time, tx_key);
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 = fee;
ptx.dust = dust;
ptx.tx = tx;
ptx.change_dts = change_dts;
ptx.selected_transfers = selected_transfers;
ptx.tx_key = tx_key;
}
Within
transfer(B) the following line is called with function
select_transfers:
select_transfers(needed_money, 0 == fake_outputs_count, dust_policy.dust_threshold, selected_transfers);
This function is specified in wallet2.cpp.
What it does is:
//----------------------------------------------------------------------------------------------------
// Select random input sources for transaction.
// returns:
// direct return: amount of money found
// modified reference: selected_transfers, a list of iterators/indices of input sources
As you can see at this point there is a lot of drilling down into multiple function calls calling other functions.
select_transfers uint64_t wallet2::select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list& selected_transfers)
{
std::vector unused_transfers_indices;
std::vector unused_dust_indices;
// aggregate sources available for transfers (by storing the indices that are unspent) in OUR wallet
// 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) //m_transfers delared in wallet2.h and of type "transfer_container" <---std::vector type
{
const transfer_details& td = m_transfers[i]; //"transfer_details" struct
if (!td.m_spent && is_transfer_unlocked(td)) //has following members
{ //uint64_t m_block_height
if (dust < td.amount()) //cryptonote::transaction m_tx
unused_transfers_indices.push_back(i); //size_t m_internal_output_index
else //uint64_t m_global_output_index
unused_dust_indices.push_back(i); // bool m_spent;
} //crypto::key_image m_key_image; //TODO: key_image stored twice :(
} //uint64_t amount() const { return m_tx.vout[m_internal_output_index].amount; }
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())) //keep iterating through inputs until enough money has been found (or we run out of inputs to look through)
{
size_t idx;
if (select_one_dust)
{
idx = pop_random_value(unused_dust_indices); //choose one dust input amount by index
select_one_dust = false;
}
else
{
idx = !unused_transfers_indices.empty() ? pop_random_value(unused_transfers_indices) : pop_random_value(unused_dust_indices); //get index from non dust inputs. If no more non dust then select index from dust inputs to use.
}
transfer_container::iterator it = m_transfers.begin() + idx;
selected_transfers.push_back(it);
found_money += it->amount(); //keep a running SUM of all the found money from inputs
}
return found_money;
}
It finds random inputs in the wallet you are running simplewallet with and makes sure you have enough input amounts + dust to at least equal fee + amount to transfer.
Now at this point I'm going to continue to read the first function at the top and keep going from there, where I left off after the function call
create_transactionsI'm finding it sort of therapeutic to read source code learn new keywords and operators etc in C++ that I never learned in my schooling in computer science.
There is a lot going on in just this one RPC command "transfer". There is a "transfer_new" which I have not delved into much yet.
I'll post more in the future once I have better understanding of the code or just want to update on where my reading and commenting for myself is at.
Trust me there is much more going on in other places of the code on a bit-wise level of shifting bits LEFT and RIGHT etc in the portion that creates your secret/view keys etc when you create an ACCOUNT in simple wallet. <---- very complex math
All for now..