Commit 1ddc2006 authored by coin-server's avatar coin-server

Merge branch 'fakestake' into 'master'

implement fakestake checking

See merge request ProjectMerge/merge!12
parents cef9f2ed 84045cb8
......@@ -64,6 +64,14 @@ CCriticalSection cs_main;
BlockMap mapBlockIndex;
map<uint256, uint256> mapProofOfStake;
set<pair<COutPoint, unsigned int> > setStakeSeen;
// maps any spent outputs in the past maxreorgdepth blocks to the height it was spent
// this means for incoming blocks, we can check that their stake output was not spent before
// the incoming block tried to use it as a staking input. We can also prevent block spam
// attacks because then we can check that either the staking input is available in the current
// active chain, or the staking input was spent in the past 100 blocks after the height
// of the incoming block.
map<COutPoint, int> mapStakeSpent;
map<unsigned int, unsigned int> mapHashedBlocks;
CChain chainActive;
CBlockIndex* pindexBestHeader = NULL;
......@@ -2049,6 +2057,9 @@ 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;
// erase the spent input
mapStakeSpent.erase(out);
}
}
}
......@@ -2296,6 +2307,27 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!pblocktree->WriteTxIndex(vPos))
return state.Abort("Failed to write transaction index");
// add new entries
for (const CTransaction tx: block.vtx) {
if (tx.IsCoinBase())
continue;
for (const CTxIn in: tx.vin) {
if (fDebug) LogPrintf("mapStakeSpent: Insert %s | %u\n", in.prevout.ToString(), pindex->nHeight);
mapStakeSpent.insert(std::make_pair(in.prevout, pindex->nHeight));
}
}
// delete old entries
for (auto it = mapStakeSpent.begin(); it != mapStakeSpent.end();) {
if (it->second < pindex->nHeight - Params().MaxReorganizationDepth()) {
if (fDebug) LogPrintf("mapStakeSpent: Erase %s | %u\n", it->first.ToString(), it->second);
it = mapStakeSpent.erase(it);
}
else {
it++;
}
}
// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());
......@@ -3479,6 +3511,59 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
int nHeight = pindex->nHeight;
if (block.IsProofOfStake()) {
int64_t nStartTime = GetTimeMillis();
LOCK(cs_main);
CCoinsViewCache coins(pcoinsTip);
if (!coins.HaveInputs(block.vtx[1])) {
// 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)) {
// 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)) {
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 = pindexPrev->pprev;
}
}
LogPrintf("Stake txchecks took %dms\n", (GetTimeMillis() - nStartTime));
}
// Write block to history file
try {
unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment