Skip to content

Commit c81d57f

Browse files
Merge #991: [RPC][Wallet] Create multiple transactions with one command.
a872b41 Create multiple transactions with one command. (Zannick) Pull request description: ## Details RPC send commands have a new argument `inputs_per_tx` that controls whether the amount specified can be split across multiple transactions. This value ranges from 0 (default) to 32 (an upper bound designed to prevent transactions larger than can be accepted). When set to 0, attempts to fit the send in one transaction as per previous behavior. The sends are interpreted to mean that the recipient will receive the sent amount, but under the hood the transactions are created as if `subtractfeefromamount` was specified. In particular, if the send needs to be split into `n > 1` transactions, the outputs of the first `n - 1` are randomly reduced to account for the fee (and the shortfall from the inputs). Coins are selected using a sliding window that prioritizes cleaning up small utxos (dust) over using fewer inputs, and thus transactions built this way will tend toward having more inputs. This is beneficial for ringct staking (keeping larger coins intact and reducing the amount of small utxos). The smallest utxo is always included if possible to prevent the amount of small utxos from increasing with usage. ## Tested On regtest some months ago. Tree-SHA512: 8b6b0b7fccdb16507da321c371b8075fdecae5f53ca2fb95a49a8d8edb6104c3c7d26270c0f029a99bf0aff4fe6a15c9e0b0fdfad372ab8166b619ce2ac38f98
2 parents 7c8bee1 + a872b41 commit c81d57f

File tree

7 files changed

+427
-139
lines changed

7 files changed

+427
-139
lines changed

src/interfaces/wallet.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -502,12 +502,12 @@ class WalletImpl : public Wallet
502502
break;
503503
}
504504
case OUTPUT_CT:
505-
if (0 != pwalletAnon->AddBlindedInputs(wtx, rtx, recipients, !fCheckFeeOnly, nFeeRet, &coin_control, fail_reason))
505+
if (0 != pwalletAnon->AddBlindedInputs(wtx, rtx, recipients, !fCheckFeeOnly, 0, nFeeRet, &coin_control, fail_reason))
506506
fFailed = true;
507507
break;
508508
case OUTPUT_RINGCT:
509509
if (!pwalletAnon->AddAnonInputs(wtx, rtx, recipients, !fCheckFeeOnly, nRingSize,
510-
nInputsPerSig, nFeeRet, &coin_control, fail_reason))
510+
nInputsPerSig, 0, nFeeRet, &coin_control, fail_reason))
511511
fFailed = true;
512512
break;
513513
default:

src/veil/ringct/anonwallet.cpp

+241-36
Large diffs are not rendered by default.

src/veil/ringct/anonwallet.h

+14-6
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,9 @@ class AnonWallet
227227
CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError, bool fZerocoinInputs, CAmount nInputValue);
228228

229229
int AddBlindedInputs(CWalletTx &wtx, CTransactionRecord &rtx, std::vector<CTempRecipient> &vecSend, bool sign,
230-
CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);
230+
size_t nMaximumInputs, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);
231231
int AddBlindedInputs_Inner(CWalletTx &wtx, CTransactionRecord &rtx, std::vector<CTempRecipient> &vecSend, bool sign,
232-
CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);
232+
size_t nMaximumInputs, CAmount &nFeeRet, const CCoinControl *coinControl, std::string &sError);
233233

234234

235235
bool PlaceRealOutputs(std::vector<std::vector<int64_t> > &vMI, size_t &nSecretColumn, size_t nRingSize, std::set<int64_t> &setHave,
@@ -240,10 +240,10 @@ class AnonWallet
240240

241241
bool IsMyAnonInput(const CTxIn& txin, COutPoint& myOutpoint);
242242
bool AddAnonInputs_Inner(CWalletTx &wtx, CTransactionRecord &rtx, std::vector<CTempRecipient> &vecSend,
243-
bool sign, size_t nRingSize, size_t nInputsPerSig, CAmount &nFeeRet,
243+
bool sign, size_t nRingSize, size_t nInputsPerSig, size_t nMaximumInputs, CAmount &nFeeRet,
244244
const CCoinControl *coinControl, std::string &sError, bool fZerocoinInputs, CAmount nInputValue);
245245
bool AddAnonInputs(CWalletTx &wtx, CTransactionRecord &rtx, std::vector<CTempRecipient> &vecSend,
246-
bool sign, size_t nRingSize, size_t nInputsPerSig, CAmount &nFeeRet,
246+
bool sign, size_t nRingSize, size_t nInputsPerSig, size_t nMaximumInputs, CAmount &nFeeRet,
247247
const CCoinControl *coinControl, std::string &sError, bool fZerocoinInputs = false,
248248
CAmount nInputValue = 0);
249249

@@ -322,15 +322,23 @@ class AnonWallet
322322
* populate vCoins with vector of available COutputs.
323323
*/
324324
void AvailableBlindedCoins(std::vector<COutputR>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 0x7FFFFFFF, bool fIncludeImmature=false) const;
325-
bool SelectBlindedCoins(const std::vector<COutputR>& vAvailableCoins, const CAmount& nTargetValue, std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &setCoinsRet, CAmount &nValueRet, const CCoinControl *coinControl = nullptr) const;
325+
/**
326+
* Returns a list of coins for a single transaction based on the target value and allowed number of inputs.
327+
*/
328+
bool SelectBlindedCoins(const std::vector<COutputR>& vAvailableCoins, const CAmount& nTargetValue, size_t nMaximumCount, std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &setCoinsRet, CAmount &nValueRet, const CCoinControl *coinControl = nullptr) const;
326329

327330
void AvailableAnonCoins(std::vector<COutputR> &vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t& nMaximumCount = 0, const int& nMinDepth = 0, const int& nMaxDepth = 0x7FFFFFFF, bool fIncludeImmature=false) const;
328331

329332
/**
330333
* Return list of available coins and locked coins grouped by non-change output address.
331334
*/
332335

333-
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutputR> vCoins, std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &setCoinsRet, CAmount &nValueRet) const;
336+
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutputR> &vCoins, std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &setCoinsRet, CAmount &nValueRet) const;
337+
/**
338+
* Like SelectCoinsMinConf, but always selects at most nMaximumCount coins,
339+
* with the intention of being put in a single tx that partially accomplishes the target to be sent.
340+
*/
341+
bool SelectCoinsForOneTx(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutputR> &vCoins, size_t nMaximumCount, std::vector<std::pair<MapRecords_t::const_iterator,unsigned int> > &setCoinsRet, CAmount &nValueRet) const;
334342

335343
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
336344

0 commit comments

Comments
 (0)