diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index f8a4de0a98..8c1d4c19bb 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -281,28 +281,9 @@ bool CNewAsset::IsValid(std::string& strError, CAssetsCache& assetCache, bool fC } if (fCheckMempool) { - for (const CTxMemPoolEntry &entry : mempool.mapTx) { - CTransaction tx = entry.GetTx(); - - bool fIsNewAsset = tx.IsNewAsset(); - bool fIsNewUniqueAsset = false; - if (!fIsNewAsset) - fIsNewUniqueAsset = tx.IsNewUniqueAsset(); - - if (fIsNewAsset || fIsNewUniqueAsset) { - CNewAsset asset; - std::string address; - - if (fIsNewAsset) - AssetFromTransaction(tx, asset, address); - else - UniqueAssetFromTransaction(tx, asset, address); - - if (asset.strName == strName) { - strError = _("Asset with this name is already in the mempool"); - return false; - } - } + if (mempool.mapAssetToHash.count(strName)) { + strError = _("Asset with this name is already in the mempool"); + return false; } } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4d85378ca5..6a9e796ecf 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -606,6 +606,12 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) mapReissuedTx.erase(hash); } } + + // Erase from the asset mempool maps if they match txid + if (mapHashToAsset.count(hash)) { + mapAssetToHash.erase(mapHashToAsset.at(hash)); + mapHashToAsset.erase(hash); + } /** RVN END */ } @@ -724,10 +730,20 @@ void CTxMemPool::removeConflicts(const CTransaction &tx) } } + /** * Called when a block is connected. Removes from mempool and updates the miner fee estimator. */ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned int nBlockHeight) +{ + std::set set; + removeForBlock(vtx, nBlockHeight, set); +} + +/** + * Called when a block is connected. Removes from mempool and updates the miner fee estimator. + */ +void CTxMemPool::removeForBlock(const std::vector& vtx, unsigned int nBlockHeight, std::set& setNewAssets) { LOCK(cs); std::vector entries; @@ -739,6 +755,21 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigne if (i != mapTx.end()) entries.push_back(&*i); } + + /** RVN START */ + // Get the newly added assets, and make sure they are in the entries + std::vector trans; + for (auto it : setNewAssets) { + if (mapAssetToHash.count(it.asset.strName)) { + indexed_transaction_set::iterator i = mapTx.find(mapAssetToHash.at(it.asset.strName)); + if (i != mapTx.end()) { + entries.push_back(&*i); + trans.emplace_back(i->GetTx()); + } + } + } + /** RVN END */ + // Before the txs in the new block have been removed from the mempool, update policy estimates if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);} for (const auto& tx : vtx) @@ -752,6 +783,22 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigne removeConflicts(*tx); ClearPrioritisation(tx->GetHash()); } + + /** RVN START */ + // Remove newly added asset issue transactions from the mempool if they haven't been removed already + for (auto tx : trans) + { + txiter it = mapTx.find(tx.GetHash()); + if (it != mapTx.end()) { + setEntries stage; + stage.insert(it); + RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); + } + removeConflicts(tx); + ClearPrioritisation(tx.GetHash()); + } + /** RVN END */ + lastRollingFeeUpdate = GetTime(); blockSinceLastRollingFeeBump = true; } @@ -767,6 +814,8 @@ void CTxMemPool::_clear() blockSinceLastRollingFeeBump = false; rollingMinimumFeeRate = 0; ++nTransactionsUpdated; + mapAssetToHash.clear(); + mapHashToAsset.clear(); } void CTxMemPool::clear() diff --git a/src/txmempool.h b/src/txmempool.h index 9cb0c1aab6..42c07f5562 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -467,6 +467,9 @@ class CTxMemPool mutable CCriticalSection cs; indexed_transaction_set mapTx; + std::map mapAssetToHash; + std::map mapHashToAsset; + typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; std::vector > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order @@ -543,6 +546,7 @@ class CTxMemPool void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeConflicts(const CTransaction &tx); + void removeForBlock(const std::vector& vtx, unsigned int nBlockHeight, std::set& setNewAssets ); void removeForBlock(const std::vector& vtx, unsigned int nBlockHeight); void clear(); diff --git a/src/validation.cpp b/src/validation.cpp index 3c816930f2..60f4a6f8bc 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -922,6 +922,20 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool mapReissuedAssets.insert(out); mapReissuedTx.insert(std::make_pair(out.second, out.first)); } + + if (AreAssetsDeployed()) { + for (auto out : tx.vout) { + if (out.scriptPubKey.IsAssetScript()) { + CAssetOutputEntry data; + if (!GetAssetData(out.scriptPubKey, data)) + continue; + if (data.type == TX_NEW_ASSET && !IsAssetNameAnOwner(data.assetName)) { + pool.mapAssetToHash[data.assetName] = hash; + pool.mapHashToAsset[hash] = data.assetName; + } + } + } + } } GetMainSignals().TransactionAddedToMempool(ptx); @@ -2802,10 +2816,20 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); + + /** RVN START */ + // Initialize sets used from removing asset entries from the mempool + std::set prevNewAssets; + std::set afterNewAsset; + /** RVN END */ + { CCoinsViewCache view(pcoinsTip); + /** RVN START */ CAssetsCache assetCache(*passets); + prevNewAssets = assetCache.setNewAssetsToAdd; // List of newly cached assets before block is connected + /** RVN END */ bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams, &assetCache); GetMainSignals().BlockChecked(blockConnecting, state); @@ -2815,6 +2839,14 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } + /** RVN START */ + // Get the newly created assets, from the connectblock assetCache + afterNewAsset = assetCache.setNewAssetsToAdd; + for (auto it : prevNewAssets) { + if (afterNewAsset.count(it)) + afterNewAsset.erase(it); + } + for (auto tx : blockConnecting.vtx) { uint256 txHash = tx->GetHash(); if (mapReissuedTx.count(txHash)) { @@ -2822,13 +2854,17 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, mapReissuedTx.erase(txHash); } } + /** RVN END */ + nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal); bool flushed = view.Flush(); assert(flushed); + /** RVN START */ bool assetFlushed = assetCache.Flush(true); assert(assetFlushed); + /** RVN END */ } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal); @@ -2838,7 +2874,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal); // Remove conflicting transactions from the mempool.; - mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); + mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight, afterNewAsset); disconnectpool.removeForBlock(blockConnecting.vtx); // Update chainActive & related variables. UpdateTip(pindexNew, chainparams); diff --git a/test/functional/feature_assets_mempool.py b/test/functional/feature_assets_mempool.py new file mode 100644 index 0000000000..b78fe98fb5 --- /dev/null +++ b/test/functional/feature_assets_mempool.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Copyright (c) 2017-2018 The Raven Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Testing asset mempool use cases + +""" +from test_framework.test_framework import RavenTestFramework +from test_framework.util import * + + +import string + +class AssetMempoolTest(RavenTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 2 + + + def activate_assets(self): + self.log.info("Generating RVN and activating assets...") + n0, n1 = self.nodes[0], self.nodes[1] + + n0.generate(1) + self.sync_all() + n0.generate(216) + self.sync_all() + n1.generate(216) + self.sync_all() + assert_equal("active", n0.getblockchaininfo()['bip9_softforks']['assets']['status']) + + + def issue_mempool_test(self): + self.log.info("Testing issue mempool...") + + n0, n1 = self.nodes[0], self.nodes[1] + + disconnect_all_nodes(self.nodes) + + asset_name = "MEMPOOL" + + # Issue asset on chain 1 and mine it into the blocks + n0.issue(asset_name) + n0.generate(15) + + # Issue asset on chain 2 but keep it in the mempool. No mining + txid = n1.issue(asset_name) + print(txid) + + connect_all_nodes_bi(self.nodes) + + assert_equal(n0.getblockcount(), n1.getblockcount()) + assert_equal(n0.getbestblockhash(), n1.getbestblockhash()) + + def run_test(self): + self.activate_assets() + self.issue_mempool_test() + +if __name__ == '__main__': + AssetMempoolTest().main() \ No newline at end of file