Skip to content

Commit

Permalink
Merge pull request #133 from phoreproject/fix-block-spam
Browse files Browse the repository at this point in the history
Fix block spam
  • Loading branch information
tohsnoom committed Jan 16, 2019
2 parents 5aeae0a + 652522e commit c72fc10
Showing 1 changed file with 80 additions and 5 deletions.
85 changes: 80 additions & 5 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,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;
Expand Down Expand Up @@ -948,7 +956,7 @@ int GetZerocoinStartHeight()
}

libzerocoin::ZerocoinParams* GetZerocoinParams(int nHeight) {
return nHeight > Params().Zerocoin_LastOldParams() ? Params().Zerocoin_Params() : Params().OldZerocoin_Params();
return nHeight > Params().Zerocoin_LastOldParams() ? Params().Zerocoin_Params() : Params().OldZerocoin_Params();
}

void FindMints(vector<CMintMeta> vMintsToFind, vector<CMintMeta>& vMintsToUpdate, vector<CMintMeta>& vMissingMints)
Expand Down Expand Up @@ -1714,7 +1722,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState& state, const CTransa
hash.ToString(),
nFees, ::minRelayTxFee.GetFee(nSize) * 10000);


unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
if (!Params().RequireStandard()) {
scriptVerifyFlags = GetArg("-promiscuousmempoolflags", scriptVerifyFlags);
Expand Down Expand Up @@ -1836,7 +1844,7 @@ bool AcceptableInputs(CTxMemPool& pool, CValidationState& state, const CTransact
}
}
}

// Check for conflicts with in-memory transactions
if (!tx.IsZerocoinSpend()) {
LOCK(pool.cs); // protect pool.mapNextTx
Expand Down Expand Up @@ -2550,6 +2558,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);
}
}
}
Expand Down Expand Up @@ -3010,7 +3021,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!view.HaveInputs(tx))
return state.DoS(100, error("ConnectBlock() : inputs missing/spent"),
REJECT_INVALID, "bad-txns-inputs-missingorspent");

// Check that zPHR mints are not already known
if (tx.IsZerocoinMint()) {
for (auto& out : tx.vout) {
Expand Down Expand Up @@ -3181,6 +3192,22 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
if (!pblocktree->AddAddrIndex(vPosAddrid))
return state.Error("Failed to write address index");

// 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));
}
}

// delete old entries
for (auto it = mapStakeSpent.begin(); it != mapStakeSpent.end(); ++it) {
if (it->second < pindex->nHeight - Params().MaxReorganizationDepth()) {
mapStakeSpent.erase(it->first);
}
}

// add this block to the view's block chain
view.SetBestBlock(pindex->GetBlockHash());

Expand Down Expand Up @@ -4328,7 +4355,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
if(block.nVersion < Params().Zerocoin_HeaderVersion())
return state.DoS(50, error("CheckBlockHeader() : block version must be above 4 after ZerocoinStartHeight"),
REJECT_INVALID, "block-version");

vector<CBigNum> vBlockSerials;
for (const CTransaction& tx : block.vtx) {
if (!CheckTransaction(tx, true, chainActive.Height() + 1 >= Params().Zerocoin_StartHeight(), state, GetSporkValue(SPORK_17_SEGWIT_ACTIVATION) < block.nTime))
Expand Down Expand Up @@ -4540,6 +4567,54 @@ 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])) {
// 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;
}
}
}

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

0 comments on commit c72fc10

Please sign in to comment.