Skip to content

Commit f172adf

Browse files
author
Andrey Galkin
committed
Merge branch 'pos-dos-revise' into 'develop'
PoS DoS protection improvements Closes dashpay#319 and dashpay#320 See merge request energi/cryptocurrency/core/energi!307
2 parents 5bf49cf + a32c938 commit f172adf

File tree

8 files changed

+437
-109
lines changed

8 files changed

+437
-109
lines changed

doc/release-notes/energi/release-notes-2.1.0.md

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Minor changes:
4242

4343
* CHANGED: updated checkpoints & minwork
4444
* CHANGED: "Sync Headers" to "Initial sync" messages
45+
* CHANGED: revised DoS protection for PoS
4546
* FIXED: PoS-enabled chain startup
4647
* FIXED: ExecuteSpork() to be called on local node
4748
* FIXED: active PoS detection on startup & ignore of invalid spork override

src/pos_kernel.cpp

+32-8
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,12 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlockIndex &blockFrom, cons
380380
}
381381

382382
// Check kernel hash target and coinstake signature
383-
bool CheckProofOfStake(CValidationState &state, const CBlockHeader &header)
383+
bool CheckProofOfStake(CValidationState &state, const CBlockHeader &header, const Consensus::Params& consensus)
384384
{
385385
if (header.posBlockSig.empty()) {
386386
return state.DoS(100, false, REJECT_MALFORMED, "bad-pos-sig", false, "missing PoS signature");
387387
}
388388

389-
auto &consensus = Params().GetConsensus();
390-
391389
COutPoint prevout = header.StakeInput();
392390

393391
// First try finding the previous transaction in database
@@ -398,26 +396,24 @@ bool CheckProofOfStake(CValidationState &state, const CBlockHeader &header)
398396
if (!GetTransaction(prevout.hash, txinPrevRef, consensus, txinHashBlock, true)) {
399397
BlockMap::iterator it = mapBlockIndex.find(header.hashPrevBlock);
400398

401-
if (it == mapBlockIndex.end()) {
399+
if ((it != mapBlockIndex.end()) && chainActive.Contains(it->second)) {
402400
return state.DoS(100, false, REJECT_INVALID, "bad-unkown-stake");
403401
} else {
404402
// We do not have the previous block, so the block may be valid
405403
return state.TransientError("tmp-bad-unkown-stake");
406404
}
407405
}
408406

409-
// NOTE: coin maturity checks are part of block validation as with PoW. So, headers may get accepted.
410-
411407
// Check tx input block is known
412408
{
413409
BlockMap::iterator it = mapBlockIndex.find(txinHashBlock);
414410

415-
if (it != mapBlockIndex.end()) {
411+
if ((it != mapBlockIndex.end()) && chainActive.Contains(it->second)) {
416412
pindex_tx = it->second;
417413
} else {
418414
it = mapBlockIndex.find(header.hashPrevBlock);
419415

420-
if (it == mapBlockIndex.end()) {
416+
if ((it != mapBlockIndex.end()) && chainActive.Contains(it->second)) {
421417
return state.DoS(100, false, REJECT_INVALID, "bad-stake-mempool",
422418
false, "stake from mempool");
423419
} else {
@@ -427,6 +423,34 @@ bool CheckProofOfStake(CValidationState &state, const CBlockHeader &header)
427423
}
428424
}
429425

426+
// Check if UTXO is beyond possible fork point
427+
{
428+
BlockMap::iterator it = mapBlockIndex.find(header.hashPrevBlock);
429+
430+
// It must never happen as it's part of header validation.
431+
if (it == mapBlockIndex.end()) {
432+
return state.DoS(100, false, REJECT_INVALID, "bad-prev-header",
433+
false, "previous PoS header is not known");
434+
}
435+
436+
auto pindex_fork = chainActive.FindFork(it->second);
437+
438+
if (!pindex_fork || (pindex_fork->nHeight < pindex_tx->nHeight)) {
439+
return state.DoS(100, false, REJECT_INVALID, "bad-stake-after-fork",
440+
false, "rogue fork tries to use UTXO from the current chain");
441+
}
442+
}
443+
444+
// NOTE: stake age check is part of CheckStakeKernelHash()
445+
446+
// Check stake maturity (double checking with other functionality for DoS mitigation)
447+
if (txinPrevRef->IsCoinBase() &&
448+
((header.nHeight - pindex_tx->nHeight) <= COINBASE_MATURITY)
449+
) {
450+
return state.DoS(100, false, REJECT_INVALID, "bad-stake-coinbase-maturity",
451+
false, "coinbase maturity mismatch for stake");
452+
}
453+
430454
// Extract stake public key ID and verify block signature
431455
{
432456
txnouttype whichType;

src/pos_kernel.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlockIndex &blockFrom, cons
2222

2323
// Check kernel hash target and coinstake signature
2424
// Sets hashProofOfStake on success return
25-
bool CheckProofOfStake(CValidationState &state, const CBlockHeader &block);
25+
bool CheckProofOfStake(CValidationState &state, const CBlockHeader &block, const Consensus::Params& consensus);
2626

2727
#endif // BITCOIN_KERNEL_H

src/test/DoS_tests.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,5 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
216216
BOOST_CHECK(mapOrphanTransactions.empty());
217217
}
218218

219+
219220
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)