Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,12 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_MTP].nTimeout = SWITCH_TO_MTP_BLOCK_HEADER + consensus.nMinerConfirmationWindow*2 * 5*60;

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000708f98bf623f02e");
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000017e2cd0ce4d870bd3"); // 1390000

// By default assume that the signatures in ancestors of this block are valid.
consensus.defaultAssumeValid = uint256S("50aff78270725ec253a722ec18069deb233f2e57eb7d64479f027141619cdda4 "); //184200
consensus.hashMTPAnchor = uint256S("0x156bf05c91cdc4a88c9d993c29402f89a901274797ed98958666b128394353c5"); // 1390000
consensus.nMTPAnchorHeight = 1390000;
consensus.defaultAssumeValid = consensus.hashMTPAnchor;

consensus.nCheckBugFixedAtBlock = ZC_CHECK_BUG_FIXED_AT_BLOCK;
consensus.nZnodePaymentsBugFixedAtBlock = ZC_ZNODE_PAYMENT_BUG_FIXED_AT_BLOCK;
Expand Down Expand Up @@ -391,13 +393,14 @@ class CMainParams : public CChainParams {
(20580, uint256S("0x591b00ac1ba7d30b9f440efc467072400805a900e92f04f272e6f70cb55ab026"))
(121378, uint256S("0xa7d9a56dd2986442b5c10ad036eb4e6555eaa8d9f6645c7b9620597792a153ac"))
(341100, uint256S("0x1ca6cbd9f6e13db8e0e1db0b77f8b1a037b01c69558214bc1ae2ce1f81da4890"))
(1390000, consensus.hashMTPAnchor)
};

chainTxData = ChainTxData{
1545712287, // * UNIX timestamp of last checkpoint block
933513, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the SetBestChain debug.log lines)
0.014 // * estimated number of transactions per second after checkpoint
1791341502, // * UNIX timestamp of block 1390000
4362287, // * total number of transactions between genesis and that block
// (the tx=... number in the UpdateTip debug.log lines)
0.0194 // * estimated number of transactions per second after that block
};
consensus.nSpendV15StartBlock = ZC_V1_5_STARTING_BLOCK;
consensus.nSpendV2ID_1 = ZC_V2_SWITCH_ID_1;
Expand Down
2 changes: 2 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ struct Params {
int64_t DifficultyAdjustmentInterval(bool fMTP = false) const { return nPowTargetTimespan / (fMTP ? nPowTargetSpacingMTP : nPowTargetSpacing); }
uint256 nMinimumChainWork;
uint256 defaultAssumeValid;
int nMTPAnchorHeight{0};
uint256 hashMTPAnchor;

bool IsMain() const { return chainType == chainMain; }
bool IsTestnet() const { return chainType == chainTestnet; }
Expand Down
23 changes: 16 additions & 7 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,9 @@ void ProcessBlockAvailability(NodeId nodeid) {
if (!state->hashLastUnknownBlock.IsNull()) {
BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) {
if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
if (IsBlockIndexAnchored(itOld->second, Params().GetConsensus()) &&
(state->pindexBestKnownBlock == NULL || !IsBlockIndexAnchored(state->pindexBestKnownBlock, Params().GetConsensus()) ||
itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork))
state->pindexBestKnownBlock = itOld->second;
state->hashLastUnknownBlock.SetNull();
}
Expand All @@ -414,7 +416,9 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
BlockMap::iterator it = mapBlockIndex.find(hash);
if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
// An actually better block was announced.
if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
if (IsBlockIndexAnchored(it->second, Params().GetConsensus()) &&
(state->pindexBestKnownBlock == NULL || !IsBlockIndexAnchored(state->pindexBestKnownBlock, Params().GetConsensus()) ||
it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork))
state->pindexBestKnownBlock = it->second;
} else {
// An unknown block was announced; just assume that the latest one is the best one.
Expand Down Expand Up @@ -505,7 +509,8 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con
// Make sure pindexBestKnownBlock is up to date, we'll need it.
ProcessBlockAvailability(nodeid);

if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
if (state->pindexBestKnownBlock == NULL || !IsBlockIndexAnchored(state->pindexBestKnownBlock, consensusParams) ||
state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
// This peer has nothing interesting.
return;
}
Expand Down Expand Up @@ -2541,7 +2546,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
return true;

if (pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
if (!IsBlockIndexAnchored(pindex, chainparams.GetConsensus()) ||
pindex->nChainWork <= chainActive.Tip()->nChainWork || // We know something better
pindex->nTx != 0) { // We had this block at some point, but pruned it
if (fAlreadyInFlight) {
// We requested this block for some reason, but our mempool will probably be useless
Expand Down Expand Up @@ -2845,7 +2851,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus());
// If this set of headers is valid and ends in a block with at least as
// much work as our tip, download as much as possible.
if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
if (fCanDirectFetch && pindexLast->IsValid(BLOCK_VALID_TREE) &&
IsBlockIndexAnchored(pindexLast, chainparams.GetConsensus()) && chainActive.Tip()->nChainWork <= pindexLast->nChainWork) {
std::vector<const CBlockIndex*> vToFetch;
const CBlockIndex *pindexWalk = pindexLast;
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
Expand Down Expand Up @@ -3481,8 +3488,10 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
pindexBestHeader = chainActive.Tip();
bool fFetch = state.fPreferredDownload || (nPreferredDownload == 0 && !pto->fClient && !pto->fOneShot); // Download if this is a nice peer, or we have no nice peers and this one might do.
if (!state.fSyncStarted && !pto->fClient && !fImporting && !fReindex) {
// Only actively request headers from a single peer, unless we're close to today.
if ((nSyncStarted == 0 && fFetch) || pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
// Query more peers until we know the anchor, then keep the usual single-peer header sync.
if ((fFetch && (nSyncStarted == 0 ||
(consensusParams.nMTPAnchorHeight && pindexBestHeader->nHeight < consensusParams.nMTPAnchorHeight))) ||
pindexBestHeader->GetBlockTime() > GetAdjustedTime() - 24 * 60 * 60) {
state.fSyncStarted = true;
nSyncStarted++;
const CBlockIndex *pindexStart = pindexBestHeader;
Expand Down
84 changes: 84 additions & 0 deletions src/test/progpow_tests.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "util.h"

#include "clientversion.h"
#include "pow.h"
#include "primitives/transaction.h"
#include "random.h"
#include "sync.h"
Expand Down Expand Up @@ -81,6 +82,30 @@ BOOST_AUTO_TEST_CASE(transition)
BOOST_ASSERT(chainActive.Height() == oldHeight+1);
BOOST_ASSERT(ppBlock.IsProgPow());

CBlockHeader header = regularBlock.GetBlockHeader();
header.hashPrevBlock = ppBlock.GetHash();
header.nTime = mutableParams.nPPSwitchTime - 1;
header.nBits = GetNextWorkRequired(chainActive.Tip(), &header, mutableParams);
while (!CheckProofOfWork(header.GetHash(), header.nBits, mutableParams))
++header.nNonce;

CValidationState state;
BOOST_CHECK(!ProcessNewBlockHeaders({header}, state, Params()));
BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-blk-progpow-state");
{
LOCK(cs_main);
BOOST_CHECK_EQUAL(mapBlockIndex.count(header.GetHash()), 0);
}
const auto switchTime = mutableParams.nPPSwitchTime;
mutableParams.nPPSwitchTime = INT_MAX; // Simulate a header indexed under the old rule.
CValidationState indexedState;
const bool indexed = ProcessNewBlockHeaders({header}, indexedState, Params());
mutableParams.nPPSwitchTime = switchTime;
BOOST_REQUIRE(indexed);
CValidationState knownState;
BOOST_CHECK(!ProcessNewBlockHeaders({header}, knownState, Params()));
BOOST_CHECK_EQUAL(knownState.GetRejectReason(), "bad-blk-progpow-state");

// Try to add regular block after PP one. Should throw an exception
SetMockTime(mutableParams.nPPSwitchTime-1);
try {
Expand Down Expand Up @@ -157,6 +182,65 @@ BOOST_AUTO_TEST_CASE(header_height_mismatch)
BOOST_CHECK_EQUAL(mapBlockIndex.count(block.GetHash()), 0);
}

BOOST_AUTO_TEST_CASE(mtp_anchor)
{
CBlockIndex* originalTip = chainActive.Tip();
CBlock block = CreateBlock({}, m_coinbaseKey);
mutableParams.nMTPAnchorHeight = originalTip->nHeight + 2;
mutableParams.hashMTPAnchor = uint256S("01");

BOOST_REQUIRE(ProcessNewBlock(Params(), std::make_shared<const CBlock>(block), true, nullptr));
BOOST_CHECK(chainActive.Tip() == originalTip);

CBlockHeader header = block.GetBlockHeader();
header.hashPrevBlock = block.GetHash();
++header.nTime;
{
LOCK(cs_main);
header.nBits = GetNextWorkRequired(mapBlockIndex.at(block.GetHash()), &header, mutableParams);
}
while (!CheckProofOfWork(header.GetHash(), header.nBits, mutableParams))
++header.nNonce;

const bool oldCheckpoints = fCheckpointsEnabled;
fCheckpointsEnabled = false;
CValidationState state;
const bool accepted = ProcessNewBlockHeaders({header}, state, Params());
fCheckpointsEnabled = oldCheckpoints;
BOOST_CHECK(!accepted);
BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-mtp-anchor");
{
LOCK(cs_main);
BOOST_CHECK_EQUAL(mapBlockIndex.count(header.GetHash()), 0);
}

mutableParams.hashMTPAnchor = header.GetHash();
CValidationState anchorState;
BOOST_REQUIRE(ProcessNewBlockHeaders({header}, anchorState, Params()));
{
LOCK(cs_main);
BOOST_CHECK(IsBlockIndexAnchored(mapBlockIndex.at(block.GetHash()), mutableParams));
BOOST_CHECK(IsBlockIndexAnchored(mapBlockIndex.at(header.GetHash()), mutableParams));
}
CValidationState activateState;
BOOST_REQUIRE(ActivateBestChain(activateState, Params()));
BOOST_CHECK(chainActive.Tip()->GetBlockHash() == block.GetHash());
}

BOOST_AUTO_TEST_CASE(mtp_anchor_unknown_at_startup)
{
// A node that stopped below the anchor height, before it saw the anchor header, must still start.
const uint256 tipHash = chainActive.Tip()->GetBlockHash();
mutableParams.nMTPAnchorHeight = chainActive.Height() + 2;
mutableParams.hashMTPAnchor = uint256S("01");

FlushStateToDisk();
UnloadBlockIndex();
mapPrevBlockIndex.clear();
BOOST_REQUIRE(LoadBlockIndex(Params()));
BOOST_CHECK(chainActive.Tip()->GetBlockHash() == tipHash);
}

BOOST_AUTO_TEST_CASE(limit)
{
mutableParams.nPPSwitchTime = INT_MAX;
Expand Down
Loading
Loading