Skip to content

Commit

Permalink
Merge pull request #928 from firoorg/lelantus_fee
Browse files Browse the repository at this point in the history
Lelantus fixes
  • Loading branch information
levonpetrosyan93 committed Nov 17, 2020
2 parents db0e002 + e826f80 commit ea199b4
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 37 deletions.
28 changes: 18 additions & 10 deletions src/hdmint/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ bool CHDMintWallet::GenerateLelantusMint(CWalletDB& walletdb, lelantus::PrivateC
* @param sigma reference to full mint object
* @return success
*/
bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CSigmaEntry& sigma)
bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CSigmaEntry& sigma, bool forEstimation)
{
sigma::CoinDenomination denom;
IntegerToDenomination(dMint.GetAmount(), denom);
Expand All @@ -1013,16 +1013,20 @@ bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CS
CKeyID seedId = dMint.GetSeedId();
int32_t nCount = dMint.GetCount();
MintPoolEntry mintPoolEntry(hashSeedMaster, seedId, nCount);
GenerateMint(walletdb, denom, coin, dMintDummy, mintPoolEntry, true);
if(!forEstimation)
GenerateMint(walletdb, denom, coin, dMintDummy, mintPoolEntry, true);

//Fill in the sigmamint object's details
GroupElement bnValue = coin.getPublicCoin().getValue();
if (primitives::GetPubCoinValueHash(bnValue) != dMint.GetPubCoinHash())
if (primitives::GetPubCoinValueHash(bnValue) != dMint.GetPubCoinHash() && !forEstimation)
return error("%s: failed to correctly generate mint, pubcoin hash mismatch", __func__);
sigma.value = bnValue;
if(forEstimation)
sigma.value = dMint.GetPubcoinValue();
else
sigma.value = bnValue;

Scalar bnSerial = coin.getSerialNumber();
if (primitives::GetSerialHash(bnSerial) != dMint.GetSerialHash())
if (primitives::GetSerialHash(bnSerial) != dMint.GetSerialHash() && !forEstimation)
return error("%s: failed to correctly generate mint, serial hash mismatch", __func__);

sigma.set_denomination(denom);
Expand All @@ -1036,7 +1040,7 @@ bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CS
return true;
}

bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CLelantusEntry& lelantusEntry)
bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CLelantusEntry& lelantusEntry, bool forEstimation)
{
//Generate the coin
lelantus::PrivateCoin coin(lelantus::Params::get_default(), dMint.GetAmount());
Expand All @@ -1045,16 +1049,20 @@ bool CHDMintWallet::RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CL
int32_t nCount = dMint.GetCount();
MintPoolEntry mintPoolEntry(hashSeedMaster, seedId, nCount);
uint160 dummySeedId;
GenerateLelantusMint(walletdb, coin, dMintDummy, dummySeedId, mintPoolEntry, true);
if(!forEstimation)
GenerateLelantusMint(walletdb, coin, dMintDummy, dummySeedId, mintPoolEntry, true);

//Fill in the lelantus object's details
GroupElement bnValue = coin.getPublicCoin().getValue();
if (primitives::GetPubCoinValueHash(bnValue) != dMint.GetPubCoinHash())
if (primitives::GetPubCoinValueHash(bnValue) != dMint.GetPubCoinHash() && !forEstimation)
return error("%s: failed to correctly generate lelantus mint, pubcoin hash mismatch", __func__);
lelantusEntry.value = bnValue;
if(forEstimation)
lelantusEntry.value = dMint.GetPubcoinValue();
else
lelantusEntry.value = bnValue;

Scalar bnSerial = coin.getSerialNumber();
if (primitives::GetSerialHash(bnSerial) != dMint.GetSerialHash())
if (primitives::GetSerialHash(bnSerial) != dMint.GetSerialHash() && !forEstimation)
return error("%s: failed to correctly generate lelantus mint, serial hash mismatch", __func__);

lelantusEntry.amount = dMint.GetAmount();
Expand Down
4 changes: 2 additions & 2 deletions src/hdmint/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ class CHDMintWallet
bool GenerateMint(CWalletDB& walletdb, const sigma::CoinDenomination denom, sigma::PrivateCoin& coin, CHDMint& dMint, boost::optional<MintPoolEntry> mintPoolEntry = boost::none, bool fAllowUnsynced=false);
bool GenerateLelantusMint(CWalletDB& walletdb, lelantus::PrivateCoin& coin, CHDMint& dMint, uint160& seedIdOut, boost::optional<MintPoolEntry> mintPoolEntry = boost::none, bool fAllowUnsynced=false);
bool LoadMintPoolFromDB();
bool RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CSigmaEntry& sigma);
bool RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CLelantusEntry& sigma);
bool RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CSigmaEntry& sigma, bool forEstimation = false);
bool RegenerateMint(CWalletDB& walletdb, const CHDMint& dMint, CLelantusEntry& sigma, bool forEstimation = false);
bool GetSerialForPubcoin(const std::vector<std::pair<uint256, GroupElement>>& serialPubcoinPairs, const uint256& hashPubcoin, uint256& hashSerial);
bool IsSerialInBlockchain(const uint256& hashSerial, int& nHeightTx, uint256& txidSpend, CTransactionRef & tx);
bool IsLelantusSerialInBlockchain(const uint256& hashSerial, int& nHeightTx, uint256& txidSpend, CTransactionRef & tx);
Expand Down
13 changes: 10 additions & 3 deletions src/qt/coincontroldialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,14 @@ void CoinControlDialog::updateView()
CAmount nSum = 0;
int nChildren = 0;
BOOST_FOREACH(const COutput& out, coins.second) {
nSum += out.tx->tx->vout[out.i].nValue;
CAmount amount;
if(out.tx->tx->vout[out.i].scriptPubKey.IsLelantusJMint()) {
amount = model->GetJMintCredit(out.tx->tx->vout[out.i]);
} else {
amount = out.tx->tx->vout[out.i].nValue;
}

nSum += amount;
nChildren++;

CCoinControlWidgetItem *itemOutput;
Expand Down Expand Up @@ -728,8 +735,8 @@ void CoinControlDialog::updateView()
}

// amount
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue));
itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly
itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, amount));
itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)amount)); // padding so that sorting works correctly

// date
itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
Expand Down
4 changes: 4 additions & 0 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,10 @@ std::vector<CSigmaEntry> WalletModel::GetUnsafeCoins(const CCoinControl* coinCon
return unsafeCoins;
}

CAmount WalletModel::GetJMintCredit(const CTxOut& txout) const {
return wallet->GetCredit(txout, ISMINE_SPENDABLE);
}

bool WalletModel::isWalletEnabled()
{
return !GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);
Expand Down
2 changes: 2 additions & 0 deletions src/qt/walletmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ class WalletModel : public QObject

std::vector<CSigmaEntry> GetUnsafeCoins(const CCoinControl* coinControl = NULL);

CAmount GetJMintCredit(const CTxOut& txout) const;

private:
CWallet *wallet;

Expand Down
96 changes: 80 additions & 16 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2800,7 +2800,7 @@ CAmount CWallet::SelectSpendCoinsForAmount(
return required - val;
}

std::list<CSigmaEntry> CWallet::GetAvailableCoins(const CCoinControl *coinControl, bool includeUnsafe) const {
std::list<CSigmaEntry> CWallet::GetAvailableCoins(const CCoinControl *coinControl, bool includeUnsafe, bool forEstimation) const {
EnsureMintWalletAvailable();

LOCK2(cs_main, cs_wallet);
Expand All @@ -2810,7 +2810,7 @@ std::list<CSigmaEntry> CWallet::GetAvailableCoins(const CCoinControl *coinContro
list<CMintMeta> listMints(vecMints.begin(), vecMints.end());
for (const CMintMeta& mint : listMints) {
CSigmaEntry entry;
GetMint(mint.hashSerial, entry);
GetMint(mint.hashSerial, entry, forEstimation);
coins.push_back(entry);
}

Expand Down Expand Up @@ -2878,7 +2878,7 @@ std::list<CSigmaEntry> CWallet::GetAvailableCoins(const CCoinControl *coinContro
return coins;
}

std::list<CLelantusEntry> CWallet::GetAvailableLelantusCoins(const CCoinControl *coinControl, bool includeUnsafe) const {
std::list<CLelantusEntry> CWallet::GetAvailableLelantusCoins(const CCoinControl *coinControl, bool includeUnsafe, bool forEstimation) const {
EnsureMintWalletAvailable();

LOCK2(cs_main, cs_wallet);
Expand All @@ -2887,7 +2887,7 @@ std::list<CLelantusEntry> CWallet::GetAvailableLelantusCoins(const CCoinControl
std::vector<CLelantusMintMeta> vecMints = zwallet->GetTracker().ListLelantusMints(true, true, false);
for (const CLelantusMintMeta& mint : vecMints) {
CLelantusEntry entry;
GetMint(mint.hashSerial, entry);
GetMint(mint.hashSerial, entry, forEstimation);
if(entry.amount != 0) // ignore 0 mints which where created to increase privacy
coins.push_back(entry);
}
Expand Down Expand Up @@ -3018,7 +3018,8 @@ bool CWallet::GetCoinsToSpend(
std::vector<sigma::CoinDenomination>& coinsToMint_out,
const size_t coinsToSpendLimit,
const CAmount amountToSpendLimit,
const CCoinControl *coinControl) const
const CCoinControl *coinControl,
bool forEstimation) 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 @@ -3047,7 +3048,7 @@ bool CWallet::GetCoinsToSpend(
_("Required amount exceed value spend limit"));
}

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

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

Expand Down Expand Up @@ -3149,7 +3150,8 @@ bool CWallet::GetCoinsToJoinSplit(
CAmount& changeToMint,
const size_t coinsToSpendLimit,
const CAmount amountToSpendLimit,
const CCoinControl *coinControl) const
const CCoinControl *coinControl,
bool forEstimation) 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 @@ -3168,7 +3170,7 @@ bool CWallet::GetCoinsToJoinSplit(
_("The required amount exceeds spend limit"));
}

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

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

Expand Down Expand Up @@ -3406,7 +3408,7 @@ void CWallet::AvailableCoins(vector <COutput> &vCoins, bool fOnlyConfirmed, cons

if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
(!IsLockedCoin((*it).first, i) || nCoinType == CoinType::ONLY_1000) &&
(pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue) &&
(pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue || (pcoin->tx->vout[i].scriptPubKey.IsLelantusJMint() && GetCredit(pcoin->tx->vout[i], ISMINE_SPENDABLE) > 0)) &&
(!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) {
vCoins.push_back(COutput(pcoin, i, nDepth,
((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
Expand Down Expand Up @@ -6853,6 +6855,69 @@ CWalletTx CWallet::CreateLelantusJoinSplitTransaction(
return tx;
}

CAmount CWallet::EstimateJoinSplitFee(CAmount required, bool subtractFeeFromAmount, const CCoinControl *coinControl) {
CAmount fee;

std::vector<CLelantusEntry> spendCoins;
std::vector<CSigmaEntry> sigmaSpendCoins;

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

if (!subtractFeeFromAmount)
currentRequired += fee;


spendCoins.clear();
sigmaSpendCoins.clear();
auto &consensusParams = Params().GetConsensus();
CAmount changeToMint = 0;

std::vector<sigma::CoinDenomination> denomChanges;
try {
std::list<CSigmaEntry> coins = this->GetAvailableCoins(coinControl, false, true);
CAmount availableBalance(0);
for (auto coin : coins) {
availableBalance += coin.get_denomination_value();
}
if (availableBalance > 0) {
CAmount inputFromSigma;
if (currentRequired > availableBalance)
inputFromSigma = availableBalance;
else
inputFromSigma = currentRequired;

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

if (currentRequired > 0) {
if (!this->GetCoinsToJoinSplit(currentRequired, spendCoins, changeToMint,
consensusParams.nMaxLelantusInputPerTransaction,
consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl, true)) {
throw InsufficientFunds();
}
}

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

if (fee >= feeNeeded) {
break;
}

fee = feeNeeded;
}

return fee;
}

bool CWallet::CommitLelantusTransaction(CWalletTx& wtxNew, std::vector<CLelantusEntry>& spendCoins, std::vector<CHDMint>& mintCoins) {
EnsureMintWalletAvailable();

Expand Down Expand Up @@ -6939,11 +7004,11 @@ bool CWallet::CommitLelantusTransaction(CWalletTx& wtxNew, std::vector<CLelantus
}


bool CWallet::GetMint(const uint256& hashSerial, CSigmaEntry& zerocoin) const
bool CWallet::GetMint(const uint256& hashSerial, CSigmaEntry& zerocoin, bool forEstimation) const
{
EnsureMintWalletAvailable();

if (IsLocked()) {
if (IsLocked() && !forEstimation) {
return false;
}

Expand All @@ -6956,7 +7021,7 @@ bool CWallet::GetMint(const uint256& hashSerial, CSigmaEntry& zerocoin) const
CHDMint dMint;
if (!walletdb.ReadHDMint(meta.GetPubCoinValueHash(), false, dMint))
return error("%s: failed to read deterministic mint", __func__);
if (!zwallet->RegenerateMint(walletdb, dMint, zerocoin))
if (!zwallet->RegenerateMint(walletdb, dMint, zerocoin, forEstimation))
return error("%s: failed to generate mint", __func__);

return true;
Expand All @@ -6967,11 +7032,11 @@ bool CWallet::GetMint(const uint256& hashSerial, CSigmaEntry& zerocoin) const
return true;
}

bool CWallet::GetMint(const uint256& hashSerial, CLelantusEntry& mint) const
bool CWallet::GetMint(const uint256& hashSerial, CLelantusEntry& mint, bool forEstimation) const
{
EnsureMintWalletAvailable();

if (IsLocked()) {
if (IsLocked() && !forEstimation) {
return false;
}

Expand All @@ -6984,9 +7049,8 @@ bool CWallet::GetMint(const uint256& hashSerial, CLelantusEntry& mint) const
CHDMint dMint;
if (!walletdb.ReadHDMint(meta.GetPubCoinValueHash(), true, dMint))
return error("%s: failed to read deterministic Lelantus mint", __func__);
if (!zwallet->RegenerateMint(walletdb, dMint, mint))
if (!zwallet->RegenerateMint(walletdb, dMint, mint, forEstimation))
return error("%s: failed to generate Lelantus mint", __func__);

return true;

}
Expand Down
16 changes: 10 additions & 6 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -926,9 +926,9 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface

// Returns a list of unspent and verified coins, I.E. coins which are ready
// to be spent.
std::list<CSigmaEntry> GetAvailableCoins(const CCoinControl *coinControl = NULL, bool includeUnsafe = false) const;
std::list<CSigmaEntry> GetAvailableCoins(const CCoinControl *coinControl = NULL, bool includeUnsafe = false, bool forEstimation = false) const;

std::list<CLelantusEntry> GetAvailableLelantusCoins(const CCoinControl *coinControl = NULL, bool includeUnsafe = false) const;
std::list<CLelantusEntry> GetAvailableLelantusCoins(const CCoinControl *coinControl = NULL, bool includeUnsafe = false, bool forEstimation = false) const;

std::vector<unsigned char> EncryptMintAmount(uint64_t amount, const secp_primitives::GroupElement& pubcoin) const;

Expand All @@ -947,15 +947,17 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
std::vector<sigma::CoinDenomination>& coinsToMint_out,
const size_t coinsLimit = SIZE_MAX,
const CAmount amountLimit = MAX_MONEY,
const CCoinControl *coinControl = NULL) const;
const CCoinControl *coinControl = NULL,
bool forEstimation = false) const;

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

/**
* Insert additional inputs into the transaction by
Expand Down Expand Up @@ -1040,9 +1042,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface

void JoinSplitLelantus(const std::vector<CRecipient>& recipients, const std::vector<CAmount>& newMints, CWalletTx& result);

bool GetMint(const uint256& hashSerial, CSigmaEntry& zerocoin) const;
CAmount EstimateJoinSplitFee(CAmount required, bool subtractFeeFromAmount, const CCoinControl *coinControl);

bool GetMint(const uint256& hashSerial, CLelantusEntry& mint) const;
bool GetMint(const uint256& hashSerial, CSigmaEntry& zerocoin, bool forEstimation = false) const;

bool GetMint(const uint256& hashSerial, CLelantusEntry& mint, bool forEstimation = false) const;

bool CreateZerocoinMintModel(string &stringError,
const string& denomAmount);
Expand Down

0 comments on commit ea199b4

Please sign in to comment.