@@ -380,14 +380,12 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlockIndex &blockFrom, cons
380
380
}
381
381
382
382
// 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 )
384
384
{
385
385
if (header.posBlockSig .empty ()) {
386
386
return state.DoS (100 , false , REJECT_MALFORMED, " bad-pos-sig" , false , " missing PoS signature" );
387
387
}
388
388
389
- auto &consensus = Params ().GetConsensus ();
390
-
391
389
COutPoint prevout = header.StakeInput ();
392
390
393
391
// First try finding the previous transaction in database
@@ -398,26 +396,24 @@ bool CheckProofOfStake(CValidationState &state, const CBlockHeader &header)
398
396
if (!GetTransaction (prevout.hash , txinPrevRef, consensus, txinHashBlock, true )) {
399
397
BlockMap::iterator it = mapBlockIndex.find (header.hashPrevBlock );
400
398
401
- if (it == mapBlockIndex.end ()) {
399
+ if (( it != mapBlockIndex.end ()) && chainActive. Contains (it-> second )) {
402
400
return state.DoS (100 , false , REJECT_INVALID, " bad-unkown-stake" );
403
401
} else {
404
402
// We do not have the previous block, so the block may be valid
405
403
return state.TransientError (" tmp-bad-unkown-stake" );
406
404
}
407
405
}
408
406
409
- // NOTE: coin maturity checks are part of block validation as with PoW. So, headers may get accepted.
410
-
411
407
// Check tx input block is known
412
408
{
413
409
BlockMap::iterator it = mapBlockIndex.find (txinHashBlock);
414
410
415
- if (it != mapBlockIndex.end ()) {
411
+ if (( it != mapBlockIndex.end ()) && chainActive. Contains (it-> second )) {
416
412
pindex_tx = it->second ;
417
413
} else {
418
414
it = mapBlockIndex.find (header.hashPrevBlock );
419
415
420
- if (it == mapBlockIndex.end ()) {
416
+ if (( it != mapBlockIndex.end ()) && chainActive. Contains (it-> second )) {
421
417
return state.DoS (100 , false , REJECT_INVALID, " bad-stake-mempool" ,
422
418
false , " stake from mempool" );
423
419
} else {
@@ -427,6 +423,34 @@ bool CheckProofOfStake(CValidationState &state, const CBlockHeader &header)
427
423
}
428
424
}
429
425
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
+
430
454
// Extract stake public key ID and verify block signature
431
455
{
432
456
txnouttype whichType;
0 commit comments