ported of send_coins.js / SendFunds() to C++, incl initial block weight support (previous commit already updated fee); set a few 'rct' lookups from get() to get_optional() in serial bridge (likely bugfix for pre rct outs - probably was the cause of some exceptions); returning tx and txBlob_byteLength from convenience__create_transaction for send fns; removed now unnecessary calculate_fee, estimate_rct_tx_size, create_transaction from bridge (encapsulated in send routine).. switched estimated_tx_network_fee over to fee_per_b (API change - the app-side bridge will take fee_per_kb with optl fee_per_b, preferring the latter and converting the former to b by /= 1024
uint64_testimated_tx_network_fee(// convenience function for size + calc
uint64_tfee_per_kb,
uint64_tfee_per_b,
uint32_tpriority,// when priority=0, falls back to monero_fee_utils::default_priority()
use_fork_rules_fn_typeuse_fork_rules_fn// this is extracted to a function so that implementations can optionally query the daemon (although this presently implies that such a call remains blocking)
attempt_at_min_fee=estimate_fee(true/*use_per_byte_fee*/,true/*use_rct*/,2/*est num inputs*/,fake_outs_count,2,extra.size(),bulletproof,base_fee,fee_multiplier,fee_quantization_mask);
// opted to do this instead of `const uint64_t min_fee = (fee_multiplier * base_fee * estimate_tx_size(use_rct, 1, fake_outs_count, 2, extra.size(), bulletproof));`
// if newNeededFee < neededFee, use neededFee instead (should only happen on the 2nd or later times through (due to estimated fee being too low))
if(needed_fee<attempt_at_min_fee){
needed_fee=attempt_at_min_fee;
}
//
// NOTE: needed_fee may get further modified below when !is_sweeping if using_outs_amount < total_incl_fees and gets finalized (for this function's scope) as using_fee
//
retVals.required_balance=is_sweeping?needed_fee:potential_total;// must store for needMoreMoneyThanFound return .... NOTE: this is set to needed_fee for is_sweeping because that's literally the required balance, which an caller may want to print in case they get needMoreMoneyThanFound - note this gets updated below when !is_sweeping
//
uint64_ttotal_wo_fee=is_sweeping
?/*now that we know outsAmount>needed_fee*/(using_outs_amount-needed_fee)
:sending_amount;
retVals.final_total_wo_fee=total_wo_fee;
//
uint64_ttotal_incl_fees;
if(is_sweeping){
if(using_outs_amount<needed_fee){// like checking if the result of the following total_wo_fee is < 0
retVals.errCode=needMoreMoneyThanFound;// sufficiently up-to-date (for this return case) required_balance and using_outs_amount (spendable balance) will have been stored for return by this point
return;
}
total_incl_fees=using_outs_amount;
}else{
total_incl_fees=sending_amount+needed_fee;// because fee changed because using_outs.size() was updated
while(using_outs_amount<total_incl_fees&&remaining_unusedOuts.size()>0){// add outputs 1 at a time till we either have them all or can meet the fee
cout<<"Final attempt at fee: "<<needed_fee<<" for "<<retVals.using_outs.size()<<" inputs"<<endl;
cout<<"Balance to be used: "<<total_incl_fees<<endl;
if(using_outs_amount<total_incl_fees){
retVals.errCode=needMoreMoneyThanFound;// sufficiently up-to-date (for this return case) required_balance and using_outs_amount (spendable balance) will have been stored for return by this point.
return;
}
//
// Change can now be calculated
uint64_tchange_amount=0;// to initialize
if(using_outs_amount>total_incl_fees){
THROW_WALLET_EXCEPTION_IF(is_sweeping,error::wallet_internal_error,"Unexpected total_incl_fees > using_outs_amount while sweeping");
THROW_WALLET_EXCEPTION_IF(create_tx__retVals.signed_serialized_tx_string==boost::none,error::wallet_internal_error,"Not expecting no signed_serialized_tx_string given no error");
// Send_Step* functions procedure for integrators:
// 1. call GetUnspentOuts endpoint
// 2. call step1__prepare_params_for_get_decoys to get params for calling RandomOuts; call GetRandomOuts
// 3. call step2__reenterable_… with retVals from Step1 (incl using_outs, RandomOuts)
// 3a. While tx must be reconstructed, re-call step1 passing step2 fee_actually_needed as passedIn_attemptAt_fee, then re-request RandomOuts again, and call step2 again
// 3b. If good tx constructed, proceed to submit/save the tx
// Note: This separation of steps fully encodes SendFunds_ProcessStep
//
structSend_Step1_RetVals
{
CreateTransactionErrorCodeerrCode;
CreateTransactionErrorCodeerrCode;// if != noError, abort Send process
// for display / information purposes on errCode=needMoreMoneyThanFound during step1:
uint64_tspendable_balance;// (effectively but not the same as spendable_balance)
uint64_trequired_balance;// for display / information purposes on errCode=needMoreMoneyThanFound during step1
//
optional<transaction>tx;
optional<secret_key>tx_key;
optional<vector<secret_key>>additional_tx_keys;
// Success case return values
uint32_tmixin;
vector<SpendableOutput>using_outs;
uint64_tusing_fee;
uint64_tfinal_total_wo_fee;
uint64_tchange_amount;
};
// TODO: add priority
voidcreate_transaction(
TransactionConstruction_RetVals&retVals,
constaccount_keys&sender_account_keys,// this will reference a particular hw::device
constuint32_tsubaddr_account_idx,// pass 0 for no subaddrs
optional<uint64_t>passedIn_attemptAt_fee// use this for passing step2 "must-reconstruct" return values back in, i.e. re-entry; when nil, defaults to attempt at network min
);
//
structSend_Step2_RetVals
{
CreateTransactionErrorCodeerrCode;// if != noError, abort Send process
//
// Reconstruct-required parameters:
booltx_must_be_reconstructed;// if true, re-request RandomOuts with the following parameters and retry step3
uint64_tfee_actually_needed;// will be non-zero if tx_must_be_reconstructed
//
// Success parameters:
optional<string>signed_serialized_tx_string;
optional<string>tx_hash_string;
optional<string>tx_key_string;// this includes additional_tx_keys
stoull(json_root.get<string>("fee_per_b")),// per v8
//
optl__passedIn_attemptAt_fee// use this for passing step2 "must-reconstruct" return values back in, i.e. re-entry; when nil, defaults to attempt at network min
{// PROBABLY don't need to shuttle these back (could send only public_key) but consumers might like the feature of being able to send this JSON structure directly back to step2 without reconstructing it for themselves
amountOutput.global_index=stoull(mix_out_output_desc.second.get<string>("global_index"));// this is, I believe, presently supplied as a string by the API, probably to avoid overflow
THROW_WALLET_EXCEPTION_IF(retVals.signed_serialized_tx_string==boost::none,error::wallet_internal_error,"Not expecting no signed_serialized_tx_string given no error");
staticinlinestringret_json_key__send__using_outs(){return"using_outs";}// this list's members' keys should probably be declared (is this the best way to do this?)