map<BinaryData, vector<BinaryData>> ZeroConfContainer::purge( function<bool(const BinaryData&)> filter) { map<BinaryData, vector<BinaryData>> invalidatedKeys; if (!db_) return invalidatedKeys; /*** When a new block appears, it will have added some ZC, but it also may invalidate other ZC, by adding a transcation that consumes the TxOut of one of our ZC (maleability is a good example). This would break a ZC chain starting off that one invalidated ZC, taking away the whole chain. The simpliest way to track down all invalidated ZC is to reparse them all, and compare the new list to the old one. For ZC chains to be parsed properly, it is important ZC transactions are parsed in the order they appeared. ***/ SCOPED_TIMER("purgeZeroConfPool"); map<HashString, HashString> txHashToDBKey; map<BinaryData, Tx> txMap; map<HashString, map<BinaryData, TxIOPair> > txioMap; keyToSpentScrAddr_.clear(); txOutsSpentByZC_.clear(); LMDBEnv::Transaction tx; db_->beginDBTransaction(&tx, HISTORY, LMDB::ReadOnly); //parse ZCs anew for (auto ZCPair : txMap_) { const BinaryData& txHash = ZCPair.second.getThisHash(); { BinaryData ZCkey; auto keyIter = txHashToDBKey_.find(txHash); if (ITER_IN_MAP(keyIter, txHashToDBKey_)) ZCkey = keyIter->second; else ZCkey = getNewZCkey(); map<BinaryData, map<BinaryData, TxIOPair> > newTxIO = ZCisMineBulkFilter( ZCPair.second, ZCkey, ZCPair.second.getTxTime(), filter ); //if a relevant ZC was found, add it to our map if (!newTxIO.empty()) { txHashToDBKey[txHash] = ZCkey; txMap[ZCPair.first] = ZCPair.second; for (const auto& scrAddrTxio : newTxIO) { auto& txioPair = txioMap[scrAddrTxio.first]; txioPair.insert(scrAddrTxio.second.begin(), scrAddrTxio.second.end()); } } } } //build the set of invalidated zc dbKeys and delete them from db vector<BinaryData> keysToWrite, keysToDelete; for (auto& tx : txMap_) { if (txMap.find(tx.first) == txMap.end()) keysToDelete.push_back(tx.first); } auto delFromDB = [&, this](void)->void { this->updateZCinDB(keysToWrite, keysToDelete); }; //run in dedicated thread to make sure we can get a RW tx thread delFromDBthread(delFromDB); delFromDBthread.join(); //intersect with current container map for (const auto& saMapPair : txioMap_) { auto saTxioIter = txioMap.find(saMapPair.first); if (saTxioIter == txioMap.end()) { auto& txioVec = invalidatedKeys[saMapPair.first]; for (const auto & txioPair : saMapPair.second) txioVec.push_back(txioPair.first); continue; } for (const auto& txioPair : saMapPair.second) { if (saTxioIter->second.find(txioPair.first) == saTxioIter->second.end()) { auto& txioVec = invalidatedKeys[saMapPair.first]; txioVec.push_back(txioPair.first); } } } //copy new containers over txHashToDBKey_ = txHashToDBKey; txMap_ = txMap; txioMap_ = txioMap; //now purge newTxioMap_ for (auto& newSaTxioPair : newTxioMap_) { auto validTxioIter = txioMap_.find(newSaTxioPair.first); if (ITER_NOT_IN_MAP(validTxioIter, txioMap_)) { newSaTxioPair.second.clear(); continue; } auto& validSaTxioMap = validTxioIter->second; auto& newSaTxioMap = newSaTxioPair.second; auto newTxioIter = newSaTxioMap.begin(); while (newTxioIter != newSaTxioMap.end()) { if (KEY_NOT_IN_MAP(newTxioIter->first, validSaTxioMap)) newSaTxioMap.erase(newTxioIter++); else ++newTxioIter; } } return invalidatedKeys; /* // Rewrite the zero-conf pool file if (hashRmVec.size() > 0) rewriteZeroConfFile(); */ }
set<BinaryData> ZeroConfContainer::purge( function<bool(const BinaryData&)> filter) { set<BinaryData> invalidatedKeys; if (!db_) return invalidatedKeys; /*** When a new block appears, it will have added some ZC, but it also may invalidate other ZC, by adding a transcation that consumes the TxOut of one of our ZC (maleability is a good example). This would break a ZC chain starting off that one invalidated ZC, taking away the whole chain. The simpliest way to track down all invalidated ZC is to reparse them all, and compare the new list to the old one. For ZC chains to be parsed properly, it is important ZC transactions are parsed in the order they appeared. ***/ SCOPED_TIMER("purgeZeroConfPool"); map<HashString, HashString> txHashToDBKey; keyToSpentScrAddr_.clear(); txOutsSpentByZC_.clear(); txioMap_.clear(); newTxioMap_.clear(); vector<BinaryData> keysToWrite, keysToDelete; LMDBEnv::Transaction tx; db_->beginDBTransaction(tx, SSH, LMDB::ReadOnly); auto zcIter = txMap_.begin(); //parse ZCs anew while (zcIter != txMap_.end()) { const BinaryData& txHash = zcIter->second.getThisHash(); BinaryData ZCkey; auto keyIter = txHashToDBKey_.find(txHash); if (ITER_IN_MAP(keyIter, txHashToDBKey_)) ZCkey = keyIter->second; else ZCkey = getNewZCkey(); map<BinaryData, map<BinaryData, TxIOPair> > newTxIO = ZCisMineBulkFilter( zcIter->second, ZCkey, zcIter->second.getTxTime(), filter ); //if a relevant ZC was found, add it to our map if (!newTxIO.empty()) { txHashToDBKey[txHash] = ZCkey; for (const auto& saTxio : newTxIO) { auto& txioPair = txioMap_[saTxio.first]; auto satxio = saTxio.second; satxio.insert(txioPair.begin(), txioPair.end()); txioPair = move(satxio); auto& newTxioPair = newTxioMap_[saTxio.first]; satxio = saTxio.second; satxio.insert(newTxioPair.begin(), newTxioPair.end()); newTxioPair = move(satxio); } ++zcIter; } else { invalidatedKeys.insert(ZCkey); keysToDelete.push_back(ZCkey); txMap_.erase(zcIter++); } } //build the set of invalidated zc dbKeys and delete them from db auto delFromDB = [&, this](void)->void { this->updateZCinDB(keysToWrite, keysToDelete); }; //run in dedicated thread to make sure we can get a RW tx thread delFromDBthread(delFromDB); delFromDBthread.join(); return invalidatedKeys; }