diff --git a/src/chainparams.cpp b/src/chainparams.cpp --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -100,6 +100,10 @@ // two days consensus.nDAAHalfLife = 2 * 24 * 60 * 60; + // PoW penalty for early blocks + consensus.earlyBlockPowTargetPenaltyFactor = 4; + consensus.earlyBlockPowTargetPenaltyWindow = 300; + // nPowTargetTimespan / nPowTargetSpacing consensus.nMinerConfirmationWindow = 2016; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY] = { @@ -264,6 +268,10 @@ // two days consensus.nDAAHalfLife = 2 * 24 * 60 * 60; + // PoW penalty for early blocks + consensus.earlyBlockPowTargetPenaltyFactor = 2; + consensus.earlyBlockPowTargetPenaltyWindow = 300; + // nPowTargetTimespan / nPowTargetSpacing consensus.nMinerConfirmationWindow = 2016; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY] = { @@ -411,6 +419,10 @@ // two days consensus.nDAAHalfLife = 2 * 24 * 60 * 60; + // No penalty on regtest + consensus.earlyBlockPowTargetPenaltyFactor = 1; + consensus.earlyBlockPowTargetPenaltyWindow = 1; + // Faster than normal for regtest (144 instead of 2016) consensus.nMinerConfirmationWindow = 144; consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY] = { diff --git a/src/consensus/params.h b/src/consensus/params.h --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -130,6 +130,10 @@ uint256 nMinimumChainWork; BlockHash defaultAssumeValid; + /** Soft consensus parameters, used in conjunction with Avalanche */ + int64_t earlyBlockPowTargetPenaltyFactor; + int64_t earlyBlockPowTargetPenaltyWindow; + int DeploymentHeight(BuriedDeployment dep) const { switch (dep) { case DEPLOYMENT_P2SH: diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -998,10 +998,14 @@ std::shared_ptr pblock = nullptr) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex) LOCKS_EXCLUDED(cs_main); - bool AcceptBlock(const Config &config, - const std::shared_ptr &pblock, - BlockValidationState &state, bool fRequested, - const FlatFilePos *dbp, bool *fNewBlock) + bool AcceptBlock( + const Config &config, const std::shared_ptr &pblock, + BlockValidationState &state, bool fRequested, const FlatFilePos *dbp, + bool *fNewBlock, + bool fPostConsensus = + false) // TODO rename to better indicate that this flag is for + // additional post-consensus rules enforced by the avalanche + // post-consensus mechanism, and not post-consensus itself EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Block (dis)connection on a given view: diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4098,18 +4098,21 @@ /** * Store a block on disk. * - * @param[in] config The global config. - * @param[in,out] pblock The block we want to accept. - * @param[in] fRequested A boolean to indicate if this block was requested - * from our peers. - * @param[in] dbp If non-null, the disk position of the block. - * @param[in,out] fNewBlock True if block was first received via this call. + * @param[in] config The global config. + * @param[in,out] pblock The block we want to accept. + * @param[in] fRequested A boolean to indicate if this block was + * requested from our peers. + * @param[in] dbp If non-null, the disk position of the block. + * @param[in,out] fNewBlock True if block was first received via this call. + * @param[in] fPostConsensus A boolean to indicate if Avalanche + * post-consensus rules should be applied. * @return True if the block is accepted as a valid block and written to disk. */ bool CChainState::AcceptBlock(const Config &config, const std::shared_ptr &pblock, BlockValidationState &state, bool fRequested, - const FlatFilePos *dbp, bool *fNewBlock) { + const FlatFilePos *dbp, bool *fNewBlock, + bool fPostConsensus) { AssertLockHeld(cs_main); const CBlock &block = *pblock; @@ -4231,6 +4234,61 @@ } } + // Avalanche post-consensus rules + if (fPostConsensus && pindex->pprev && !IsBlockAvalancheFinalized(pindex)) { + const CBlockIndex *parent = pindex->pprev; + const int64_t timeDiff = + pindex->nTimeReceived >= parent->nTimeReceived + ? pindex->nTimeReceived - parent->nTimeReceived + : 0; + + if (timeDiff < consensusParams.earlyBlockPowTargetPenaltyWindow) { + const CBlockHeader header = pindex->GetBlockHeader(); + LogPrintf("Early block hash: %s, parentTime: %d, receivedTime: %d, " + "timeDiff: %d\n", + header.GetHash().ToString(), parent->nTimeReceived, + pindex->nTimeReceived, timeDiff); + arith_uint256 target; + arith_uint256 oldTarget; + bool negative; + bool overflow; + uint32_t powRequired = + GetNextWorkRequired(parent, &header, m_params); + target.SetCompact(powRequired, &negative, &overflow); + oldTarget = target; + target *= consensusParams.earlyBlockPowTargetPenaltyWindow; + target /= (1 - consensusParams.earlyBlockPowTargetPenaltyFactor) * + timeDiff + + consensusParams.earlyBlockPowTargetPenaltyFactor * + consensusParams.earlyBlockPowTargetPenaltyWindow; + const uint32_t newPowLimit = target.GetCompact(negative); + + LogPrintf("Early block:\n" + "oldTarget: %s, oldPowLimit: 0x%.8x\n" + "newTarget: %s, newPowLimit: 0x%.8x\n", + oldTarget.ToString(), powRequired, target.ToString(), + newPowLimit); + + if (!CheckProofOfWork(header.GetHash(), newPowLimit, + consensusParams)) { + // Mark this block as parked + /*BlockValidationState state; + UpdateFlagsForBlock(nullptr, pindex, + [](const BlockStatus status) { + return status.withParked(); + });*/ + pindex->nStatus = pindex->nStatus.withParked(); + setDirtyBlockIndex.insert(pindex); + LogPrintf("Early block rejected due to PoW " + "penalty: %s\n", + header.GetHash().ToString()); + } else { + LogPrintf("Early block accepted: %s\n", + header.GetHash().ToString()); + } + } + } + // Header is valid/has work and the merkle tree is good. // Relay now, but if it does not build on our best tip, let the // SendMessages loop relay it. @@ -4268,6 +4326,8 @@ bool force_processing, bool *new_block) { AssertLockNotHeld(cs_main); + bool applyPostConsensus = g_avalanche && g_avalanche->isQuorumEstablished(); + { if (new_block) { *new_block = false; @@ -4288,8 +4348,9 @@ BlockValidationOptions(config)); if (ret) { // Store to disk - ret = ActiveChainstate().AcceptBlock( - config, block, state, force_processing, nullptr, new_block); + ret = ActiveChainstate().AcceptBlock(config, block, state, + force_processing, nullptr, + new_block, applyPostConsensus); } if (!ret) {