@@ -53,6 +53,7 @@ CCriticalSection cs_main;
5353
5454BlockMap mapBlockIndex;
5555map<uint256, uint256> mapProofOfStake;
56+ map<COutPoint, int > mapStakeSpent;
5657set<pair<COutPoint, unsigned int > > setStakeSeen;
5758map<unsigned int , unsigned int > mapHashedBlocks;
5859CChain chainActive;
@@ -2004,6 +2005,8 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
20042005 if (coins->vout .size () < out.n + 1 )
20052006 coins->vout .resize (out.n + 1 );
20062007 coins->vout [out.n ] = undo.txout ;
2008+ // erase the spent input
2009+ mapStakeSpent.erase (out);
20072010 }
20082011 }
20092012 }
@@ -2235,6 +2238,33 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
22352238 if (!pblocktree->WriteTxIndex (vPos))
22362239 return state.Abort (" Failed to write transaction index" );
22372240
2241+
2242+
2243+
2244+ // add new entries
2245+ for (const CTransaction tx: block.vtx ) {
2246+ if (tx.IsCoinBase ())
2247+ continue ;
2248+ for (const CTxIn in: tx.vin ) {
2249+ LogPrint (" map" , " mapStakeSpent: Insert %s | %u\n " , in.prevout .ToString (), pindex->nHeight );
2250+ mapStakeSpent.insert (std::make_pair (in.prevout , pindex->nHeight ));
2251+ }
2252+ }
2253+
2254+
2255+ // delete old entries
2256+ for (auto it = mapStakeSpent.begin (); it != mapStakeSpent.end ();) {
2257+ if (it->second < pindex->nHeight - Params ().MaxReorganizationDepth ()) {
2258+ LogPrint (" map" , " mapStakeSpent: Erase %s | %u\n " , it->first .ToString (), it->second );
2259+ it = mapStakeSpent.erase (it);
2260+ }
2261+ else {
2262+ it++;
2263+ }
2264+ }
2265+
2266+
2267+
22382268 // add this block to the view's block chain
22392269 view.SetBestBlock (pindex->GetBlockHash ());
22402270
@@ -3338,6 +3368,57 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
33383368
33393369 int nHeight = pindex->nHeight ;
33403370
3371+
3372+ if (block.IsProofOfStake ()) {
3373+ LOCK (cs_main);
3374+
3375+ CCoinsViewCache coins (pcoinsTip);
3376+
3377+ if (!coins.HaveInputs (block.vtx [1 ])) {
3378+ // the inputs are spent at the chain tip so we should look at the recently spent outputs
3379+
3380+ for (CTxIn in : block.vtx [1 ].vin ) {
3381+ auto it = mapStakeSpent.find (in.prevout );
3382+ if (it == mapStakeSpent.end ()) {
3383+ return false ;
3384+ }
3385+ if (it->second <= pindexPrev->nHeight ) {
3386+ return false ;
3387+ }
3388+ }
3389+ }
3390+
3391+ // if this is on a fork
3392+ if (!chainActive.Contains (pindexPrev) && pindexPrev != NULL ) {
3393+ // start at the block we're adding on to
3394+ CBlockIndex *last = pindexPrev;
3395+
3396+ // while that block is not on the main chain
3397+ while (!chainActive.Contains (last) && pindexPrev != NULL ) {
3398+ CBlock bl;
3399+ ReadBlockFromDisk (bl, last);
3400+ // loop through every spent input from said block
3401+ for (CTransaction t : bl.vtx ) {
3402+ for (CTxIn in: t.vin ) {
3403+ // loop through every spent input in the staking transaction of the new block
3404+ for (CTxIn stakeIn : block.vtx [1 ].vin ) {
3405+ // if they spend the same input
3406+ if (stakeIn.prevout == in.prevout ) {
3407+ // reject the block
3408+ return false ;
3409+ }
3410+ }
3411+ }
3412+ }
3413+
3414+
3415+ // go to the parent block
3416+ last = pindexPrev->pprev ;
3417+ }
3418+ }
3419+ }
3420+
3421+
33413422 // Write block to history file
33423423 try {
33433424 unsigned int nBlockSize = ::GetSerializeSize (block, SER_DISK, CLIENT_VERSION);
0 commit comments