Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 97 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ using namespace std;
*/

CCriticalSection cs_main;
CCriticalSection cs_mapstake;

BlockMap mapBlockIndex;
map<uint256, uint256> mapProofOfStake;
Expand Down Expand Up @@ -84,6 +85,7 @@ struct COrphanTx {
CTransaction tx;
NodeId fromPeer;
};
map<COutPoint, int> mapStakeSpent;
map<uint256, COrphanTx> mapOrphanTransactions;
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
map<uint256, int64_t> mapRejectedBlocks;
Expand Down Expand Up @@ -2301,6 +2303,13 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
if (coins->vout.size() < out.n + 1)
coins->vout.resize(out.n + 1);
coins->vout[out.n] = undo.txout;

{
LOCK(cs_mapstake);

// erase the spent input
mapStakeSpent.erase(out);
}
}
}
}
Expand Down Expand Up @@ -2534,6 +2543,27 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!pblocktree->WriteTxIndex(vPos))
return state.Abort("Failed to write transaction index");

{
LOCK(cs_mapstake);
// add new entries
for (const CTransaction tx: block.vtx) {
if (tx.IsCoinBase() || tx.IsZerocoinSpend())
continue;

for (const CTxIn in: tx.vin) {
mapStakeSpent.insert(std::make_pair(in.prevout, pindex->nHeight));
}
}

for (auto it = mapStakeSpent.begin(); it != mapStakeSpent.end();) {
if (it->second < pindex->nHeight - Params().MaxReorganizationDepth()) {
it = mapStakeSpent.erase(it);
}
else {
it++;
}
}
}
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());

Expand Down Expand Up @@ -3643,13 +3673,25 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
if (mi == mapBlockIndex.end())
return state.DoS(0, error("%s : prev block %s not found", __func__, block.hashPrevBlock.ToString().c_str()), 0, "bad-prevblk");
pindexPrev = (*mi).second;
if (pindexPrev->nStatus & BLOCK_FAILED_MASK)
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
//If this "invalid" block is an exact match from the checkpoints, then reconsider it
if (Checkpoints::CheckBlock(pindexPrev->nHeight, block.hashPrevBlock, true)) {
LogPrintf("%s : Reconsidering block %s height %d\n", __func__, pindexPrev->GetBlockHash().GetHex(), pindexPrev->nHeight);
CValidationState statePrev;
ReconsiderBlock(statePrev, pindexPrev);
if (statePrev.IsValid()) {
ActivateBestChain(statePrev);
return true;
}
}
return state.DoS(100, error("%s : prev block invalid", __func__), REJECT_INVALID, "bad-prevblk");
}
}

if (block.GetHash() != Params().HashGenesisBlock() && !CheckWork(block, pindexPrev))
return false;


if (!AcceptBlockHeader(block, state, &pindex))
return false;

Expand All @@ -3669,6 +3711,60 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,

int nHeight = pindex->nHeight;

if (block.IsProofOfStake()) {
LOCK(cs_main);

CCoinsViewCache coins(pcoinsTip);

if (!coins.HaveInputs(block.vtx[1])) {
LOCK(cs_mapstake);

// the inputs are spent at the chain tip so we should look at the recently spent outputs
for (CTxIn in : block.vtx[1].vin) {
auto it = mapStakeSpent.find(in.prevout);
if (it == mapStakeSpent.end()) {
return false;
}
if (it->second < pindexPrev->nHeight) {
return false;
}
}
}

// if this is on a fork
if (!chainActive.Contains(pindexPrev) && pindexPrev != NULL) {

// start at the block we're adding on to
CBlockIndex *last = pindexPrev;

// while that block is not on the main chain
while (!chainActive.Contains(last) && last != NULL) {
CBlock bl;
ReadBlockFromDisk(bl, last);

// loop through every spent input from said block
for (CTransaction t : bl.vtx) {
for (CTxIn in: t.vin) {

// loop through every spent input in the staking transaction of the new block
for (CTxIn stakeIn : block.vtx[1].vin) {

// if they spend the same input
if (stakeIn.prevout == in.prevout) {

// reject the block
return false;
}
}
}
}

// go to the parent block
last = last->pprev;
}
}
}

// Write block to history file
try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
Expand Down