Skip to content

Commit

Permalink
Fee estimation optimized (#1004)
Browse files Browse the repository at this point in the history
* Simple calculation used for coincontrol fee estimation

* Fee estimation function optimized

* Failing tests fixed

* Updated fee estimation based on Lelantus improvements
  • Loading branch information
levonpetrosyan93 committed Apr 15, 2021
1 parent f3fdcc6 commit 54cc566
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 40 deletions.
3 changes: 2 additions & 1 deletion src/elysium/wallettxs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ int64_t SelectCoins(const std::string& fromAddress, CCoinControl& coinControl, i
std::vector<CSigmaEntry> coinsToSpend;
std::vector<sigma::CoinDenomination> remints;
try {
pwalletMain->GetCoinsToSpend(nMax, coinsToSpend, remints);
std::list<CSigmaEntry> sigmaCoins = pwalletMain->GetAvailableCoins();
pwalletMain->GetCoinsToSpend(nMax, coinsToSpend, remints, sigmaCoins);
} catch (std::exception const &err) {
LogPrintf("SelectCoins() fail to get coin to spend: %s\n", err.what());
return nTotal;
Expand Down
7 changes: 5 additions & 2 deletions src/qt/coincontroldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog, bool a
// calculation
if (nQuantity > 0)
{
if(anonymousMode){
std::tie(nPayFee, nBytes) = model->getWallet()->EstimateJoinSplitFee(nPayAmount,CoinControlDialog::fSubtractFeeFromAmount, coinControl);
if (anonymousMode) {
// 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data
// 83 assuming 1 jmint, 34 is the size of each normal vout, 10 is the size of empty transaction, 52 other constant parts
nBytes = 1054 + 2560 * vOutputs.size() + 83 + CoinControlDialog::payAmounts.size() * 34 + 10 + 52;
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
if (nPayAmount > 0) {
nChange = nAmount - nPayAmount;
if (!CoinControlDialog::fSubtractFeeFromAmount)
Expand Down
12 changes: 7 additions & 5 deletions src/wallet/lelantusjoinsplitbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ CWalletTx LelantusJoinSplitBuilder::Build(
}

std::tie(fee, std::ignore) = wallet.EstimateJoinSplitFee(vOut + mint, recipientsToSubtractFee, coinControl);

std::list<CSigmaEntry> sigmaCoins = pwalletMain->GetAvailableCoins(coinControl);
std::list<CLelantusEntry> coins = pwalletMain->GetAvailableLelantusCoins(coinControl);

for (;;) {
// In case of not enough fee, reset mint seed counter
Expand Down Expand Up @@ -187,9 +188,8 @@ CWalletTx LelantusJoinSplitBuilder::Build(

std::vector<sigma::CoinDenomination> denomChanges;
try {
std::list<CSigmaEntry> coins = pwalletMain->GetAvailableCoins(coinControl);
CAmount availableBalance(0);
for (auto coin : coins) {
for (auto coin : sigmaCoins) {
availableBalance += coin.get_denomination_value();
}
if(availableBalance > 0) {
Expand All @@ -199,9 +199,11 @@ CWalletTx LelantusJoinSplitBuilder::Build(
else
inputFromSigma = required;

wallet.GetCoinsToSpend(inputFromSigma, sigmaSpendCoins, denomChanges, //try to spend sigma first
std::list<CSigmaEntry> sigmaCoinsCp = sigmaCoins;
wallet.GetCoinsToSpend(inputFromSigma, sigmaSpendCoins, denomChanges, sigmaCoinsCp, //try to spend sigma first
consensusParams.nMaxLelantusInputPerTransaction,
consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl);

required -= inputFromSigma;

isSigmaToLelantusJoinSplit = true;
Expand All @@ -210,7 +212,7 @@ CWalletTx LelantusJoinSplitBuilder::Build(
}

if(required > 0) {
if (!wallet.GetCoinsToJoinSplit(required, spendCoins, changeToMint,
if (!wallet.GetCoinsToJoinSplit(required, spendCoins, changeToMint, coins,
consensusParams.nMaxLelantusInputPerTransaction,
consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl)) {
throw InsufficientFunds();
Expand Down
3 changes: 2 additions & 1 deletion src/wallet/sigmaspendbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,9 @@ CAmount SigmaSpendBuilder::GetInputs(std::vector<std::unique_ptr<InputSigner>>&
denomChanges.clear();

auto& consensusParams = Params().GetConsensus();
std::list<CSigmaEntry> sigmaCoins = pwalletMain->GetAvailableCoins(coinControl);

if (!wallet.GetCoinsToSpend(required, selected, denomChanges,
if (!wallet.GetCoinsToSpend(required, selected, denomChanges, sigmaCoins,
consensusParams.nMaxSigmaInputPerTransaction, consensusParams.nMaxValueSigmaSpendPerTransaction, coinControl)) {
throw InsufficientFunds();
}
Expand Down
36 changes: 27 additions & 9 deletions src/wallet/test/sigma_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ BOOST_AUTO_TEST_CASE(get_coin_no_coin)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint), InsufficientFunds);
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint, availableCoins), InsufficientFunds);

std::vector<std::pair<sigma::CoinDenomination, int>> needCoins;

Expand All @@ -270,7 +272,9 @@ BOOST_AUTO_TEST_CASE(get_coin_different_denomination)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_NO_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint));
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_NO_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint, availableCoins));
sigmaState->Reset();
}

Expand All @@ -287,7 +291,9 @@ BOOST_AUTO_TEST_CASE(get_coin_round_up)

std::vector<CSigmaEntry> coinsToSpend;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coinsToSpend, coinsToMint),
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coinsToSpend, coinsToMint, availableCoins),
"Expect enough for requirement");

// We would expect to spend 100 + 10 + 1 + 0.5 + 0.1 + 0.05
Expand All @@ -312,7 +318,9 @@ BOOST_AUTO_TEST_CASE(get_coin_not_enough)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint), InsufficientFunds);
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint, availableCoins), InsufficientFunds);
sigmaState->Reset();
}

Expand All @@ -329,7 +337,9 @@ BOOST_AUTO_TEST_CASE(get_coin_cannot_spend_unconfirmed_coins)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint), InsufficientFunds);
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_THROW(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint, availableCoins), InsufficientFunds);
sigmaState->Reset();
}

Expand All @@ -345,7 +355,9 @@ BOOST_AUTO_TEST_CASE(get_coin_minimize_coins_spend_fit_amount)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coins,coinsToMint),
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coins,coinsToMint, availableCoins),
"Expect enough coin and equal to one SIGMA_DENOM_100");

std::vector<std::pair<sigma::CoinDenomination, int>> expectedCoins;
Expand All @@ -368,7 +380,9 @@ BOOST_AUTO_TEST_CASE(get_coin_minimize_coins_spend)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint),
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint, availableCoins),
"Coins to spend value is not equal to required amount.");

std::vector<std::pair<sigma::CoinDenomination, int>> expectedCoins;
Expand All @@ -391,7 +405,9 @@ BOOST_AUTO_TEST_CASE(get_coin_choose_smallest_enough)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coins,coinsToMint),
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_MESSAGE(pwalletMain->GetCoinsToSpend(require, coins,coinsToMint, availableCoins),
"Expect enough coin and equal one SIGMA_DENOM_1");

std::vector<std::pair<sigma::CoinDenomination, int>> expectedCoins;
Expand All @@ -414,7 +430,9 @@ BOOST_AUTO_TEST_CASE(get_coin_by_limit_max_to_1)

std::vector<CSigmaEntry> coins;
std::vector<sigma::CoinDenomination> coinsToMint;
BOOST_CHECK_EXCEPTION(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint, 1),
std::list<CSigmaEntry> availableCoins = pwalletMain->GetAvailableCoins();

BOOST_CHECK_EXCEPTION(pwalletMain->GetCoinsToSpend(require, coins, coinsToMint, availableCoins, 1),
std::runtime_error,
[](const std::runtime_error& e) {
return e.what() == std::string("Can not choose coins within limit.");
Expand Down
32 changes: 14 additions & 18 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3029,10 +3029,10 @@ bool CWallet::GetCoinsToSpend(
CAmount required,
std::vector<CSigmaEntry>& coinsToSpend_out,
std::vector<sigma::CoinDenomination>& coinsToMint_out,
std::list<CSigmaEntry>& coins,
const size_t coinsToSpendLimit,
const CAmount amountToSpendLimit,
const CCoinControl *coinControl,
bool forEstimation) const
const CCoinControl *coinControl) const
{
// Sanity check to make sure this function is never called with a too large
// amount to spend, resulting to a possible crash due to out of memory condition.
Expand Down Expand Up @@ -3061,8 +3061,6 @@ bool CWallet::GetCoinsToSpend(
_("Required amount exceed value spend limit"));
}

std::list<CSigmaEntry> coins = GetAvailableCoins(coinControl, false, forEstimation);

CAmount availableBalance = CalculateCoinsBalance(coins.begin(), coins.end());

if (roundedRequired * zeros > availableBalance) {
Expand Down Expand Up @@ -3161,10 +3159,10 @@ bool CWallet::GetCoinsToJoinSplit(
CAmount required,
std::vector<CLelantusEntry>& coinsToSpend_out,
CAmount& changeToMint,
std::list<CLelantusEntry> coins,
const size_t coinsToSpendLimit,
const CAmount amountToSpendLimit,
const CCoinControl *coinControl,
bool forEstimation) const
const CCoinControl *coinControl) const
{
// Sanity check to make sure this function is never called with a too large
// amount to spend, resulting to a possible crash due to out of memory condition.
Expand All @@ -3183,8 +3181,6 @@ bool CWallet::GetCoinsToJoinSplit(
_("The required amount exceeds spend limit"));
}

std::list<CLelantusEntry> coins = GetAvailableLelantusCoins(coinControl, false, forEstimation);

CAmount availableBalance = CalculateLelantusCoinsBalance(coins.begin(), coins.end());

if (required > availableBalance) {
Expand Down Expand Up @@ -6877,13 +6873,14 @@ std::pair<CAmount, unsigned int> CWallet::EstimateJoinSplitFee(CAmount required,
unsigned size;
std::vector<CLelantusEntry> spendCoins;
std::vector<CSigmaEntry> sigmaSpendCoins;

std::list<CSigmaEntry> coins = this->GetAvailableCoins(coinControl, false, true);
std::list<CSigmaEntry> sigmaCoins = this->GetAvailableCoins(coinControl, false, true);
CAmount availableSigmaBalance(0);
for (auto coin : coins) {
for (auto coin : sigmaCoins) {
availableSigmaBalance += coin.get_denomination_value();
}

std::list<CLelantusEntry> coins = GetAvailableLelantusCoins(coinControl, false, true);

for (fee = payTxFee.GetFeePerK();;) {
CAmount currentRequired = required;

Expand All @@ -6903,26 +6900,25 @@ std::pair<CAmount, unsigned int> CWallet::EstimateJoinSplitFee(CAmount required,
inputFromSigma = availableSigmaBalance;
else
inputFromSigma = currentRequired;

this->GetCoinsToSpend(inputFromSigma, sigmaSpendCoins, denomChanges, //try to spend sigma first
this->GetCoinsToSpend(inputFromSigma, sigmaSpendCoins, denomChanges, sigmaCoins, //try to spend sigma first
consensusParams.nMaxLelantusInputPerTransaction,
consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl, true);
consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl);
currentRequired -= inputFromSigma;
}

if (currentRequired > 0) {
if (!this->GetCoinsToJoinSplit(currentRequired, spendCoins, changeToMint,
if (!this->GetCoinsToJoinSplit(currentRequired, spendCoins, changeToMint, coins,
consensusParams.nMaxLelantusInputPerTransaction,
consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl, true)) {
consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl)) {
return std::make_pair(0, 0);
}
}
} catch (std::runtime_error) {
}

// 956 is constant part, mainly Schnorr and Range proof, 2560 is for each sigma/aux data
// 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data
// 179 other parts of tx, assuming 1 utxo and 1 jmint
size = 956 + 2560 * (spendCoins.size() + sigmaSpendCoins.size()) + 179;
size = 1054 + 2560 * (spendCoins.size() + sigmaSpendCoins.size()) + 179;
CAmount feeNeeded = CWallet::GetMinimumFee(size, nTxConfirmTarget, mempool);

if (fee >= feeNeeded) {
Expand Down
8 changes: 4 additions & 4 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -943,19 +943,19 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
CAmount required,
std::vector<CSigmaEntry>& coinsToSpend_out,
std::vector<sigma::CoinDenomination>& coinsToMint_out,
std::list<CSigmaEntry>& coins,
const size_t coinsLimit = SIZE_MAX,
const CAmount amountLimit = MAX_MONEY,
const CCoinControl *coinControl = NULL,
bool forEstimation = false) const;
const CCoinControl *coinControl = NULL) const;

bool GetCoinsToJoinSplit(
CAmount required,
std::vector<CLelantusEntry>& coinsToSpend_out,
CAmount& changeToMint,
std::list<CLelantusEntry> coins,
const size_t coinsToSpendLimit = SIZE_MAX,
const CAmount amountToSpendLimit = MAX_MONEY,
const CCoinControl *coinControl = NULL,
bool forEstimation = false) const;
const CCoinControl *coinControl = NULL) const;

/**
* Insert additional inputs into the transaction by
Expand Down

0 comments on commit 54cc566

Please sign in to comment.