From bbc088926d9d1a9397fd87083c2d861a5174d1e6 Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Thu, 16 Aug 2018 16:11:38 -0600 Subject: [PATCH 01/12] Chaining-asset-tx --- src/assets/assets.cpp | 2 + src/assets/assets.h | 3 + src/core_write.cpp | 1 + src/test/assets/asset_tests.cpp | 3 + src/wallet/wallet.cpp | 448 +++++++++++++++++++++++++------- src/wallet/wallet.h | 1 + 6 files changed, 363 insertions(+), 95 deletions(-) diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index ed0c08d5a2..6f9e069384 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -53,6 +53,8 @@ static const std::regex VOTE_INDICATOR(R"(^[^^~#!]+\^[^~#!\/]+$)"); static const std::regex RAVEN_NAMES("^RVN$|^RAVEN$|^RAVENCOIN$|^RAVENC0IN$|^RAVENCO1N$|^RAVENC01N$"); +std::map mapMempoolAssetOutpoints; + bool IsRootNameValid(const std::string& name) { return std::regex_match(name, ROOT_NAME_CHARACTERS) diff --git a/src/assets/assets.h b/src/assets/assets.h index a67ea46839..b2e4b7a735 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -49,6 +49,9 @@ struct CAssetOutputEntry; // 50000 * 82 Bytes == 4.1 Mb #define MAX_CACHE_ASSETS_SIZE 50000 +extern std::map mapMempoolAssetOutpoints; + + class CAssets { public: std::map > mapMyUnspentAssets; // Asset Name -> COutPoint diff --git a/src/core_write.cpp b/src/core_write.cpp index 97b917a328..c93c093d29 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -26,6 +26,7 @@ std::string ValueFromAmountString(const CAmount& amount, const int8_t units) int64_t remainder = n_abs % COIN; remainder = remainder / pow(10, 8 - units); + std::cout << quotient << std::endl; if (units == 0 && remainder == 0) { return strprintf("%s%d", sign ? "-" : "", quotient); } diff --git a/src/test/assets/asset_tests.cpp b/src/test/assets/asset_tests.cpp index 0c581ab4c9..b704a4838e 100644 --- a/src/test/assets/asset_tests.cpp +++ b/src/test/assets/asset_tests.cpp @@ -239,6 +239,9 @@ BOOST_FIXTURE_TEST_SUITE(asset_tests, BasicTestingSetup) amount = 1; BOOST_CHECK(ValueFromAmountString(amount, 8) == "0.00000001"); + amount = 40000000; + BOOST_CHECK(ValueFromAmountString(amount, 8) == "0.40000000"); + } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 72e8048a57..b725c838d5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -81,6 +81,15 @@ struct CompareValueOnly } }; +struct CompareAssetValueOnly +{ + bool operator()(const std::pair& t1, + const std::pair& t2) const + { + return t1.second < t2.second; + } +}; + std::string COutput::ToString() const { return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); @@ -2152,7 +2161,6 @@ void CWallet::AvailableCoinsWithAssets(std::vector& vCoins, std::map& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fWithAssets, bool fOnlySafe, const CCoinControl *coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t& nMaximumCount, const int& nMinDepth, const int& nMaxDepth) const { vCoins.clear(); @@ -2163,47 +2171,85 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map usedMempoolHashes; + std::map mapAssetTotals; std::map mapOutPoints; if (fWithAssets) { std::set setAssetMaxFound; // Turn the OutPoints into a map that is easily interatable. - for (auto out : setAssetOutPoint) { - if (mapWallet.count(out.hash)) { - const CWalletTx *pcoin = &mapWallet.at(out.hash); - - if (!CheckFinalTx(*pcoin)) - continue; + for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { + const uint256 &wtxid = it->first; + const CWalletTx *pcoin = &(*it).second; + + if (!CheckFinalTx(*pcoin)) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 0) + continue; + + // We should not consider coins which aren't at least in our mempool + // It's possible for these to be conflicted via ancestors which we may never be able to detect + if (nDepth == 0 && !pcoin->InMempool()) + continue; + + bool safeTx = pcoin->IsTrusted(); + + // We should not consider coins from transactions that are replacing + // other transactions. + // + // Example: There is a transaction A which is replaced by bumpfee + // transaction B. In this case, we want to prevent creation of + // a transaction B' which spends an output of B. + // + // Reason: If transaction A were initially confirmed, transactions B + // and B' would no longer be valid, so the user would have to create + // a new transaction C to replace B'. However, in the case of a + // one-block reorg, transactions B' and C might BOTH be accepted, + // when the user only wanted one of them. Specifically, there could + // be a 1-block reorg away from the chain where transactions A and C + // were accepted to another chain where B, B', and C were all + // accepted. + if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) { + safeTx = false; + } - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; + // Similarly, we should not consider coins from transactions that + // have been replaced. In the example above, we would want to prevent + // creation of a transaction A' spending an output of A, because if + // transaction B were initially confirmed, conflicting with A and + // A', we wouldn't want to the user to create a transaction D + // intending to replace A', but potentially resulting in a scenario + // where A, A', and D could all be accepted (instead of just B and + // D, or just A and A' like the user would want). + if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { + safeTx = false; + } - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < 0) - continue; + if (fOnlySafe && !safeTx) { + continue; + } - // We should not consider coins which aren't at least in our mempool - // It's possible for these to be conflicted via ancestors which we may never be able to detect - if (nDepth == 0 && !pcoin->InMempool()) - continue; + if (nDepth < nMinDepth || nDepth > nMaxDepth) + continue; - bool safeTx = pcoin->IsTrusted(); + for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { - // see explanation below - if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) { - safeTx = false; - } + if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i))) + continue; - // see explanation below - if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { - safeTx = false; - } + if (IsLockedCoin((*it).first, i)) + continue; - if (fOnlySafe && !safeTx) { + if (IsSpent(wtxid, i)) continue; - } - isminetype mine = IsMine(pcoin->tx->vout[out.n]); + isminetype mine = IsMine(pcoin->tx->vout[i]); if (mine == ISMINE_NO) { continue; @@ -2224,24 +2270,38 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::maptx->vout[out.n].scriptPubKey, assetTransfer, address)) { - strAssetName = assetTransfer.strName; - fWasTransferAssetOutPoint = true; - } else if (AssetFromScript(pcoin->tx->vout[out.n].scriptPubKey, asset, address)) { - strAssetName = asset.strName; - fWasNewAssetOutPoint = true; - } else if (OwnerAssetFromScript(pcoin->tx->vout[out.n].scriptPubKey, ownerName, address)) { - strAssetName = ownerName; - fWasOwnerAssetOutPoint = true; - } else if (ReissueAssetFromScript(pcoin->tx->vout[out.n].scriptPubKey, reissue, address)) { - strAssetName = reissue.strName; - fWasReissueAssetOutPoint = true; - } else { - continue; + + int nType; + bool fIsOwner; + if (pcoin->tx->vout[i].scriptPubKey.IsAssetScript(nType, fIsOwner)) { + + if ((txnouttype)nType == TX_TRANSFER_ASSET) { + if(TransferAssetFromScript(pcoin->tx->vout[i].scriptPubKey, assetTransfer, address)) { + strAssetName = assetTransfer.strName; + fWasTransferAssetOutPoint = true; + } + } else if ((txnouttype)nType == TX_NEW_ASSET && !fIsOwner) { + if (AssetFromScript(pcoin->tx->vout[i].scriptPubKey, asset, address)) + { + strAssetName = asset.strName; + fWasNewAssetOutPoint = true; + } + } else if ((txnouttype)nType == TX_NEW_ASSET && fIsOwner) { + if (OwnerAssetFromScript(pcoin->tx->vout[i].scriptPubKey, ownerName, address)) { + strAssetName = ownerName; + fWasOwnerAssetOutPoint = true; + } + } else if ((txnouttype)nType == TX_REISSUE_ASSET) { + if (ReissueAssetFromScript(pcoin->tx->vout[i].scriptPubKey, reissue, address)) { + strAssetName = reissue.strName; + fWasReissueAssetOutPoint = true; + } + } else { + continue; + } } - if (fWasNewAssetOutPoint || fWasTransferAssetOutPoint || fWasOwnerAssetOutPoint || - fWasReissueAssetOutPoint) { + if (fWasNewAssetOutPoint || fWasTransferAssetOutPoint || fWasOwnerAssetOutPoint || fWasReissueAssetOutPoint) { // If we already have the maximum amount or size for this asset, skip it if (setAssetMaxFound.count(strAssetName)) @@ -2255,7 +2315,7 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map& vValue, const C } } +static void ApproximateBestAssetSubset(const std::vector >& vValue, const CAmount& nTotalLower, const CAmount& nTargetValue, + std::vector& vfBest, CAmount& nBest, int iterations = 1000) +{ + std::vector vfIncluded; + + vfBest.assign(vValue.size(), true); + nBest = nTotalLower; + + FastRandomContext insecure_rand; + + for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) + { + vfIncluded.assign(vValue.size(), false); + CAmount nTotal = 0; + bool fReachedTarget = false; + for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) + { + for (unsigned int i = 0; i < vValue.size(); i++) + { + //The solver here uses a randomized algorithm, + //the randomness serves no real security purpose but is just + //needed to prevent degenerate behavior and it is important + //that the rng is fast. We do not use a constant random sequence, + //because there may be some privacy improvement by making + //the selection random. + if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i]) + { + nTotal += vValue[i].second; + vfIncluded[i] = true; + if (nTotal >= nTargetValue) + { + fReachedTarget = true; + if (nTotal < nBest) + { + nBest = nTotal; + vfBest = vfIncluded; + } + nTotal -= vValue[i].second; + vfIncluded[i] = false; + } + } + } + } + } +} + bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector vCoins, std::set& setCoinsRet, CAmount& nValueRet) const { @@ -2684,75 +2790,223 @@ bool CWallet::SelectCoins(const std::vector& vAvailableCoins, const CAm } /** RVN START */ -bool CWallet::SelectAssets(const std::map >& mapAvailableAssets, const std::map& mapAssetTargetValue, std::set& setCoinsRet, std::map& mapValueRet) const +bool CWallet::SelectAssetsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, const std::string& strAssetName, std::vector vCoins, + std::set& setCoinsRet, CAmount& nValueRet) const { - if (!AreAssetsDeployed()) - return false; + setCoinsRet.clear(); + nValueRet = 0; + + // List of values less than target + boost::optional coinLowestLarger; + boost::optional coinLowestLargerAmount; + std::vector > vValue; + std::map mapValueAmount; + CAmount nTotalLower = 0; - for (auto asset : mapAvailableAssets) { - if (!mapAssetTargetValue.count(asset.first)) + random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt); + + for (const COutput &output : vCoins) + { + if (!output.fSpendable) continue; - if (mapAssetTargetValue.at(asset.first) <= 0) + const CWalletTx *pcoin = output.tx; + + if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) continue; - if (!mapValueRet.count(asset.first)) - mapValueRet.insert(std::make_pair(asset.first, 0)); + if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors)) + continue; - for (auto out : asset.second) { - if (!out.fSpendable) - continue; + int i = output.i; - int nType = 0; - bool fIsOwner = false; - if (!out.tx->tx->vout[out.i].scriptPubKey.IsAssetScript(nType, fIsOwner)) - continue; + CInputCoin coin = CInputCoin(pcoin, i); - txnouttype type = (txnouttype) nType; + //------------------------------- - if (type == TX_NEW_ASSET && !fIsOwner) { - CNewAsset assetTemp; - std::string address; - if (!AssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, assetTemp, address)) - continue; - mapValueRet.at(assetTemp.strName) += assetTemp.nAmount; - } else if (type == TX_TRANSFER_ASSET) { - CAssetTransfer transferTemp; - std::string address; - if (!TransferAssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, transferTemp, address)) - continue; - mapValueRet.at(transferTemp.strName) += transferTemp.nAmount; - } else if (type == TX_NEW_ASSET && fIsOwner) { - std::string ownerName; - std::string address; - if (!OwnerAssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, ownerName, address)) - continue; - mapValueRet.at(ownerName) = OWNER_ASSET_AMOUNT; - } else if (type == TX_REISSUE_ASSET) { - CReissueAsset reissueTemp; - std::string address; - if (!ReissueAssetFromScript(out.tx->tx->vout[out.i].scriptPubKey, reissueTemp, address)) - continue; - mapValueRet.at(reissueTemp.strName) += reissueTemp.nAmount; - } else { - continue; - } + int nType = 0; + bool fIsOwner = false; + if (!coin.txout.scriptPubKey.IsAssetScript(nType, fIsOwner)) { + // TODO - Remove std::cout this before mainnet release + std::cout << "This shouldn't be occuring: Non Asset Script pub key made it to the SelectAssetsMinConf function call. Look into this!" << std::endl; + continue; + } + txnouttype type = (txnouttype) nType; - setCoinsRet.insert(CInputCoin(out.tx, out.i)); + CAmount nTempAmount = 0; + if (type == TX_NEW_ASSET && !fIsOwner) { // Root/Sub Asset + CNewAsset assetTemp; + std::string address; + if (!AssetFromScript(coin.txout.scriptPubKey, assetTemp, address)) + continue; + nTempAmount = assetTemp.nAmount; + } else if (type == TX_TRANSFER_ASSET) { // Transfer Asset + CAssetTransfer transferTemp; + std::string address; + if (!TransferAssetFromScript(coin.txout.scriptPubKey, transferTemp, address)) + continue; + nTempAmount = transferTemp.nAmount; + } else if (type == TX_NEW_ASSET && fIsOwner) { // Owner Asset + std::string ownerName; + std::string address; + if (!OwnerAssetFromScript(coin.txout.scriptPubKey, ownerName, address)) + continue; + nTempAmount = OWNER_ASSET_AMOUNT; + } else if (type == TX_REISSUE_ASSET) { // Reissue Asset + CReissueAsset reissueTemp; + std::string address; + if (!ReissueAssetFromScript(coin.txout.scriptPubKey, reissueTemp, address)) + continue; + nTempAmount = reissueTemp.nAmount; + } else { + continue; + } - if (mapValueRet.at(asset.first) >= mapAssetTargetValue.at(asset.first)) - break; + if (nTempAmount == nTargetValue) + { + setCoinsRet.insert(coin); + nValueRet += nTempAmount; + return true; } + else if (nTempAmount < nTargetValue + MIN_CHANGE) + { + vValue.push_back(std::make_pair(coin, nTempAmount)); + nTotalLower += nTempAmount; + } + else if (!coinLowestLarger || !coinLowestLargerAmount || nTempAmount < coinLowestLargerAmount) + { + coinLowestLarger = coin; + coinLowestLargerAmount = nTempAmount; + } + + +// setCoinsRet.insert(CInputCoin(out.tx, out.i)); +// +// if (mapValueRet.at(asset.first) >= mapAssetTargetValue.at(asset.first)) +// break; +// +// if (mapValueRet.at(asset.first) < mapAssetTargetValue.at(asset.first)) { +// return error( +// "%s : Tried to transfer an asset but this wallet didn't have enough, Asset Name: %s, Transfer Amount: %d, Wallet Total: %d", +// __func__, asset.first, mapValueRet.at(asset.first), mapAssetTargetValue.at(asset.first)); +// } + //------------------------------- + } - if (mapValueRet.at(asset.first) < mapAssetTargetValue.at(asset.first)) { - return error( - "%s : Tried to transfer an asset but this wallet didn't have enough, Asset Name: %s, Transfer Amount: %d, Wallet Total: %d", - __func__, asset.first, mapValueRet.at(asset.first), mapAssetTargetValue.at(asset.first)); + if (nTotalLower == nTargetValue) + { + for (const auto& pair : vValue) + { + setCoinsRet.insert(pair.first); + nValueRet += pair.second; } + return true; } + if (nTotalLower < nTargetValue) + { + if (!coinLowestLarger || !coinLowestLargerAmount) + return false; + setCoinsRet.insert(coinLowestLarger.get()); + nValueRet += coinLowestLargerAmount.get(); return true; + } + + // Solve subset sum by stochastic approximation + std::sort(vValue.begin(), vValue.end(), CompareAssetValueOnly()); + std::reverse(vValue.begin(), vValue.end()); + std::vector vfBest; + CAmount nBest; + + ApproximateBestAssetSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) + ApproximateBestAssetSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest); + + // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, + // or the next bigger coin is closer), return the bigger coin + if (coinLowestLarger && coinLowestLargerAmount && + ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLargerAmount <= nBest)) + { + setCoinsRet.insert(coinLowestLarger.get()); + nValueRet += coinLowestLargerAmount.get(); + } + else { + for (unsigned int i = 0; i < vValue.size(); i++) + if (vfBest[i]) + { + setCoinsRet.insert(vValue[i].first); + nValueRet += vValue[i].second; + } + + if (LogAcceptCategory(BCLog::SELECTCOINS)) { + LogPrint(BCLog::SELECTCOINS, "SelectAssets() best subset: "); + for (unsigned int i = 0; i < vValue.size(); i++) { + if (vfBest[i]) { + LogPrint(BCLog::SELECTCOINS, "%s : %s", strAssetName, FormatMoney(vValue[i].second)); + } + } + LogPrint(BCLog::SELECTCOINS, "total %s : %s\n", strAssetName, FormatMoney(nBest)); + } + } + + return true; +} + + +bool CWallet::SelectAssets(const std::map >& mapAvailableAssets, const std::map& mapAssetTargetValue, std::set& setCoinsRet, std::map& mapValueRet) const +{ + if (!AreAssetsDeployed()) + return false; + + size_t nMaxChainLength = std::min(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)); + bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); + + for (auto assetVector : mapAvailableAssets) { + // Setup temporay variables + std::vector vAssets(assetVector.second); + + std::set tempCoinsRet; + CAmount nTempAmountRet; + CAmount nTempTargetValue; + std::string strAssetName = assetVector.first; + + CAmount nValueFromPresetInputs = 0; // This is used with coincontrol, which assets doesn't support yet + + // If we dont have a target value for this asset, don't select coins for it + if (!mapAssetTargetValue.count(strAssetName)) + continue; + + // If we dont have a target value greater than zero, don't select coins for it + if (mapAssetTargetValue.at(strAssetName) <= 0) + continue; + + // Add the starting value into the mapValueRet + if (!mapValueRet.count(strAssetName)) + mapValueRet.insert(std::make_pair(strAssetName, 0)); + + // assign our temporary variable + nTempAmountRet = mapValueRet.at(strAssetName); + nTempTargetValue = mapAssetTargetValue.at(strAssetName); + + bool res = nTempTargetValue <= nValueFromPresetInputs || + SelectAssetsMinConf(nTempTargetValue - nValueFromPresetInputs, 1, 6, 0, strAssetName, vAssets, tempCoinsRet, nTempAmountRet) || + SelectAssetsMinConf(nTempTargetValue - nValueFromPresetInputs, 1, 1, 0, strAssetName, vAssets, tempCoinsRet, nTempAmountRet) || + (bSpendZeroConfChange && SelectAssetsMinConf(nTempTargetValue - nValueFromPresetInputs, 0, 1, 2, strAssetName, vAssets, tempCoinsRet, nTempAmountRet)) || + (bSpendZeroConfChange && SelectAssetsMinConf(nTempTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), strAssetName, vAssets, tempCoinsRet, nTempAmountRet)) || + (bSpendZeroConfChange && SelectAssetsMinConf(nTempTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, strAssetName, vAssets, tempCoinsRet, nTempAmountRet)) || + (bSpendZeroConfChange && SelectAssetsMinConf(nTempTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, strAssetName, vAssets, tempCoinsRet, nTempAmountRet)) || + (bSpendZeroConfChange && !fRejectLongChains && SelectAssetsMinConf(nTempTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits::max(), strAssetName, vAssets, tempCoinsRet, nTempAmountRet)); + + if (res) { + setCoinsRet.insert(tempCoinsRet.begin(), tempCoinsRet.end()); + mapValueRet.at(strAssetName) = nTempAmountRet + nValueFromPresetInputs; + } else { + return false; + } + } + + return true; } /** RVN END */ @@ -3090,6 +3344,8 @@ bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWall /** RVN START */ if (AreAssetsDeployed()) { + setAssets.clear(); + mapAssetsIn.clear(); if (!SelectAssets(mapAssetCoins, mapAssetValue, setAssets, mapAssetsIn)) { strFailReason = _("Insufficient asset funds"); return false; @@ -3398,6 +3654,8 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); + + mapMempoolAssetOutpoints[txin.prevout] = wtxNew.tx->GetHash(); } } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 25b3892a10..14259cc7ab 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -865,6 +865,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface * assembled */ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector vCoins, std::set& setCoinsRet, CAmount& nValueRet) const; + bool SelectAssetsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, const std::string& strAssetName, std::vector vCoins, std::set& setCoinsRet, CAmount& nValueRet) const; bool IsSpent(const uint256& hash, unsigned int n) const; From 49ac4398e9ca73687e4e5808982c446567f8026f Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Fri, 17 Aug 2018 15:10:46 -0600 Subject: [PATCH 02/12] Remove duplicate code for chaining transactions --- src/assets/assets.cpp | 35 +-- src/chainparams.cpp | 2 +- src/consensus/tx_verify.cpp | 23 +- src/core_write.cpp | 1 - src/wallet/wallet.cpp | 426 ++++++++++++++++++------------------ 5 files changed, 252 insertions(+), 235 deletions(-) diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index 22243e1815..10aff5fe64 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -2401,6 +2401,7 @@ bool SendAssetTransaction(CWallet* pwallet, CWalletTx& transaction, CReserveKey& error = std::make_pair(RPC_WALLET_ERROR, strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); return false; } + txid = transaction.GetHash().GetHex(); return true; } @@ -2413,23 +2414,23 @@ bool VerifyAssetOwner(const std::string& asset_name, std::set& myOwne std::string("This wallet is not the owner of the asset: ") + asset_name); return false; } - - // Get the outpoint that belongs to the Owner Asset - if (!passets->GetAssetsOutPoints(asset_name + OWNER_TAG, myOwnerOutPoints)) { - error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet can't find the owner token information for: ") + asset_name); - return false; - } - - // Check to make sure we have the right amount of outpoints - if (myOwnerOutPoints.size() == 0) { - error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet doesn't own any assets with the name: ") + asset_name + OWNER_TAG); - return false; - } - - if (myOwnerOutPoints.size() != 1) { - error = std::make_pair(RPC_INVALID_PARAMS, "Found multiple Owner Assets. Database is out of sync. You might have to run the wallet with -reindex"); - return false; - } +// +// // Get the outpoint that belongs to the Owner Asset +// if (!passets->GetAssetsOutPoints(asset_name + OWNER_TAG, myOwnerOutPoints)) { +// error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet can't find the owner token information for: ") + asset_name); +// return false; +// } +// +// // Check to make sure we have the right amount of outpoints +// if (myOwnerOutPoints.size() == 0) { +// error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet doesn't own any assets with the name: ") + asset_name + OWNER_TAG); +// return false; +// } +// +// if (myOwnerOutPoints.size() != 1) { +// error = std::make_pair(RPC_INVALID_PARAMS, "Found multiple Owner Assets. Database is out of sync. You might have to run the wallet with -reindex"); +// return false; +// } return true; } \ No newline at end of file diff --git a/src/chainparams.cpp b/src/chainparams.cpp index a145a85adc..4d72456ebd 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -412,7 +412,7 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_ASSETS].bit = 5; + consensus.vDeployments[Consensus::DEPLOYMENT_ASSETS].bit = 4; consensus.vDeployments[Consensus::DEPLOYMENT_ASSETS].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_ASSETS].nTimeout = 999999999999ULL; diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 9a7cddd390..8ec603b5fa 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -401,22 +401,27 @@ bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, c "bad-txns" + strError); } } + } + } + + for (const auto& outValue : totalOutputs) { + if (!totalInputs.count(outValue.first)) { + std::string errorMsg; + errorMsg = strprintf("Bad Transaction - Trying to create outpoint for asset that you don't have: %s", outValue.first); + return state.DoS(100, false, REJECT_INVALID, "bad-tx-inputs-outputs-mismatch " + errorMsg); + } + if (totalInputs.at(outValue.first) != outValue.second) { + std::string errorMsg; + errorMsg = strprintf("Bad Transaction - Assets would be burnt %s", outValue.first); + return state.DoS(100, false, REJECT_INVALID, "bad-tx-inputs-outputs-mismatch " + errorMsg); } } - // Check the input values and the output values + // Check the input size and the output size if (totalOutputs.size() != totalInputs.size()) { return state.DoS(100, false, REJECT_INVALID, "bad-tx-asset-inputs-size-does-not-match-outputs-size"); } - for (const auto& outValue : totalOutputs) { - if (!totalInputs.count(outValue.first)) - return state.DoS(100, false, REJECT_INVALID, "bad-tx-asset-inputs-does-not-have-asset-that-is-in-outputs"); - - if (totalInputs.at(outValue.first) != outValue.second) - return state.DoS(100, false, REJECT_INVALID, "bad-tx-asset-inputs-amount-mismatch-with-outputs-amount"); - } - return true; } diff --git a/src/core_write.cpp b/src/core_write.cpp index c93c093d29..97b917a328 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -26,7 +26,6 @@ std::string ValueFromAmountString(const CAmount& amount, const int8_t units) int64_t remainder = n_abs % COIN; remainder = remainder / pow(10, 8 - units); - std::cout << quotient << std::endl; if (units == 0 && remainder == 0) { return strprintf("%s%d", sign ? "-" : "", quotient); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b725c838d5..86220661b9 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2170,194 +2170,14 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map usedMempoolHashes; - - std::map mapAssetTotals; - std::map mapOutPoints; - if (fWithAssets) { - std::set setAssetMaxFound; - // Turn the OutPoints into a map that is easily interatable. - for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { - const uint256 &wtxid = it->first; - const CWalletTx *pcoin = &(*it).second; - - if (!CheckFinalTx(*pcoin)) - continue; - - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) - continue; - - int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < 0) - continue; - - // We should not consider coins which aren't at least in our mempool - // It's possible for these to be conflicted via ancestors which we may never be able to detect - if (nDepth == 0 && !pcoin->InMempool()) - continue; - - bool safeTx = pcoin->IsTrusted(); - - // We should not consider coins from transactions that are replacing - // other transactions. - // - // Example: There is a transaction A which is replaced by bumpfee - // transaction B. In this case, we want to prevent creation of - // a transaction B' which spends an output of B. - // - // Reason: If transaction A were initially confirmed, transactions B - // and B' would no longer be valid, so the user would have to create - // a new transaction C to replace B'. However, in the case of a - // one-block reorg, transactions B' and C might BOTH be accepted, - // when the user only wanted one of them. Specifically, there could - // be a 1-block reorg away from the chain where transactions A and C - // were accepted to another chain where B, B', and C were all - // accepted. - if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) { - safeTx = false; - } - - // Similarly, we should not consider coins from transactions that - // have been replaced. In the example above, we would want to prevent - // creation of a transaction A' spending an output of A, because if - // transaction B were initially confirmed, conflicting with A and - // A', we wouldn't want to the user to create a transaction D - // intending to replace A', but potentially resulting in a scenario - // where A, A', and D could all be accepted (instead of just B and - // D, or just A and A' like the user would want). - if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { - safeTx = false; - } - - if (fOnlySafe && !safeTx) { - continue; - } - - if (nDepth < nMinDepth || nDepth > nMaxDepth) - continue; - - for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { - - if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i))) - continue; - - if (IsLockedCoin((*it).first, i)) - continue; - - if (IsSpent(wtxid, i)) - continue; - - isminetype mine = IsMine(pcoin->tx->vout[i]); - - if (mine == ISMINE_NO) { - continue; - } - - bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || - (coinControl && coinControl->fAllowWatchOnly && - (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); - bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO; - - std::string address; - CAssetTransfer assetTransfer; - CNewAsset asset; - CReissueAsset reissue; - std::string ownerName; - bool fWasNewAssetOutPoint = false; - bool fWasTransferAssetOutPoint = false; - bool fWasOwnerAssetOutPoint = false; - bool fWasReissueAssetOutPoint = false; - std::string strAssetName; - - int nType; - bool fIsOwner; - if (pcoin->tx->vout[i].scriptPubKey.IsAssetScript(nType, fIsOwner)) { - - if ((txnouttype)nType == TX_TRANSFER_ASSET) { - if(TransferAssetFromScript(pcoin->tx->vout[i].scriptPubKey, assetTransfer, address)) { - strAssetName = assetTransfer.strName; - fWasTransferAssetOutPoint = true; - } - } else if ((txnouttype)nType == TX_NEW_ASSET && !fIsOwner) { - if (AssetFromScript(pcoin->tx->vout[i].scriptPubKey, asset, address)) - { - strAssetName = asset.strName; - fWasNewAssetOutPoint = true; - } - } else if ((txnouttype)nType == TX_NEW_ASSET && fIsOwner) { - if (OwnerAssetFromScript(pcoin->tx->vout[i].scriptPubKey, ownerName, address)) { - strAssetName = ownerName; - fWasOwnerAssetOutPoint = true; - } - } else if ((txnouttype)nType == TX_REISSUE_ASSET) { - if (ReissueAssetFromScript(pcoin->tx->vout[i].scriptPubKey, reissue, address)) { - strAssetName = reissue.strName; - fWasReissueAssetOutPoint = true; - } - } else { - continue; - } - } - - if (fWasNewAssetOutPoint || fWasTransferAssetOutPoint || fWasOwnerAssetOutPoint || fWasReissueAssetOutPoint) { - - // If we already have the maximum amount or size for this asset, skip it - if (setAssetMaxFound.count(strAssetName)) - continue; - - // Initialize the map vector is it doesn't exist yet - if (!mapAssetCoins.count(strAssetName)) { - std::vector vOutput; - mapAssetCoins.insert(std::make_pair(strAssetName, vOutput)); - } - - // Add the COutput to the map of available Asset Coins - mapAssetCoins.at(strAssetName).push_back( - COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); - - // Initialize the map of current asset totals - if (!mapAssetTotals.count(strAssetName)) - mapAssetTotals[strAssetName] = 0; - - // Update the map of totals depending the which type of asset tx we are looking at - if (fWasNewAssetOutPoint) - mapAssetTotals[strAssetName] += asset.nAmount; - else if (fWasTransferAssetOutPoint) - mapAssetTotals[strAssetName] += assetTransfer.nAmount; - else if (fWasReissueAssetOutPoint) - mapAssetTotals[strAssetName] += reissue.nAmount; - else if (fWasOwnerAssetOutPoint) - mapAssetTotals[strAssetName] = OWNER_ASSET_AMOUNT; - } - - // Checks the sum amount of all UTXO's, and adds to the set of assets that we found the max for - if (nMinimumSumAmount != MAX_MONEY) { - if (mapAssetTotals[strAssetName] >= nMinimumSumAmount) - setAssetMaxFound.insert(strAssetName); - } - - // Checks the maximum number of UTXO's, and addes to set of of asset that we found the max for - if (nMaximumCount > 0 && mapAssetCoins[strAssetName].size() >= nMaximumCount) { - setAssetMaxFound.insert(strAssetName); - } - } - } - } - - // TODO Remove when done logging - for (auto asset : mapAssetTotals) - LogPrintf("%s : Found a total number of assets available from transfer. Asset: %s, Amount: %d\n", - __func__, asset.first, asset.second); - - for (auto asset : mapAssetCoins) - for (auto out : asset.second) - LogPrintf("%s : %s Output Found: %s\n", __func__, asset.first, out.ToString()); - - } - /** RVN END */ - + bool fFoundEnoughRVN = false; + // A set of the hashes that have already been used + std::set usedMempoolHashes; + + std::map mapAssetTotals; + std::map mapOutPoints; + std::set setAssetMaxFound; + // Turn the OutPoints into a map that is easily interatable. for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const uint256 &wtxid = it->first; const CWalletTx *pcoin = &(*it).second; @@ -2418,8 +2238,6 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::maptx->vout.size(); i++) { - if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount) - continue; if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i))) continue; @@ -2436,26 +2254,224 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::mapfAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); + bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (coinControl && coinControl->fAllowWatchOnly && + (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO; - vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); + std::string address; + CAssetTransfer assetTransfer; + CNewAsset asset; + CReissueAsset reissue; + std::string ownerName; + bool fWasNewAssetOutPoint = false; + bool fWasTransferAssetOutPoint = false; + bool fWasOwnerAssetOutPoint = false; + bool fWasReissueAssetOutPoint = false; + std::string strAssetName; + + int nType; + bool fIsOwner; + if (AreAssetsDeployed() && fWithAssets && pcoin->tx->vout[i].scriptPubKey.IsAssetScript(nType, fIsOwner)) { // If it is an asset + + if ((txnouttype) nType == TX_TRANSFER_ASSET) { + if (TransferAssetFromScript(pcoin->tx->vout[i].scriptPubKey, assetTransfer, address)) { + strAssetName = assetTransfer.strName; + fWasTransferAssetOutPoint = true; + } + } else if ((txnouttype) nType == TX_NEW_ASSET && !fIsOwner) { + if (AssetFromScript(pcoin->tx->vout[i].scriptPubKey, asset, address)) { + strAssetName = asset.strName; + fWasNewAssetOutPoint = true; + } + } else if ((txnouttype) nType == TX_NEW_ASSET && fIsOwner) { + if (OwnerAssetFromScript(pcoin->tx->vout[i].scriptPubKey, ownerName, address)) { + strAssetName = ownerName; + fWasOwnerAssetOutPoint = true; + } + } else if ((txnouttype) nType == TX_REISSUE_ASSET) { + if (ReissueAssetFromScript(pcoin->tx->vout[i].scriptPubKey, reissue, address)) { + strAssetName = reissue.strName; + fWasReissueAssetOutPoint = true; + } + } else { + continue; + } + + if (AreAssetsDeployed() && fWithAssets && (fWasNewAssetOutPoint || fWasTransferAssetOutPoint || fWasOwnerAssetOutPoint || fWasReissueAssetOutPoint)) { + + // If we already have the maximum amount or size for this asset, skip it + if (setAssetMaxFound.count(strAssetName)) + continue; + + // Initialize the map vector is it doesn't exist yet + if (!mapAssetCoins.count(strAssetName)) { + std::vector vOutput; + mapAssetCoins.insert(std::make_pair(strAssetName, vOutput)); + } + + // Add the COutput to the map of available Asset Coins + mapAssetCoins.at(strAssetName).push_back( + COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); - // Checks the sum amount of all UTXO's. - if (nMinimumSumAmount != MAX_MONEY) { - nTotal += pcoin->tx->vout[i].nValue; + // Initialize the map of current asset totals + if (!mapAssetTotals.count(strAssetName)) + mapAssetTotals[strAssetName] = 0; + + // Update the map of totals depending the which type of asset tx we are looking at + if (fWasNewAssetOutPoint) + mapAssetTotals[strAssetName] += asset.nAmount; + else if (fWasTransferAssetOutPoint) + mapAssetTotals[strAssetName] += assetTransfer.nAmount; + else if (fWasReissueAssetOutPoint) + mapAssetTotals[strAssetName] += reissue.nAmount; + else if (fWasOwnerAssetOutPoint) + mapAssetTotals[strAssetName] = OWNER_ASSET_AMOUNT; + + // Checks the sum amount of all UTXO's, and adds to the set of assets that we found the max for + if (nMinimumSumAmount != MAX_MONEY) { + if (mapAssetTotals[strAssetName] >= nMinimumSumAmount) + setAssetMaxFound.insert(strAssetName); + } - if (nTotal >= nMinimumSumAmount) { - return; + // Checks the maximum number of UTXO's, and addes to set of of asset that we found the max for + if (nMaximumCount > 0 && mapAssetCoins[strAssetName].size() >= nMaximumCount) { + setAssetMaxFound.insert(strAssetName); + } + } + + // TODO Remove when done logging/ Uncomment if you need to print out the available coins found +// for (auto asset : mapAssetTotals) +// LogPrintf("%s : Found a total number of assets available from transfer. Asset: %s, Amount: %d\n", +// __func__, asset.first, asset.second); +// +// for (auto asset : mapAssetCoins) +// for (auto out : asset.second) +// LogPrintf("%s : %s Output Found: %s\n", __func__, asset.first, out.ToString()); + + } else { // If it is RVN + if (fFoundEnoughRVN) // We hit our limit + continue; + + vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); + + // Checks the sum amount of all UTXO's. + if (nMinimumSumAmount != MAX_MONEY) { + nTotal += pcoin->tx->vout[i].nValue; + + if (nTotal >= nMinimumSumAmount) { + fFoundEnoughRVN = true; + } } - } - // Checks the maximum number of UTXO's. - if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { - return; + // Checks the maximum number of UTXO's. + if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { + fFoundEnoughRVN = true; + } + continue; } } } + /** RVN END */ + +// for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { +// const uint256 &wtxid = it->first; +// const CWalletTx *pcoin = &(*it).second; +// +// if (!CheckFinalTx(*pcoin)) +// continue; +// +// if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) +// continue; +// +// int nDepth = pcoin->GetDepthInMainChain(); +// if (nDepth < 0) +// continue; +// +// // We should not consider coins which aren't at least in our mempool +// // It's possible for these to be conflicted via ancestors which we may never be able to detect +// if (nDepth == 0 && !pcoin->InMempool()) +// continue; +// +// bool safeTx = pcoin->IsTrusted(); +// +// // We should not consider coins from transactions that are replacing +// // other transactions. +// // +// // Example: There is a transaction A which is replaced by bumpfee +// // transaction B. In this case, we want to prevent creation of +// // a transaction B' which spends an output of B. +// // +// // Reason: If transaction A were initially confirmed, transactions B +// // and B' would no longer be valid, so the user would have to create +// // a new transaction C to replace B'. However, in the case of a +// // one-block reorg, transactions B' and C might BOTH be accepted, +// // when the user only wanted one of them. Specifically, there could +// // be a 1-block reorg away from the chain where transactions A and C +// // were accepted to another chain where B, B', and C were all +// // accepted. +// if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) { +// safeTx = false; +// } +// +// // Similarly, we should not consider coins from transactions that +// // have been replaced. In the example above, we would want to prevent +// // creation of a transaction A' spending an output of A, because if +// // transaction B were initially confirmed, conflicting with A and +// // A', we wouldn't want to the user to create a transaction D +// // intending to replace A', but potentially resulting in a scenario +// // where A, A', and D could all be accepted (instead of just B and +// // D, or just A and A' like the user would want). +// if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { +// safeTx = false; +// } +// +// if (fOnlySafe && !safeTx) { +// continue; +// } +// +// if (nDepth < nMinDepth || nDepth > nMaxDepth) +// continue; +// +// for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { +// if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount) +// continue; +// +// if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i))) +// continue; +// +// if (IsLockedCoin((*it).first, i)) +// continue; +// +// if (IsSpent(wtxid, i)) +// continue; +// +// isminetype mine = IsMine(pcoin->tx->vout[i]); +// +// if (mine == ISMINE_NO) { +// continue; +// } +// +// bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); +// bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO; +// +// vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); +// +// // Checks the sum amount of all UTXO's. +// if (nMinimumSumAmount != MAX_MONEY) { +// nTotal += pcoin->tx->vout[i].nValue; +// +// if (nTotal >= nMinimumSumAmount) { +// return; +// } +// } +// +// // Checks the maximum number of UTXO's. +// if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { +// return; +// } +// } +// } } } @@ -3138,18 +3154,12 @@ bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWall return false; /** RVN START */ - if (fTransferAsset && setAssetOutPoints.size() == 0) - return error("%s : Tried transfering an asset and didn't have any asset outpoints selected", __func__); - if (fNewAsset && (asset.IsNull() || !IsValidDestination(destination))) return error("%s : Tried creating a new asset transaction and the asset was null or the destination was invalid", __func__); if ((fNewAsset && fTransferAsset) || (fReissueAsset && fTransferAsset) || (fReissueAsset && fNewAsset)) return error("%s : Only one type of asset transaction allowed per transaction"); - if (fReissueAsset && setAssetOutPoints.size() == 0) - return error("%s : Tried reissuing an asset and didn't have the owner asset outpoint selected", __func__); - if (fReissueAsset && (reissueAsset.IsNull() || !IsValidDestination(destination))) return error("%s : Tried reissuing an asset and the reissue data was null or the destination was invalid", __func__); /** RVN END */ @@ -3668,6 +3678,8 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) { LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", state.GetRejectReason()); // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure. + AbandonTransaction(wtxNew.tx->GetHash()); + return false; } else { wtxNew.RelayWalletTransaction(connman); } From 389f9977371ea9f555c270b638f3f382d79ba70f Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Fri, 17 Aug 2018 18:18:16 -0600 Subject: [PATCH 03/12] Get chaining working invalidation of blocks --- src/assets/assets.cpp | 94 ++++++++++++++++++++++++++++++------ src/assets/assets.h | 3 -- src/consensus/tx_verify.cpp | 4 ++ src/primitives/transaction.h | 4 ++ src/validation.cpp | 6 +++ src/wallet/wallet.cpp | 4 +- 6 files changed, 93 insertions(+), 22 deletions(-) diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index 10aff5fe64..0f0863d67b 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -26,6 +26,7 @@ #include "protocol.h" #include "wallet/coincontrol.h" #include "utilmoneystr.h" +#include "coins.h" // excluding owner tag ('!') static const auto MAX_NAME_LENGTH = 30; @@ -53,8 +54,6 @@ static const std::regex VOTE_INDICATOR(R"(^[^^~#!]+\^[^~#!\/]+$)"); static const std::regex RAVEN_NAMES("^RVN$|^RAVEN$|^RAVENCOIN$|^RAVENC0IN$|^RAVENCO1N$|^RAVENC01N$"); -std::map mapMempoolAssetOutpoints; - bool IsRootNameValid(const std::string& name) { return std::regex_match(name, ROOT_NAME_CHARACTERS) @@ -511,6 +510,16 @@ bool ReissueAssetFromScript(const CScript& scriptPubKey, CReissueAsset& reissue, } bool CTransaction::IsNewAsset() const +{ + // Check for the assets data CTxOut. This will always be the last output in the transaction + if (!CheckIssueDataTx(vout[vout.size() - 1])) + return false; + + return true; +} + +//! To be called on CTransactions where IsNewAsset returns true +bool CTransaction::VerifyNewAsset() const { // Issuing an Asset must contain at least 3 CTxOut( Raven Burn Tx, Any Number of other Outputs ..., Owner Asset Change Tx, Reissue Tx) if (vout.size() < 3) @@ -533,6 +542,14 @@ bool CTransaction::IsNewAsset() const AssetType assetType; IsAssetNameValid(asset.strName, assetType); + bool fFoundOwnerAsset; + std::string strOwnerName; + if (!OwnerAssetFromScript(vout[vout.size() - 2].scriptPubKey, strOwnerName, address)) + return false; + + if (strOwnerName != asset.strName + OWNER_TAG) + return false; + // Check for the Burn CTxOut in one of the vouts ( This is needed because the change CTxOut is places in a random position in the CWalletTx for (auto out : vout) if (CheckIssueBurnTx(out, assetType)) @@ -542,6 +559,16 @@ bool CTransaction::IsNewAsset() const } bool CTransaction::IsReissueAsset() const +{ + // Check for the reissue asset data CTxOut. This will always be the last output in the transaction + if (!CheckReissueDataTx(vout[vout.size() - 1])) + return false; + + return true; +} + +//! To be called on CTransactions where IsReissueAsset returns true +bool CTransaction::VerifyReissueAsset(CCoinsViewCache& view) const { // Reissuing an Asset must contain at least 3 CTxOut ( Raven Burn Tx, Any Number of other Outputs ..., Reissue Asset Tx, Owner Asset Change Tx) if (vout.size() < 3) @@ -552,14 +579,45 @@ bool CTransaction::IsReissueAsset() const return false; // Check that there is an asset transfer, this will be the owner asset change - bool ownerFound = false; - for (auto out : vout) + bool fOwnerOutFound = false; + for (auto out : vout) { if (CheckTransferOwnerTx(out)) { - ownerFound = true; + fOwnerOutFound = true; break; } + } + + if (!fOwnerOutFound) + return false; - if (!ownerFound) + CReissueAsset reissue; + std::string address; + if (!ReissueAssetFromScript(vout[vout.size() - 1].scriptPubKey, reissue, address)) + return false; + + bool fFoundCorrectInput = false; + for (unsigned int i = 0; i < vin.size(); ++i) { + const COutPoint &prevout = vin[i].prevout; + const Coin& coin = view.AccessCoin(prevout); + assert(!coin.IsSpent()); + + int nType = -1; + bool fOwner = false; + if (coin.out.scriptPubKey.IsAssetScript(nType, fOwner)) { + std::string strAssetName; + CAmount nAssetAmount; + if (!GetAssetFromCoin(coin, strAssetName, nAssetAmount)) + continue; + if (IsAssetNameAnOwner(strAssetName)) { + if (strAssetName == reissue.strName + OWNER_TAG) { + fFoundCorrectInput = true; + break; + } + } + } + } + + if (!fFoundCorrectInput) return false; // Check for the Burn CTxOut in one of the vouts ( This is needed because the change CTxOut is placed in a random position in the CWalletTx @@ -778,10 +836,14 @@ bool CAssetsCache::TrySpendCoin(const COutPoint& out, const CTxOut& txOut) if (address != "" && assetName != "" && nAmount > 0) { CAssetCacheSpendAsset spend(assetName, address, nAmount); if (GetBestAssetAddressAmount(*this, assetName, address)) { - assert(mapAssetsAddressAmount[make_pair(assetName, address)] >= nAmount); - mapAssetsAddressAmount[make_pair(assetName, address)] -= nAmount; - - if (mapAssetsAddressAmount[make_pair(assetName, address)] == 0 && + std::cout << assetName << " : " << address << " : " << nAmount << " : " << mapAssetsAddressAmount[make_pair(assetName, address)] << std::endl; + auto pair = make_pair(assetName, address); +// assert(mapAssetsAddressAmount[pair] >= nAmount); + mapAssetsAddressAmount.at(pair) -= nAmount; + + if (mapAssetsAddressAmount.at(pair) < 0) + mapAssetsAddressAmount.at(pair) = 0; + if (mapAssetsAddressAmount.at(pair) == 0 && mapAssetsAddresses.count(assetName)) mapAssetsAddresses.at(assetName).erase(address); @@ -2408,12 +2470,12 @@ bool SendAssetTransaction(CWallet* pwallet, CWalletTx& transaction, CReserveKey& bool VerifyAssetOwner(const std::string& asset_name, std::set& myOwnerOutPoints, std::pair& error) { - // Check to make sure this wallet is the owner of the asset - if(!CheckAssetOwner(asset_name)) { - error = std::make_pair(RPC_INVALID_PARAMS, - std::string("This wallet is not the owner of the asset: ") + asset_name); - return false; - } +// // Check to make sure this wallet is the owner of the asset +// if(!CheckAssetOwner(asset_name)) { +// error = std::make_pair(RPC_INVALID_PARAMS, +// std::string("This wallet is not the owner of the asset: ") + asset_name); +// return false; +// } // // // Get the outpoint that belongs to the Owner Asset // if (!passets->GetAssetsOutPoints(asset_name + OWNER_TAG, myOwnerOutPoints)) { diff --git a/src/assets/assets.h b/src/assets/assets.h index b2e4b7a735..a67ea46839 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -49,9 +49,6 @@ struct CAssetOutputEntry; // 50000 * 82 Bytes == 4.1 Mb #define MAX_CACHE_ASSETS_SIZE 50000 -extern std::map mapMempoolAssetOutpoints; - - class CAssets { public: std::map > mapMyUnspentAssets; // Asset Name -> COutPoint diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 8ec603b5fa..a2ecbc74aa 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -237,6 +237,9 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, CAssetsCa if (assetCache) { // Get the new asset from the transaction if (tx.IsNewAsset()) { + if(!tx.VerifyNewAsset()) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-verifying-issue-asset"); + CNewAsset asset; std::string strAddress; if (!AssetFromTransaction(tx, asset, strAddress)) @@ -251,6 +254,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, CAssetsCa return state.DoS(100, false, REJECT_INVALID, "bad-txns-" + strError); } else if (tx.IsReissueAsset()) { + CReissueAsset reissue; std::string strAddress; if (!ReissueAssetFromTransaction(tx, reissue, strAddress)) diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index a6b194b5f9..c3cf095ee7 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -15,6 +15,8 @@ static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000; +class CCoinsViewCache; + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -326,7 +328,9 @@ class CTransaction /** RVN START */ bool IsNewAsset() const; + bool VerifyNewAsset() const; bool IsReissueAsset() const; + bool VerifyReissueAsset(CCoinsViewCache& view) const; /** RVN END */ /** diff --git a/src/validation.cpp b/src/validation.cpp index a103d7b8a6..52310e2bc4 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2191,6 +2191,9 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!AreAssetsDeployed()) return state.DoS(100, false, REJECT_INVALID, "bad-txns-new-asset-when-assets-is-not-active"); + if (!tx.VerifyNewAsset()) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-issue-asset-failed-verify"); + CNewAsset asset; std::string strAddress; if (!AssetFromTransaction(tx, asset, strAddress)) @@ -2206,6 +2209,9 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } else if (tx.IsReissueAsset()) { if (!AreAssetsDeployed()) return state.DoS(100, false, REJECT_INVALID, "bad-txns-reissue-asset-when-assets-is-not-active"); + + if (!tx.VerifyReissueAsset(view)) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-reissue-asset-failed-verify"); CReissueAsset reissue; std::string strAddress; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 86220661b9..25367d0664 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1700,8 +1700,8 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman) assert(pwallet->GetBroadcastTransactions()); if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0) { - CValidationState state; /* GetDepthInMainChain already catches known conflicts. */ + CValidationState state; if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) { LogPrintf("Relaying wtx %s\n", GetHash().ToString()); if (connman) { @@ -3664,8 +3664,6 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CCon CWalletTx &coin = mapWallet[txin.prevout.hash]; coin.BindWallet(this); NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); - - mapMempoolAssetOutpoints[txin.prevout] = wtxNew.tx->GetHash(); } } From 765d83c64814df4c8287134dccbd962bf4cd950d Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 11:01:46 -0600 Subject: [PATCH 04/12] remove bad asset --- src/assets/assets.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index 0f0863d67b..5c5488699d 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -836,9 +836,7 @@ bool CAssetsCache::TrySpendCoin(const COutPoint& out, const CTxOut& txOut) if (address != "" && assetName != "" && nAmount > 0) { CAssetCacheSpendAsset spend(assetName, address, nAmount); if (GetBestAssetAddressAmount(*this, assetName, address)) { - std::cout << assetName << " : " << address << " : " << nAmount << " : " << mapAssetsAddressAmount[make_pair(assetName, address)] << std::endl; auto pair = make_pair(assetName, address); -// assert(mapAssetsAddressAmount[pair] >= nAmount); mapAssetsAddressAmount.at(pair) -= nAmount; if (mapAssetsAddressAmount.at(pair) < 0) From f632328859e6158513c566389d91f7c78e7cd0cb Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 11:32:59 -0600 Subject: [PATCH 05/12] Remove duplicate code, remove asset txs from AvailableCoins call --- src/assets/assets.cpp | 1 - src/wallet/wallet.cpp | 135 +++++++----------------------------------- src/wallet/wallet.h | 8 ++- 3 files changed, 29 insertions(+), 115 deletions(-) diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index 5c5488699d..bea87fd366 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -542,7 +542,6 @@ bool CTransaction::VerifyNewAsset() const AssetType assetType; IsAssetNameValid(asset.strName, assetType); - bool fFoundOwnerAsset; std::string strOwnerName; if (!OwnerAssetFromScript(vout[vout.size() - 2].scriptPubKey, strOwnerName, address)) return false; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 25367d0664..57919e612a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2150,18 +2150,22 @@ void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlySafe, const AvailableCoinsAll(vCoins, mapAssetCoins, setAssetOutPoint, false, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); } -void CWallet::AvailableCoinsWithAssets(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fWithAssets, bool fOnlySafe, const CCoinControl *coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t& nMaximumCount, const int& nMinDepth, const int& nMaxDepth) const +void CWallet::AvailableAssets(std::vector &vCoins, std::map > &mapAssetCoins, + const std::set &setAssetOutPoint, bool fOnlyAssets, bool fOnlySafe, + const CCoinControl *coinControl, const CAmount &nMinimumAmount, + const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, + const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const { if (!AreAssetsDeployed()) return; - if (!fWithAssets) + if (!fOnlyAssets) return; - AvailableCoinsAll(vCoins, mapAssetCoins, setAssetOutPoint, fWithAssets, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); + AvailableCoinsAll(vCoins, mapAssetCoins, setAssetOutPoint, fOnlyAssets, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); } -void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fWithAssets, bool fOnlySafe, const CCoinControl *coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t& nMaximumCount, const int& nMinDepth, const int& nMaxDepth) const { +void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fOnlyAssets, bool fOnlySafe, const CCoinControl *coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t& nMaximumCount, const int& nMinDepth, const int& nMaxDepth) const { vCoins.clear(); { @@ -2170,7 +2174,7 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map usedMempoolHashes; @@ -2272,7 +2276,9 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::maptx->vout[i].scriptPubKey.IsAssetScript(nType, fIsOwner)) { // If it is an asset + + // Looking for Asset Tx OutPoints Only + if (AreAssetsDeployed() && fOnlyAssets && pcoin->tx->vout[i].scriptPubKey.IsAssetScript(nType, fIsOwner)) { if ((txnouttype) nType == TX_TRANSFER_ASSET) { if (TransferAssetFromScript(pcoin->tx->vout[i].scriptPubKey, assetTransfer, address)) { @@ -2298,7 +2304,7 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map& vCoins, std::map& vCoins, std::maptx->vout[i].scriptPubKey.IsAssetScript()) continue; vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); @@ -2360,118 +2370,19 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::maptx->vout[i].nValue; if (nTotal >= nMinimumSumAmount) { - fFoundEnoughRVN = true; + fRVNLimitHit = true; } } // Checks the maximum number of UTXO's. if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { - fFoundEnoughRVN = true; + fRVNLimitHit = true; } continue; } } } /** RVN END */ - -// for (std::map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { -// const uint256 &wtxid = it->first; -// const CWalletTx *pcoin = &(*it).second; -// -// if (!CheckFinalTx(*pcoin)) -// continue; -// -// if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) -// continue; -// -// int nDepth = pcoin->GetDepthInMainChain(); -// if (nDepth < 0) -// continue; -// -// // We should not consider coins which aren't at least in our mempool -// // It's possible for these to be conflicted via ancestors which we may never be able to detect -// if (nDepth == 0 && !pcoin->InMempool()) -// continue; -// -// bool safeTx = pcoin->IsTrusted(); -// -// // We should not consider coins from transactions that are replacing -// // other transactions. -// // -// // Example: There is a transaction A which is replaced by bumpfee -// // transaction B. In this case, we want to prevent creation of -// // a transaction B' which spends an output of B. -// // -// // Reason: If transaction A were initially confirmed, transactions B -// // and B' would no longer be valid, so the user would have to create -// // a new transaction C to replace B'. However, in the case of a -// // one-block reorg, transactions B' and C might BOTH be accepted, -// // when the user only wanted one of them. Specifically, there could -// // be a 1-block reorg away from the chain where transactions A and C -// // were accepted to another chain where B, B', and C were all -// // accepted. -// if (nDepth == 0 && pcoin->mapValue.count("replaces_txid")) { -// safeTx = false; -// } -// -// // Similarly, we should not consider coins from transactions that -// // have been replaced. In the example above, we would want to prevent -// // creation of a transaction A' spending an output of A, because if -// // transaction B were initially confirmed, conflicting with A and -// // A', we wouldn't want to the user to create a transaction D -// // intending to replace A', but potentially resulting in a scenario -// // where A, A', and D could all be accepted (instead of just B and -// // D, or just A and A' like the user would want). -// if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) { -// safeTx = false; -// } -// -// if (fOnlySafe && !safeTx) { -// continue; -// } -// -// if (nDepth < nMinDepth || nDepth > nMaxDepth) -// continue; -// -// for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) { -// if (pcoin->tx->vout[i].nValue < nMinimumAmount || pcoin->tx->vout[i].nValue > nMaximumAmount) -// continue; -// -// if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs && !coinControl->IsSelected(COutPoint((*it).first, i))) -// continue; -// -// if (IsLockedCoin((*it).first, i)) -// continue; -// -// if (IsSpent(wtxid, i)) -// continue; -// -// isminetype mine = IsMine(pcoin->tx->vout[i]); -// -// if (mine == ISMINE_NO) { -// continue; -// } -// -// bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); -// bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO; -// -// vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); -// -// // Checks the sum amount of all UTXO's. -// if (nMinimumSumAmount != MAX_MONEY) { -// nTotal += pcoin->tx->vout[i].nValue; -// -// if (nTotal >= nMinimumSumAmount) { -// return; -// } -// } -// -// // Checks the maximum number of UTXO's. -// if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) { -// return; -// } -// } -// } } } @@ -3252,7 +3163,7 @@ bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWall std::vector vAvailableCoins; std::map > mapAssetCoins; if (fTransferAsset || fReissueAsset || assetType == AssetType::SUB) - AvailableCoinsWithAssets(vAvailableCoins, mapAssetCoins, setAssetOutPoints, true, true, &coin_control); + AvailableAssets(vAvailableCoins, mapAssetCoins, setAssetOutPoints, true, true, &coin_control); else AvailableCoins(vAvailableCoins, true, &coin_control); /** RVN END */ diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 14259cc7ab..d6bf89726c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -836,12 +836,16 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface /** * populate vCoins with vector of available COutputs, and populates vAssetCoins in fWithAssets is set to true. */ - void AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fWithAssets = false, 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 = 9999999) const; + void AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fOnlyAssets = false, 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 = 9999999) const; /** * Helper function that calls AvailableCoinsAll, used for transfering assets */ - void AvailableCoinsWithAssets(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fWithAssets = false, 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 = 9999999) const; + void AvailableAssets(std::vector &vCoins, std::map > &mapAssetCoins, + const std::set &setAssetOutPoint, bool fOnlyAssets = false, 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 = 9999999) const; /** * populate vCoins with vector of available COutputs. From 8dc1a70af6bf78734d6e25cfa6d0afddbbc5b3e4 Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 11:40:36 -0600 Subject: [PATCH 06/12] Asset tx have zero RVN --- src/consensus/tx_verify.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index a2ecbc74aa..61f2ad7200 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -186,14 +186,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, CAssetsCa return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); /** RVN START */ - if (!AreAssetsDeployed() && txout.scriptPubKey.IsAssetScript() && !fReindex) + bool isAsset = false; + int nType; + bool fIsOwner; + if (txout.scriptPubKey.IsAssetScript(nType, fIsOwner)) + isAsset = true; + + // Make sure that all asset tx have a nValue of zero RVN + if (isAsset && txout.nValue != 0) + return state.DoS(100, false, REJECT_INVALID, "bad-txns-asset-tx-amount-isn't-zero"); + + if (!AreAssetsDeployed() && isAsset && !fReindex) return state.DoS(100, false, REJECT_INVALID, "bad-txns-is-asset-and-asset-not-active"); // Check for transfers that don't meet the assets units only if the assetCache is not null if (AreAssetsDeployed()) { if (assetCache) { // Get the transfer transaction data from the scriptPubKey - if (txout.scriptPubKey.IsTransferAsset()) { + if ((txnouttype) nType == TX_TRANSFER_ASSET) { CAssetTransfer transfer; std::string address; if (!TransferAssetFromScript(txout.scriptPubKey, transfer, address)) From d86e62811c9801cd038c918011d9936ede418bb0 Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 11:43:32 -0600 Subject: [PATCH 07/12] Add additional asset tx check before evaluation tx --- src/consensus/tx_verify.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 61f2ad7200..2ea3b0bcec 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -200,7 +200,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, CAssetsCa return state.DoS(100, false, REJECT_INVALID, "bad-txns-is-asset-and-asset-not-active"); // Check for transfers that don't meet the assets units only if the assetCache is not null - if (AreAssetsDeployed()) { + if (AreAssetsDeployed() && isAsset) { if (assetCache) { // Get the transfer transaction data from the scriptPubKey if ((txnouttype) nType == TX_TRANSFER_ASSET) { From c94145aca9b824da3bd66a6fc5c86d5ef0ee2f78 Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 14:45:44 -0600 Subject: [PATCH 08/12] Remove unused code that chaining bypasses now, fix asset tx transfers submitting tx twice --- src/assets/assets.cpp | 105 +++++++++++---------------- src/assets/assets.h | 7 +- src/consensus/tx_verify.cpp | 2 +- src/qt/assetsdialog.cpp | 138 ------------------------------------ src/wallet/wallet.cpp | 51 +++++++------ src/wallet/wallet.h | 30 +++++--- 6 files changed, 95 insertions(+), 238 deletions(-) diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index bea87fd366..e5cee5918a 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -605,7 +605,7 @@ bool CTransaction::VerifyReissueAsset(CCoinsViewCache& view) const if (coin.out.scriptPubKey.IsAssetScript(nType, fOwner)) { std::string strAssetName; CAmount nAssetAmount; - if (!GetAssetFromCoin(coin, strAssetName, nAssetAmount)) + if (!GetAssetInfoFromCoin(coin, strAssetName, nAssetAmount)) continue; if (IsAssetNameAnOwner(strAssetName)) { if (strAssetName == reissue.strName + OWNER_TAG) { @@ -1846,11 +1846,11 @@ bool CAssetsCache::GetAssetIfExists(const std::string& name, CNewAsset& asset) return false; } -bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) -{ +bool GetAssetInfoFromScript(const CScript& scriptPubKey, std::string& strName, CAmount& nAmount) { + int nType = 0; bool fIsOwner = false; - if (!coin.out.scriptPubKey.IsAssetScript(nType, fIsOwner)) { + if (!scriptPubKey.IsAssetScript(nType, fIsOwner)) { return false; } @@ -1860,7 +1860,7 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) if (type == TX_NEW_ASSET && !fIsOwner) { CNewAsset asset; std::string address; - if (!AssetFromScript(coin.out.scriptPubKey, asset, address)) + if (!AssetFromScript(scriptPubKey, asset, address)) return false; strName = asset.strName; nAmount = asset.nAmount; @@ -1868,7 +1868,7 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) } else if (type == TX_TRANSFER_ASSET) { CAssetTransfer asset; std::string address; - if (!TransferAssetFromScript(coin.out.scriptPubKey, asset, address)) + if (!TransferAssetFromScript(scriptPubKey, asset, address)) return false; strName = asset.strName; nAmount = asset.nAmount; @@ -1876,7 +1876,7 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) } else if (type == TX_NEW_ASSET && fIsOwner) { std::string name; std::string address; - if (!OwnerAssetFromScript(coin.out.scriptPubKey, name, address)) + if (!OwnerAssetFromScript(scriptPubKey, name, address)) return false; strName = name; nAmount = OWNER_ASSET_AMOUNT; @@ -1884,7 +1884,7 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) } else if (type == TX_REISSUE_ASSET) { CReissueAsset reissue; std::string address; - if (!ReissueAssetFromScript(coin.out.scriptPubKey, reissue, address)) + if (!ReissueAssetFromScript(scriptPubKey, reissue, address)) return false; strName = reissue.strName; nAmount = reissue.nAmount; @@ -1894,6 +1894,11 @@ bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) return false; } +bool GetAssetInfoFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount) +{ + return GetAssetInfoFromScript(coin.out.scriptPubKey, strName, nAmount); +} + void GetAssetData(const CScript& script, CAssetOutputEntry& data) { // Placeholder strings that will get set if you successfully get the transfer or asset from the script @@ -1947,15 +1952,6 @@ void GetAssetData(const CScript& script, CAssetOutputEntry& data) } } -bool CheckAssetOwner(const std::string& assetName) -{ - if (passets->mapMyUnspentAssets.count(assetName + OWNER_TAG)) { - return true; - } - - return false; -} - void GetAllOwnedAssets(std::vector& names) { for (auto owned : passets->mapMyUnspentAssets) { @@ -2230,15 +2226,14 @@ bool CreateAssetTransaction(CWallet* pwallet, const CNewAsset& asset, const std: } // Get the owner outpoints if this is a subasset - std::set myAssetOutPoints; if (assetType == AssetType::SUB) { // Verify that this wallet is the owner for the asset, and get the owner asset outpoint - if (!VerifyAssetOwner(GetParentName(asset.strName), myAssetOutPoints, error)) { + if (!VerifyWalletHasAsset(GetParentName(asset.strName + OWNER_TAG), error)) { return false; } } - if (!pwallet->CreateTransactionWithAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control, asset, DecodeDestination(address), myAssetOutPoints, assetType)) { + if (!pwallet->CreateTransactionWithAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control, asset, DecodeDestination(address), assetType)) { if (!fSubtractFeeFromAmount && burnAmount + nFeeRequired > curBalance) strTxError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); error = std::make_pair(RPC_WALLET_ERROR, strTxError); @@ -2316,8 +2311,7 @@ bool CreateReissueAssetTransaction(CWallet* pwallet, const CReissueAsset& reissu } // Verify that this wallet is the owner for the asset, and get the owner asset outpoint - std::set myAssetOutPoints; - if (!VerifyAssetOwner(asset_name, myAssetOutPoints, error)) { + if (!VerifyWalletHasAsset(asset_name, error)) { return false; } @@ -2359,7 +2353,7 @@ bool CreateReissueAssetTransaction(CWallet* pwallet, const CReissueAsset& reissu CRecipient recipient2 = {scriptTransferOwnerAsset, 0, fSubtractFeeFromAmount}; vecSend.push_back(recipient); vecSend.push_back(recipient2); - if (!pwallet->CreateTransactionWithReissueAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control, reissueAsset, DecodeDestination(address), myAssetOutPoints)) { + if (!pwallet->CreateTransactionWithReissueAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control, reissueAsset, DecodeDestination(address))) { if (!fSubtractFeeFromAmount && burnAmount + nFeeRequired > curBalance) strTxError = strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired)); error = std::make_pair(RPC_WALLET_ERROR, strTxError); @@ -2389,7 +2383,6 @@ bool CreateTransferAssetTransaction(CWallet* pwallet, const std::vector< std::pa return false; } - std::set myAssetOutPoints; // Loop through all transfers and create scriptpubkeys for them for (auto transfer : vTransfers) { std::string address = transfer.second; @@ -2406,27 +2399,17 @@ bool CreateTransferAssetTransaction(CWallet* pwallet, const std::vector< std::pa return false; } - std::set myTempAssetOutPoints; - if (!passets->GetAssetsOutPoints(asset_name, myTempAssetOutPoints)) { - error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet doesn't own any assets with the name: ") + asset_name); - return false; - } - - if (myTempAssetOutPoints.size() == 0) { - error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet doesn't own any assets with the name: ") + asset_name); + if (!VerifyWalletHasAsset(asset_name, error)) // Sets error if it fails return false; - } - - // Put the outpoints into our master set - myAssetOutPoints.insert(myTempAssetOutPoints.begin(), myTempAssetOutPoints.end()); // If it is an ownership transfer, make a quick check to make sure the amount is 1 - if (IsAssetNameAnOwner(asset_name)) - if (nAmount != COIN * 1) { + if (IsAssetNameAnOwner(asset_name)) { + if (nAmount != OWNER_ASSET_AMOUNT) { error = std::make_pair(RPC_INVALID_PARAMS, std::string( "When transfer an 'Ownership Asset' the amount must always be 1. Please try again with the amount of 1")); return false; } + } // Get the script for the burn address CScript scriptPubKey = GetScriptForDestination(DecodeDestination(address)); @@ -2442,7 +2425,7 @@ bool CreateTransferAssetTransaction(CWallet* pwallet, const std::vector< std::pa CCoinControl coin_control; // Create and send the transaction - if (!pwallet->CreateTransactionWithTransferAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control, myAssetOutPoints)) { + if (!pwallet->CreateTransactionWithTransferAsset(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strTxError, coin_control)) { if (!fSubtractFeeFromAmount && nFeeRequired > curBalance) { error = std::make_pair(RPC_WALLET_ERROR, strprintf("Error: This transaction requires a transaction fee of at least %s", FormatMoney(nFeeRequired))); return false; @@ -2465,31 +2448,23 @@ bool SendAssetTransaction(CWallet* pwallet, CWalletTx& transaction, CReserveKey& return true; } -bool VerifyAssetOwner(const std::string& asset_name, std::set& myOwnerOutPoints, std::pair& error) -{ -// // Check to make sure this wallet is the owner of the asset -// if(!CheckAssetOwner(asset_name)) { -// error = std::make_pair(RPC_INVALID_PARAMS, -// std::string("This wallet is not the owner of the asset: ") + asset_name); -// return false; -// } -// -// // Get the outpoint that belongs to the Owner Asset -// if (!passets->GetAssetsOutPoints(asset_name + OWNER_TAG, myOwnerOutPoints)) { -// error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet can't find the owner token information for: ") + asset_name); -// return false; -// } -// -// // Check to make sure we have the right amount of outpoints -// if (myOwnerOutPoints.size() == 0) { -// error = std::make_pair(RPC_INVALID_PARAMS, std::string("This wallet doesn't own any assets with the name: ") + asset_name + OWNER_TAG); -// return false; -// } -// -// if (myOwnerOutPoints.size() != 1) { -// error = std::make_pair(RPC_INVALID_PARAMS, "Found multiple Owner Assets. Database is out of sync. You might have to run the wallet with -reindex"); -// return false; -// } +bool VerifyWalletHasAsset(const std::string& asset_name, std::pair& pairError) +{ + CWallet* pwallet; + if (vpwallets.size() > 0) + pwallet = vpwallets[0]; + else { + pairError = std::make_pair(RPC_WALLET_ERROR, strprintf("Wallet not found. Can't verify if it contains: %s", asset_name)); + return false; + } - return true; + std::vector vCoins; + std::map > mapAssetCoins; + pwallet->AvailableAssets(mapAssetCoins); + + if (mapAssetCoins.count(asset_name)) + return true; + + pairError = std::make_pair(RPC_INVALID_REQUEST, strprintf("Wallet doesn't have asset: %s", asset_name)); + return false; } \ No newline at end of file diff --git a/src/assets/assets.h b/src/assets/assets.h index a67ea46839..3e3041d253 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -338,13 +338,13 @@ bool IsScriptTransferAsset(const CScript& scriptPubKey); bool IsNewOwnerTxValid(const CTransaction& tx, const std::string& assetName, const std::string& address, std::string& errorMsg); -bool CheckAssetOwner(const std::string& assetName); void GetAllOwnedAssets(std::vector& names); void GetAllMyAssets(std::vector& names); void UpdatePossibleAssets(); -bool GetAssetFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount); +bool GetAssetInfoFromCoin(const Coin& coin, std::string& strName, CAmount& nAmount); +bool GetAssetInfoFromScript(const CScript& scriptPubKey, std::string& strName, CAmount& nAmount); void GetAssetData(const CScript& script, CAssetOutputEntry& data); @@ -356,7 +356,8 @@ bool GetMyAssetBalance(CAssetsCache& cache, const std::string& assetName, CAmoun bool GetMyAssetBalances(CAssetsCache& cache, const std::vector& assetNames, std::map& balances); bool GetMyAssetBalances(CAssetsCache& cache, std::map& balances); -bool VerifyAssetOwner(const std::string& asset_name, std::set& myOwnerOutPoints, std::pair& error); +/** Verifies that this wallet owns the give asset */ +bool VerifyWalletHasAsset(const std::string& asset_name, std::pair& pairError); std::string DecodeIPFS(std::string encoded); std::string EncodeIPFS(std::string decoded); diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 2ea3b0bcec..076fab3039 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -357,7 +357,7 @@ bool Consensus::CheckTxAssets(const CTransaction& tx, CValidationState& state, c std::string strName; CAmount nAmount; - if (!GetAssetFromCoin(coin, strName, nAmount)) + if (!GetAssetInfoFromCoin(coin, strName, nAmount)) return state.DoS(100, false, REJECT_INVALID, "bad-txns-failed-to-get-asset-from-script"); // Add to the total value of assets in the inputs diff --git a/src/qt/assetsdialog.cpp b/src/qt/assetsdialog.cpp index f881b094cd..056d498dd3 100644 --- a/src/qt/assetsdialog.cpp +++ b/src/qt/assetsdialog.cpp @@ -383,144 +383,6 @@ void AssetsDialog::on_sendButton_clicked() coinControlUpdateLabels(); } fNewRecipientAllowed = true; - - - - std::string txid; - if (!SendAssetTransaction(model->getWallet(), tx, reservekey, error, txid)) { - QMessageBox msgBox; - msgBox.setText(QString::fromStdString(error.second)); - msgBox.exec(); - return; - } else { - QMessageBox msgBox; - msgBox.setText("Transaction sent to network"); - msgBox.setInformativeText("Transaction Hash: " + QString::fromStdString(txid)); - msgBox.exec(); - accept(); - } - fNewRecipientAllowed = true; - -// // prepare transaction for getting txFee earlier -// WalletModelTransaction currentTransaction(recipients); -// WalletModel::SendCoinsReturn prepareStatus; -// -// // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled -// CCoinControl ctrl; -// if (model->getOptionsModel()->getCoinControlFeatures()) -// ctrl = *CoinControlDialog::coinControl; -// -// updateCoinControlState(ctrl); -// -// prepareStatus = model->prepareTransaction(currentTransaction, ctrl); -// -// // process prepareStatus and on error generate message shown to user -// processSendCoinsReturn(prepareStatus, -// RavenUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())); -// -// if(prepareStatus.status != WalletModel::OK) { -// fNewRecipientAllowed = true; -// return; -// } -// -// CAmount txFee = currentTransaction.getTransactionFee(); -// -// // Format confirmation message -// QStringList formatted; -// for (const SendCoinsRecipient &rcp : currentTransaction.getRecipients()) -// { -// // generate bold amount string -// QString amount = "" + RavenUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); -// amount.append(""); -// // generate monospace address string -// QString address = "" + rcp.address; -// address.append(""); -// -// QString recipientElement; -// -// if (!rcp.paymentRequest.IsInitialized()) // normal payment -// { -// if(rcp.label.length() > 0) // label with address -// { -// recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); -// recipientElement.append(QString(" (%1)").arg(address)); -// } -// else // just address -// { -// recipientElement = tr("%1 to %2").arg(amount, address); -// } -// } -// else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request -// { -// recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); -// } -// else // unauthenticated payment request -// { -// recipientElement = tr("%1 to %2").arg(amount, address); -// } -// -// formatted.append(recipientElement); -// } -// -// QString questionString = tr("Are you sure you want to send?"); -// questionString.append("

%1"); -// -// if(txFee > 0) -// { -// // append fee string if a fee is required -// questionString.append("
"); -// questionString.append(RavenUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); -// questionString.append(" "); -// questionString.append(tr("added as transaction fee")); -// -// // append transaction size -// questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)"); -// } -// -// // add total amount in all subdivision units -// questionString.append("
"); -// CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; -// QStringList alternativeUnits; -// for (RavenUnits::Unit u : RavenUnits::availableUnits()) -// { -// if(u != model->getOptionsModel()->getDisplayUnit()) -// alternativeUnits.append(RavenUnits::formatHtmlWithUnit(u, totalAmount)); -// } -// questionString.append(tr("Total Amount %1") -// .arg(RavenUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))); -// questionString.append(QString("
(=%2)
") -// .arg(alternativeUnits.join(" " + tr("or") + "
"))); -// -// if (ui->optInRBF->isChecked()) -// { -// questionString.append("
"); -// questionString.append(tr("This transaction signals replaceability (optin-RBF).")); -// questionString.append(""); -// } -// -// SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), -// questionString.arg(formatted.join("
")), SEND_CONFIRM_DELAY, this); -// confirmationDialog.exec(); -// QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result(); -// -// if(retval != QMessageBox::Yes) -// { -// fNewRecipientAllowed = true; -// return; -// } -// -// // now send the prepared transaction -// WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); -// // process sendStatus and on error generate message shown to user -// processSendCoinsReturn(sendStatus); -// -// if (sendStatus.status == WalletModel::OK) -// { -// accept(); -// CoinControlDialog::coinControl->UnSelectAll(); -// coinControlUpdateLabels(); -// } -// fNewRecipientAllowed = true; } void AssetsDialog::clear() diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 57919e612a..93c65fdde4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2146,12 +2146,10 @@ CAmount CWallet::GetAvailableBalance(const CCoinControl* coinControl) const void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const { std::map > mapAssetCoins; - std::set setAssetOutPoint; - AvailableCoinsAll(vCoins, mapAssetCoins, setAssetOutPoint, false, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); + AvailableCoinsAll(vCoins, mapAssetCoins, true, false, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); } -void CWallet::AvailableAssets(std::vector &vCoins, std::map > &mapAssetCoins, - const std::set &setAssetOutPoint, bool fOnlyAssets, bool fOnlySafe, +void CWallet::AvailableAssets(std::map > &mapAssetCoins, bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const @@ -2159,13 +2157,23 @@ void CWallet::AvailableAssets(std::vector &vCoins, std::map vCoins; + + AvailableCoinsAll(vCoins, mapAssetCoins, false, true, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); +} + +void CWallet::AvailableCoinsWithAssets(std::vector &vCoins, std::map > &mapAssetCoins, + bool fOnlySafe, const CCoinControl *coinControl, const CAmount &nMinimumAmount, + const CAmount &nMaximumAmount, const CAmount &nMinimumSumAmount, + const uint64_t &nMaximumCount, const int &nMinDepth, const int &nMaxDepth) const +{ + if (!AreAssetsDeployed()) return; - AvailableCoinsAll(vCoins, mapAssetCoins, setAssetOutPoint, fOnlyAssets, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); + AvailableCoinsAll(vCoins, mapAssetCoins, true, true, fOnlySafe, coinControl, nMinimumAmount, nMaximumAmount, nMinimumSumAmount, nMaximumCount, nMinDepth, nMaxDepth); } -void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fOnlyAssets, bool fOnlySafe, const CCoinControl *coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t& nMaximumCount, const int& nMinDepth, const int& nMaxDepth) const { +void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, bool fGetRVN, bool fGetAssets, bool fOnlySafe, const CCoinControl *coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t& nMaximumCount, const int& nMinDepth, const int& nMaxDepth) const { vCoins.clear(); { @@ -2278,7 +2286,7 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::maptx->vout[i].scriptPubKey.IsAssetScript(nType, fIsOwner)) { + if (fGetAssets && AreAssetsDeployed() && pcoin->tx->vout[i].scriptPubKey.IsAssetScript(nType, fIsOwner)) { if ((txnouttype) nType == TX_TRANSFER_ASSET) { if (TransferAssetFromScript(pcoin->tx->vout[i].scriptPubKey, assetTransfer, address)) { @@ -2355,7 +2363,9 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination destination, const std::set& setAssetOutPoints, const AssetType& type, bool sign) + std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination destination, const AssetType& type, bool sign) { CReissueAsset reissueAsset; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, true, asset, destination, false, setAssetOutPoints, false, reissueAsset, type, sign); + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, true, asset, destination, false, false, reissueAsset, type, sign); } bool CWallet::CreateTransactionWithTransferAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const std::set& setAssetOutPoints, bool sign) + std::string& strFailReason, const CCoinControl& coin_control, bool sign) { - - if (setAssetOutPoints.size() == 0) - return error("%s : Tried transfering an asset and didn't have any asset outpoints selected", __func__); - CNewAsset asset; CReissueAsset reissueAsset; CTxDestination destination; AssetType assetType = AssetType::INVALID; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, true, setAssetOutPoints, false, reissueAsset, assetType, sign); + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, true, false, reissueAsset, assetType, sign); } bool CWallet::CreateTransactionWithReissueAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const CReissueAsset& reissueAsset, const CTxDestination destination, const std::set& setAssetOutPoints, bool sign) + std::string& strFailReason, const CCoinControl& coin_control, const CReissueAsset& reissueAsset, const CTxDestination destination, bool sign) { CNewAsset asset; AssetType assetType = AssetType::REISSUE; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, setAssetOutPoints, true, reissueAsset, assetType, sign); + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, true, reissueAsset, assetType, sign); } bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, @@ -3051,14 +3057,13 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT CNewAsset asset; CReissueAsset reissueAsset; CTxDestination destination; - std::set setAssetOutPoints; AssetType assetType = AssetType::INVALID; - return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, setAssetOutPoints, false, reissueAsset, assetType, sign); + return CreateTransactionAll(vecSend, wtxNew, reservekey, nFeeRet, nChangePosInOut, strFailReason, coin_control, false, asset, destination, false, false, reissueAsset, assetType, sign); } bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, - int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination destination, bool fTransferAsset, const std::set& setAssetOutPoints, bool fReissueAsset, const CReissueAsset& reissueAsset, const AssetType& assetType, bool sign) + int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination destination, bool fTransferAsset, bool fReissueAsset, const CReissueAsset& reissueAsset, const AssetType& assetType, bool sign) { if (!AreAssetsDeployed() && (fTransferAsset || fNewAsset || fReissueAsset)) @@ -3163,7 +3168,7 @@ bool CWallet::CreateTransactionAll(const std::vector& vecSend, CWall std::vector vAvailableCoins; std::map > mapAssetCoins; if (fTransferAsset || fReissueAsset || assetType == AssetType::SUB) - AvailableAssets(vAvailableCoins, mapAssetCoins, setAssetOutPoints, true, true, &coin_control); + AvailableCoinsWithAssets(vAvailableCoins, mapAssetCoins, true, &coin_control); else AvailableCoins(vAvailableCoins, true, &coin_control); /** RVN END */ diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index d6bf89726c..195c3f5d63 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -836,21 +836,35 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface /** * populate vCoins with vector of available COutputs, and populates vAssetCoins in fWithAssets is set to true. */ - void AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, const std::set& setAssetOutPoint, bool fOnlyAssets = false, 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 = 9999999) const; + void AvailableCoinsAll(std::vector& vCoins, std::map >& mapAssetCoins, + bool fGetRVN = true, bool fOnlyAssets = false, + 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 = 9999999) const; /** * Helper function that calls AvailableCoinsAll, used for transfering assets */ - void AvailableAssets(std::vector &vCoins, std::map > &mapAssetCoins, - const std::set &setAssetOutPoint, bool fOnlyAssets = false, bool fOnlySafe = true, + void AvailableAssets(std::map > &mapAssetCoins, 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 = 9999999) const; + /** + * Helper function that calls AvailableCoinsAll, used to receive all coins, Assets and RVN + */ + void AvailableCoinsWithAssets(std::vector &vCoins, std::map > &mapAssetCoins, + 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 = 9999999) const; /** * populate vCoins with vector of available COutputs. */ - void AvailableCoins(std::vector& 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 = 9999999) const; + void AvailableCoins(std::vector& 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 = 9999999) const; /** * Return list of available coins and locked coins grouped by non-change output address. @@ -976,13 +990,13 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface /** RVN START */ bool CreateTransactionWithAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination dest, const std::set& setAssetOutPoints, const AssetType& assetType, bool sign = true); + std::string& strFailReason, const CCoinControl& coin_control, const CNewAsset& asset, const CTxDestination dest, const AssetType& assetType, bool sign = true); bool CreateTransactionWithTransferAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const std::set& setAssetOutPoints, bool sign = true); + std::string& strFailReason, const CCoinControl& coin_control, bool sign = true); bool CreateTransactionWithReissueAsset(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, const CReissueAsset& reissueAsset, const CTxDestination destination, const std::set& setAssetOutPoints, bool sign = true); + std::string& strFailReason, const CCoinControl& coin_control, const CReissueAsset& reissueAsset, const CTxDestination destination, bool sign = true); bool CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign = true); @@ -993,7 +1007,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface * @note passing nChangePosInOut as -1 will result in setting a random position */ bool CreateTransactionAll(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, - std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination dest, bool fTransferAsset, const std::set& setAssetOutPoints, bool fReissueAsset, const CReissueAsset& reissueAsset, const AssetType& assetType, bool sign = true); + std::string& strFailReason, const CCoinControl& coin_control, bool fNewAsset, const CNewAsset& asset, const CTxDestination dest, bool fTransferAsset, bool fReissueAsset, const CReissueAsset& reissueAsset, const AssetType& assetType, bool sign = true); /** RVN END */ From 82b56fc29fec3ed197537df8cfcad609e36f385b Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 14:48:12 -0600 Subject: [PATCH 09/12] Remove unused logging --- src/wallet/wallet.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 93c65fdde4..e778d23552 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2353,16 +2353,6 @@ void CWallet::AvailableCoinsAll(std::vector& vCoins, std::map Date: Mon, 20 Aug 2018 15:00:51 -0600 Subject: [PATCH 10/12] Fix asset_name parent typo --- src/assets/assets.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index e5cee5918a..806e128e6b 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -2228,7 +2228,7 @@ bool CreateAssetTransaction(CWallet* pwallet, const CNewAsset& asset, const std: // Get the owner outpoints if this is a subasset if (assetType == AssetType::SUB) { // Verify that this wallet is the owner for the asset, and get the owner asset outpoint - if (!VerifyWalletHasAsset(GetParentName(asset.strName + OWNER_TAG), error)) { + if (!VerifyWalletHasAsset(GetParentName(asset.strName) + OWNER_TAG, error)) { return false; } } From e74b70e3e9593305a4da7a14dccd5064d6fbf232 Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 15:23:48 -0600 Subject: [PATCH 11/12] Add functional test for chaining --- test/functional/assets.py | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/functional/assets.py b/test/functional/assets.py index 7fe4e5bf9b..fcec767f3c 100755 --- a/test/functional/assets.py +++ b/test/functional/assets.py @@ -12,6 +12,8 @@ assert_is_hash_string, ) +import string + class AssetTest(RavenTestFramework): def set_test_params(self): self.setup_clean_chain = True @@ -28,6 +30,8 @@ def run_test(self): n2.generate(431) self.sync_all() assert_equal(n0.getbalance(), 5000) + n0.generate(431) + self.sync_all() self.log.info("Calling issue()...") address0 = n0.getnewaddress() @@ -159,6 +163,44 @@ def run_test(self): assert_equal(raven_assets[1], "RAVEN3") self.sync_all() + self.log.info("Issuing chained assets in depth issue()...") + chain_address = n0.getnewaddress() + chain_string = "CHAIN1" + n0.issue(asset_name=chain_string, qty=1000, to_address=chain_address, change_address="", \ + units=4, reissuable=True, has_ipfs=True, ipfs_hash=ipfs_hash) + + for c in string.ascii_uppercase: + chain_string += '/' + c + if len(chain_string) > 30: + break + n0.issue(asset_name=chain_string, qty=1000, to_address=chain_address, change_address="", \ + units=4, reissuable=True, has_ipfs=True, ipfs_hash=ipfs_hash) + + n0.generate(1) + self.sync_all() + + chain_assets = n1.listassets(asset="CHAIN1*", verbose=False) + print(len(chain_assets)) + assert_equal(len(chain_assets), 13) + + self.log.info("Issuing chained assets in width issue()...") + chain_address = n0.getnewaddress() + chain_string = "CHAIN2" + n0.issue(asset_name=chain_string, qty=1000, to_address=chain_address, change_address="", \ + units=4, reissuable=True, has_ipfs=True, ipfs_hash=ipfs_hash) + + for c in string.ascii_uppercase: + asset_name = chain_string + '/' + c + + n0.issue(asset_name=asset_name, qty=1000, to_address=chain_address, change_address="", \ + units=4, reissuable=True, has_ipfs=True, ipfs_hash=ipfs_hash) + + n0.generate(1) + self.sync_all() + + chain_assets = n1.listassets(asset="CHAIN2/*", verbose=False) + print(len(chain_assets)) + assert_equal(len(chain_assets), 26) if __name__ == '__main__': AssetTest().main() From 84821b86ebeeba236378599324bf815cde96ea6e Mon Sep 17 00:00:00 2001 From: blondfrogs Date: Mon, 20 Aug 2018 15:31:21 -0600 Subject: [PATCH 12/12] Fix PR as requested by dev team --- src/wallet/wallet.cpp | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e778d23552..7a1d431de5 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2167,10 +2167,7 @@ void CWallet::AvailableCoinsWithAssets(std::vector &vCoins, std::map& vCoins, std::map >& mapAssetCoins, bool fGetRVN, bool fGetAssets, bool fOnlySafe, const CCoinControl *coinControl, const CAmount& nMinimumAmount, const CAmount& nMaximumAmount, const CAmount& nMinimumSumAmount, const uint64_t& nMaximumCount, const int& nMinDepth, const int& nMaxDepth) const { @@ -2806,19 +2803,6 @@ bool CWallet::SelectAssetsMinConf(const CAmount& nTargetValue, const int nConfMi coinLowestLarger = coin; coinLowestLargerAmount = nTempAmount; } - - -// setCoinsRet.insert(CInputCoin(out.tx, out.i)); -// -// if (mapValueRet.at(asset.first) >= mapAssetTargetValue.at(asset.first)) -// break; -// -// if (mapValueRet.at(asset.first) < mapAssetTargetValue.at(asset.first)) { -// return error( -// "%s : Tried to transfer an asset but this wallet didn't have enough, Asset Name: %s, Transfer Amount: %d, Wallet Total: %d", -// __func__, asset.first, mapValueRet.at(asset.first), mapAssetTargetValue.at(asset.first)); -// } - //------------------------------- } if (nTotalLower == nTargetValue)