Skip to content

Commit

Permalink
Double check tx size when creating zPIV tx's.
Browse files Browse the repository at this point in the history
It is possible for a spend of 7 to be larger than the max transaction size. Catch these cases and send a correct error report up the chain to the GUI.

Github-Merge: PIVX-Project#412
Rebased-From: 0983755
  • Loading branch information
presstab authored and meyer9 committed Feb 15, 2018
1 parent a652647 commit ea1ad2f
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 27 deletions.
65 changes: 40 additions & 25 deletions src/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1326,7 +1326,7 @@ CAmount CWallet::GetUnconfirmedZerocoinBalance() const
CAmount nUnconfirmed = 0;
CWalletDB walletdb(pwalletMain->strWalletFile);
list<CZerocoinMint> listMints = walletdb.ListMintedCoins(true, false, true);

std::map<libzerocoin::CoinDenomination, int> mapUnconfirmed;
for (const auto& denom : libzerocoin::zerocoinDenomList){
mapUnconfirmed.insert(make_pair(denom, 0));
Expand Down Expand Up @@ -4065,14 +4065,14 @@ bool CWallet::CreateZerocoinMintTransaction(const CAmount nValue, CMutableTransa
bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, const uint256& hashTxOut, CTxIn& newTxIn, CZerocoinSpendReceipt& receipt)
{
// Default error status if not changed below
receipt.SetStatus("Transaction Mint Started", ZPHR_TXMINT_GENERAL);
receipt.SetStatus(_("Transaction Mint Started"), ZPHR_TXMINT_GENERAL);

libzerocoin::CoinDenomination denomination = zerocoinSelected.GetDenomination();
// 2. Get pubcoin from the private coin
libzerocoin::PublicCoin pubCoinSelected(Params().Zerocoin_Params(), zerocoinSelected.GetValue(), denomination);
LogPrintf("%s : pubCoinSelected:\n denom=%d\n value%s\n", __func__, denomination, pubCoinSelected.getValue().GetHex());
if (!pubCoinSelected.validate()) {
receipt.SetStatus("the selected mint coin is an invalid coin", ZPHR_INVALID_COIN);
receipt.SetStatus(_("the selected mint coin is an invalid coin"), ZPHR_INVALID_COIN);
return false;
}

Expand All @@ -4082,7 +4082,7 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con
string strFailReason = "";
int nMintsAdded = 0;
if (!GenerateAccumulatorWitness(pubCoinSelected, accumulator, witness, nSecurityLevel, nMintsAdded, strFailReason)) {
receipt.SetStatus("Try to spend with a higher security level to include more coins", ZPHR_FAILED_ACCUMULATOR_INITIALIZATION);
receipt.SetStatus(_("Try to spend with a higher security level to include more coins"), ZPHR_FAILED_ACCUMULATOR_INITIALIZATION);
LogPrintf("%s : %s \n", __func__, receipt.GetStatusMessage());
return false;
}
Expand All @@ -4098,7 +4098,7 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con
libzerocoin::CoinSpend spend(Params().Zerocoin_Params(), privateCoin, accumulator, nChecksum, witness, hashTxOut);

if (!spend.Verify(accumulator)) {
receipt.SetStatus("the new spend coin transaction did not verify", ZPHR_INVALID_WITNESS);
receipt.SetStatus(_("The new spend coin transaction did not verify"), ZPHR_INVALID_WITNESS);
return false;
}

Expand All @@ -4122,13 +4122,13 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con
serializedCoinSpendChecking << spend;
}
catch (...) {
receipt.SetStatus("failed to deserialize", ZPHR_BAD_SERIALIZATION);
receipt.SetStatus(_("Failed to deserialize"), ZPHR_BAD_SERIALIZATION);
return false;
}

libzerocoin::CoinSpend newSpendChecking(Params().Zerocoin_Params(), serializedCoinSpendChecking);
if (!newSpendChecking.Verify(accumulator)) {
receipt.SetStatus("the transaction did not verify", ZPHR_BAD_SERIALIZATION);
receipt.SetStatus(_("The transaction did not verify"), ZPHR_BAD_SERIALIZATION);
return false;
}

Expand All @@ -4141,7 +4141,7 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con
LogPrintf("%s failed to write zerocoinmint\n", __func__);

pwalletMain->NotifyZerocoinChanged(pwalletMain, zerocoinSelected.GetValue().GetHex(), "Used", CT_UPDATED);
receipt.SetStatus("the coin spend has been used", ZPHR_SPENT_USED_ZPHR);
receipt.SetStatus(_("The coin spend has been used"), ZPHR_SPENT_USED_ZPHR);
return false;
}
}
Expand All @@ -4152,11 +4152,11 @@ bool CWallet::MintToTxIn(CZerocoinMint zerocoinSelected, int nSecurityLevel, con
receipt.AddSpend(zcSpend);
}
catch (const std::exception&) {
receipt.SetStatus("CoinSpend: Accumulator witness does not verify", ZPHR_INVALID_WITNESS);
receipt.SetStatus(_("CoinSpend: Accumulator witness does not verify"), ZPHR_INVALID_WITNESS);
return false;
}

receipt.SetStatus("Spend Valid", ZPHR_SPEND_OKAY); // Everything okay
receipt.SetStatus(_("Spend Valid"), ZPHR_SPEND_OKAY); // Everything okay

return true;
}
Expand All @@ -4166,12 +4166,12 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
// Check available funds
int nStatus = ZPHR_TRX_FUNDS_PROBLEMS;
if (nValue > GetZerocoinBalance(true)) {
receipt.SetStatus("You don't have enough Zerocoins in your wallet", nStatus);
receipt.SetStatus(_("You don't have enough Zerocoins in your wallet"), nStatus);
return false;
}

if (nValue < 1) {
receipt.SetStatus("Value is below the the smallest available denomination (= 1) of zPhr", nStatus);
receipt.SetStatus(_("Value is below the the smallest available denomination (= 1) of zPhr"), nStatus);
return false;
}

Expand All @@ -4188,7 +4188,7 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
if (vSelectedMints.empty()) {
listMints = walletdb.ListMintedCoins(true, true, true); // need to find mints to spend
if(listMints.empty()) {
receipt.SetStatus("failed to find Zerocoins in in wallet.dat", nStatus);
receipt.SetStatus(_("Failed to find Zerocoins in in wallet.dat"), nStatus);
return false;
}

Expand All @@ -4214,7 +4214,7 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
for (CZerocoinMint mint : vSelectedMints) {
// see if this serial has already been spent
if (IsSerialKnown(mint.GetSerialNumber())) {
receipt.SetStatus("trying to spend an already spent serial #, try again.", nStatus);
receipt.SetStatus(_("Trying to spend an already spent serial #, try again."), nStatus);

mint.SetUsed(true);
walletdb.WriteZerocoinMint(mint);
Expand All @@ -4227,10 +4227,10 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
uint256 hashBlock;
bool fArchive = false;
if (!GetTransaction(mint.GetTxHash(), txMint, hashBlock)) {
receipt.SetStatus("unable to find transaction containing mint", nStatus);
receipt.SetStatus(_("Unable to find transaction containing mint"), nStatus);
fArchive = true;
} else if (mapBlockIndex.count(hashBlock) < 1) {
receipt.SetStatus("mint did not make it into blockchain", nStatus);
receipt.SetStatus(_("Mint did not make it into blockchain"), nStatus);
fArchive = true;
}

Expand All @@ -4246,16 +4246,16 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
if (vSelectedMints.empty()) {
if(nNeededSpends > 0){
// Too much spends needed, so abuse nStatus to report back the number of needed spends
receipt.SetStatus("Too much spends needed", nStatus, nNeededSpends);
receipt.SetStatus(_("Too many spends needed"), nStatus, nNeededSpends);
}
else {
receipt.SetStatus("failed to select a zerocoin", nStatus);
receipt.SetStatus(_("Failed to select a zerocoin"), nStatus);
}
return false;
}

if ((static_cast<int>(vSelectedMints.size()) > Params().Zerocoin_MaxSpendsPerTransaction())) {
receipt.SetStatus("Failed to find coin set amongst held coins with less than maxNumber of Spends", nStatus);
receipt.SetStatus(_("Failed to find coin set amongst held coins with less than maxNumber of Spends"), nStatus);
return false;
}

Expand All @@ -4275,9 +4275,11 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
CScript scriptChange;
CAmount nChange = nValueSelected - nValue;
if (nChange && !address) {
receipt.SetStatus("Need address because change is not exact", nStatus);
receipt.SetStatus(_("Need address because change is not exact"), nStatus);
return false;
} else if (address) {
}

if (address) {
scriptZerocoinSpend = GetScriptForDestination(address->Get());
if (nChange) {
// Reserve a new key pair from key pool
Expand All @@ -4299,7 +4301,7 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
CAmount nFeeRet = 0;
string strFailReason = "";
if (!CreateZerocoinMintTransaction(nChange, txNew, vNewMints, &reserveKey, nFeeRet, strFailReason, NULL, true)) {
receipt.SetStatus("Failed to create mint", nStatus);
receipt.SetStatus(_("Failed to create mint"), nStatus);
return false;
}
} else {
Expand All @@ -4324,13 +4326,20 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
txNew.vin.push_back(newTxIn);
}

// Limit size
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_ZEROCOIN_TX_SIZE) {
receipt.SetStatus(_("In rare cases, a spend with 7 coins exceeds our maximum allowable transaction size, please retry spend using 6 or less coins"), ZPHR_TX_TOO_LARGE);
return false;
}

//now that all inputs have been added, add full tx hash to zerocoinspend records and write to db
uint256 txHash = txNew.GetHash();
for (CZerocoinSpend spend : receipt.GetSpends()) {
spend.SetTxHash(txHash);

if (!CWalletDB(strWalletFile).WriteZerocoinSpendSerialEntry(spend)) {
receipt.SetStatus("failed to write coin serial number into wallet", nStatus);
receipt.SetStatus(_("Failed to write coin serial number into wallet"), nStatus);
}
}

Expand All @@ -4342,7 +4351,7 @@ bool CWallet::CreateZerocoinSpendTransaction(CAmount nValue, int nSecurityLevel,
}
}

receipt.SetStatus("Transaction Created", ZPHR_SPEND_OKAY); // Everything okay
receipt.SetStatus(_("Transaction Created"), ZPHR_SPEND_OKAY); // Everything okay

return true;
}
Expand Down Expand Up @@ -4501,7 +4510,7 @@ string CWallet::MintZerocoin(CAmount nValue, CWalletTx& wtxNew, vector<CZerocoin

if (IsLocked()) {
string strError = _("Error: Wallet locked, unable to create transaction!");
printf("MintZerocoin() : %s", strError.c_str());
LogPrintf("MintZerocoin() : %s", strError.c_str());
return strError;
}

Expand All @@ -4517,6 +4526,12 @@ string CWallet::MintZerocoin(CAmount nValue, CWalletTx& wtxNew, vector<CZerocoin
wtxNew.fFromMe = true;
wtxNew.fTimeReceivedIsTxTime = true;

// Limit size
unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);
if (nBytes >= MAX_ZEROCOIN_TX_SIZE) {
return _("Error: The transaction is larger than the maximum allowed transaction size!");
}

//commit the transaction to the network
if (!CommitTransaction(wtxNew, reservekey)) {
return _("Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
Expand Down
5 changes: 3 additions & 2 deletions src/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ enum ZerocoinSpendStatus {
ZPHR_FAILED_ACCUMULATOR_INITIALIZATION = 11, // Failed to initialize witness
ZPHR_INVALID_WITNESS = 12, // Spend coin transaction did not verify
ZPHR_BAD_SERIALIZATION = 13, // Transaction verification failed
ZPHR_SPENT_USED_ZPHR = 14 // Coin has already been spend
ZPHR_SPENT_USED_ZPHR = 14, // Coin has already been spend
ZPHR_TX_TOO_LARGE = 15 // The transaction is larger than the max tx size
};

struct CompactTallyItem {
Expand Down Expand Up @@ -313,7 +314,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
{
fBackupMints = fEnabled;
}

bool isMultiSendEnabled()
{
return fMultiSendMasternodeReward || fMultiSendStake;
Expand Down

0 comments on commit ea1ad2f

Please sign in to comment.