/******************************************************************** * (C) 2018 Michael Toutonghi * * Distributed under the MIT software license, see the accompanying * file COPYING or http://www.opensource.org/licenses/mit-license.php. * * This supports code to catch nothing at stake cheaters who stake * on multiple forks. * */ #include "cc/StakeGuard.h" #include "script/script.h" #include "main.h" #include "hash.h" #include "cheatcatcher.h" #include "streams.h" using namespace std; CCheatList cheatList; boost::optional cheatCatcher; uint32_t CCheatList::Prune(uint32_t height) { uint32_t count = 0; pair::iterator, multimap::iterator> range; vector toPrune; if (height > 0 && NetworkUpgradeActive(height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { LOCK(cs_cheat); for (auto it = orderedCheatCandidates.begin(); it != orderedCheatCandidates.end() && it->second.height <= height; it--) { toPrune.push_back(&it->second); } count = toPrune.size(); for (auto ptxHolder : toPrune) { Remove(*ptxHolder); } } return count; // return how many removed } bool GetStakeParams(const CTransaction &stakeTx, CStakeParams &stakeParams); bool CCheatList::IsCheatInList(const CTransaction &tx, CTransaction *cheatTx) { // for a tx to be cheat, it needs to spend the same UTXO and be for a different prior block // the list should be pruned before this call // we return the first valid cheat we find CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); hw << tx.vin[0].prevout.hash; hw << tx.vin[0].prevout.n; uint256 utxo = hw.GetHash(); pair::iterator, multimap::iterator> range; CStakeParams p, s; if (GetStakeParams(tx, p)) { LOCK(cs_cheat); range = indexedCheatCandidates.equal_range(utxo); //printf("IsCheatInList - found candidates: %s\n", range.first == range.second ? "false" : "true"); for (auto it = range.first; it != range.second; it++) { CTransaction &cTx = it->second->tx; printf("cTx::opret : %s\n", cTx.vout[1].scriptPubKey.ToString().c_str()); // need both parameters to check if (GetStakeParams(cTx, s)) { if (p.prevHash != s.prevHash && s.blkHeight >= p.blkHeight) { cheatTx = &cTx; return true; } } } } return false; } bool CCheatList::Add(CTxHolder &txh) { if (NetworkUpgradeActive(txh.height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { LOCK(cs_cheat); auto it = orderedCheatCandidates.insert(pair(txh.height, txh)); indexedCheatCandidates.insert(pair(txh.utxo, &it->second)); } } void CCheatList::Remove(const CTxHolder &txh) { // first narrow by source tx, then compare with tx hash uint32_t count; pair::iterator, multimap::iterator> range; vector::iterator> utxoPrune; vector::iterator> heightPrune; { LOCK(cs_cheat); range = indexedCheatCandidates.equal_range(txh.utxo); for (auto it = range.first; it != range.second; it++) { uint256 hash = txh.tx.GetHash(); if (hash == it->second->tx.GetHash()) { auto hrange = orderedCheatCandidates.equal_range(it->second->height); for (auto hit = hrange.first; hit != hrange.second; hit++) { if (hit->second.tx.GetHash() == hash) { // add and remove them together utxoPrune.push_back(it); heightPrune.push_back(hit); } } } } for (auto it : utxoPrune) { indexedCheatCandidates.erase(it); } for (auto it : heightPrune) { orderedCheatCandidates.erase(it); } } }