Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core][Qt] Rewind block index to last checkpoint functionality #2820

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
5 changes: 3 additions & 2 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,13 @@ static MapCheckpoints mapCheckpoints = {
{3014000, uint256S("78ad99b7225f73c42238bd7ca841ff700542b92bba75a0ef2ed351caa560f87f")}, //!< PIVX v5.3.0 enforced
{3024000, uint256S("be4bc75afcfb9136924810f7483b2695089a366cc4ee27fd6dc3ecd5396e1f0f")}, //!< Superblock
{3715200, uint256S("a676b9a598c393c82b949c37dd35013aeda55f5d18ab062349db6a8235972aaa")}, //!< Superblock for 5.5.0 mainnet rewards changeover
{3780000, uint256S("2667fa1d552999aca930ced7fd3902ae5721e5c256a607049e3c47a3137a18ee")}, //!< Fri, 10 Mar 2023 rewindblockindex testing

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This checkpoint is useless

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree! I should have prefaced thats for testing since the last checkpoint was a big further back and would have been a faster sync

};

static const CCheckpointData data = {
&mapCheckpoints,
1591401645, // * UNIX timestamp of last checkpoint block
5607713, // * total number of transactions between genesis and last checkpoint
1678401150, // * UNIX timestamp of last checkpoint block

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hence re-update those with last v5.5 checkpoint

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I will rebase and we just make sure to update more checkpoints more frequently

8716106, // * total number of transactions between genesis and last checkpoint
// (the tx=... number in the UpdateTip debug.log lines)
3000 // * estimated number of transactions per day after checkpoint
};
Expand Down
15 changes: 13 additions & 2 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks");
strUsage += HelpMessageOpt("-reindex", "Rebuild block chain index from current blk000??.dat files on startup");
strUsage += HelpMessageOpt("-resync", "Delete blockchain folders and resync from scratch on startup");
strUsage += HelpMessageOpt("-rewindblockindex", "Rewind blockchain to the last checkpoint");
#if !defined(WIN32)
strUsage += HelpMessageOpt("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)");
#endif
Expand Down Expand Up @@ -1424,6 +1425,7 @@ bool AppInitMain()

fReindex = gArgs.GetBoolArg("-reindex", false);
bool fReindexChainState = gArgs.GetBoolArg("-reindex-chainstate", false);
bool clearWitnessCaches = false;

// cache size calculations
int64_t nTotalCache = (gArgs.GetArg("-dbcache", nDefaultDbCache) << 20);
Expand Down Expand Up @@ -1572,8 +1574,17 @@ bool AppInitMain()
}
}

uiInterface.InitMessage(_("Verifying blocks..."));

if (!is_coinsview_empty) {
uiInterface.InitMessage(_("Verifying blocks..."));
if (!fReindex && chainActive.Tip() != NULL) {
uiInterface.InitMessage(_("Rewinding blocks to last checkpoint if needed..."));
if (!RewindBlockIndexToLastCheckpoint(chainparams, clearWitnessCaches, gArgs.GetBoolArg("-rewindblockindex", false))) {
strLoadError = _("Unable to rewind the blockchain to last checkpoint. You will need to redownload the blockchain");
break;
}
}

CBlockIndex *tip = chainActive.Tip();
RPCNotifyBlockChange(true, tip);
if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) {
Expand Down Expand Up @@ -1635,7 +1646,7 @@ bool AppInitMain()

// ********************************************************* Step 8: Backup and Load wallet
#ifdef ENABLE_WALLET
if (!InitLoadWallet())
if (!InitLoadWallet(clearWitnessCaches))
return false;
#else
LogPrintf("No wallet compiled in!\n");
Expand Down
1 change: 1 addition & 0 deletions src/qt/pivx/qtutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const QString ZAPTXES2("-zapwallettxes=2");
const QString UPGRADEWALLET("-upgradewallet");
const QString REINDEX("-reindex");
const QString RESYNC("-resync");
const QString REWIND("-rewindblockindex");

extern Qt::Modifier SHORT_KEY;

Expand Down
83 changes: 80 additions & 3 deletions src/qt/pivx/settings/forms/settingswalletrepairwidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>398</width>
<height>620</height>
<y>-57</y>
<width>383</width>
<height>677</height>
</rect>
</property>
<property name="autoFillBackground">
Expand Down Expand Up @@ -584,11 +584,88 @@
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalRewind" stretch="1,1">
<property name="spacing">
<number>20</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalRewind" stretch="0">
<property name="spacing">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="pushButtonRewind">
<property name="minimumSize">
<size>
<width>0</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>40</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Rewind blockchain</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="labelMessageRewind">
<property name="text">
<string>Rewind blockchain to last checkpoint</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
Expand Down
24 changes: 22 additions & 2 deletions src/qt/pivx/settings/settingswalletrepairwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ SettingsWalletRepairWidget::SettingsWalletRepairWidget(PIVXGUI* _window, QWidget
// Labels
setCssProperty({ui->labelMessageSalvage, ui->labelMessageRescan, ui->labelMessageRecover1,
ui->labelMessageRecover2, ui->labelMessageUpgrade, ui->labelMessageRebuild,
ui->labelMessageDelete}, "text-main-settings");
ui->labelMessageDelete, ui->labelMessageRewind}, "text-main-settings");

// Buttons
setCssProperty({ui->pushButtonSalvage, ui->pushButtonRescan, ui->pushButtonRecover1,
ui->pushButtonRecover2, ui->pushButtonUpgrade, ui->pushButtonRebuild,
ui->pushButtonDelete}, "btn-primary");
ui->pushButtonDelete, ui->pushButtonRewind}, "btn-primary");

// Wallet Repair Buttons
connect(ui->pushButtonSalvage, &QPushButton::clicked, this, &SettingsWalletRepairWidget::walletSalvage);
Expand All @@ -41,6 +41,7 @@ SettingsWalletRepairWidget::SettingsWalletRepairWidget(PIVXGUI* _window, QWidget
connect(ui->pushButtonUpgrade, &QPushButton::clicked, this, &SettingsWalletRepairWidget::walletUpgrade);
connect(ui->pushButtonRebuild, &QPushButton::clicked, this, &SettingsWalletRepairWidget::walletReindex);
connect(ui->pushButtonDelete, &QPushButton::clicked, this, &SettingsWalletRepairWidget::walletResync);
connect(ui->pushButtonRewind, &QPushButton::clicked, this, &SettingsWalletRepairWidget::walletRewind);
}

/** Restart wallet with "-salvagewallet" */
Expand Down Expand Up @@ -100,6 +101,25 @@ void SettingsWalletRepairWidget::walletResync()
buildParameterlist(RESYNC);
}

/** Restart wallet with "-rewindblockindex" */
void SettingsWalletRepairWidget::walletRewind()
{
QString rewindWarning = tr("This will rewind your blocks to the most recent checkpoint.<br /><br />");
rewindWarning += tr("Do you want to continue?.<br />");
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm resync Blockchain"),
rewindWarning,
QMessageBox::Yes | QMessageBox::Cancel,
QMessageBox::Cancel);

if (retval != QMessageBox::Yes) {
// Rewind canceled
return;
}

// Restart and rewind
buildParameterlist(REWIND);
}

/** Build command-line parameter list for restart */
void SettingsWalletRepairWidget::buildParameterlist(QString arg)
{
Expand Down
1 change: 1 addition & 0 deletions src/qt/pivx/settings/settingswalletrepairwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public Q_SLOTS:
void walletUpgrade();
void walletReindex();
void walletResync();
void walletRewind();

private:
Ui::SettingsWalletRepairWidget *ui;
Expand Down
8 changes: 8 additions & 0 deletions src/txdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,14 @@ bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockF
return WriteBatch(batch, true);
}

bool CBlockTreeDB::EraseBatchSync(const std::vector<const CBlockIndex*>& blockinfo) {
CDBBatch batch;
for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
batch.Erase(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()));
}
return WriteBatch(batch, true);
}

bool CBlockTreeDB::ReadTxIndex(const uint256& txid, CDiskTxPos& pos)
{
return Read(std::make_pair(DB_TXINDEX, txid), pos);
Expand Down
1 change: 1 addition & 0 deletions src/txdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class CBlockTreeDB : public CDBWrapper

bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo);
bool EraseBatchSync(const std::vector<const CBlockIndex*>& blockinfo);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo& info);
bool ReadLastBlockFile(int& nFile);
bool WriteReindexing(bool fReindexing);
Expand Down
96 changes: 96 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3741,6 +3741,102 @@ bool CVerifyDB::VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth
return true;
}

bool RewindBlockIndexToLastCheckpoint(const CChainParams& chainparams, bool& clearWitnessCaches, bool fJustCheck)
{
LOCK(cs_main);

// Get current Height and current Checkpoints
int nHeight = chainActive.Height();
const CBlockIndex* prevCheckPoint = GetLastCheckpoint();
const int checkPointHeight = prevCheckPoint ? prevCheckPoint->nHeight : 0;

// First load options
if(!fJustCheck)
return true;
Comment on lines +3753 to +3755

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we automatically detect if a rewind is needed?
If there are checkpoints between the tip and the last (known) checkpoint, we are not on the right chain.
Maybe something like this:

// If there are checkpoints between prevCheckPoint and chainActive.Tip
// This is not the right chain and a rewind to prevCheckPoint is needed
bool bNeedRewind = fJustCheck;
if (Checkpoints::fEnabled){
    const MapCheckpoints& checkpoints = *Params().Checkpoints().mapCheckpoints;

    for (const auto& i : reverse_iterate(checkpoints)) {
        if (i.first > checkPointHeight && i.first <= nHeight) {
            bNeedRewind = true;
            break;
        }
    }
}

if(!bNeedRewind)
    return true;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are checkpoints between the tip and the last (known) checkpoint, we are not on the right chain. and this is right,
but if there are not checkpoints between the tip and the last (known) checkpoint it does not necessarily mean we are on the right chain

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see a possibility in automatically detecting a rewind.

In such we have two cases:
1.
There is a checkpoint known and in the future, but we are behind said checkpoint, we would then simply sync to it.
2.
There is not a checkpoint and between the tip and the last known, to which we should be fine.

IE: Checkpoint block is 2150000, we are at block 2150010, if there was any in-between, that user would already have synced those
Checkpoint is block 2150005 and 2150000, while we are at block 2150010, there's no way we would be at 2150010 with a prevCheckpoint of 2150000 not 2150005 and on the same wallet version.


clearWitnessCaches = true;
CValidationState state;
const int blocksToRollBack = nHeight - checkPointHeight;
// Iterate to start removing blocks
while (nHeight > checkPointHeight) {
if (!DisconnectTip(state, chainparams, nullptr)) {
return error("%s: unable to disconnect block at height %i", __func__, nHeight);
}
// Occasionally flush state to disk.
if (!FlushStateToDisk(state, FLUSH_STATE_PERIODIC))
return false;

nHeight = chainActive.Height();
}

// Collect blocks to be removed (blocks in mapBlockIndex must be at least BLOCK_VALID_TREE).
// We do this after actual disconnecting, otherwise we'll end up writing the lack of data
// to disk before writing the chainstate, resulting in a failure to continue if interrupted.
std::vector<const CBlockIndex*> vBlocks;
for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) {
CBlockIndex* pindexIter = it->second;
if (!chainActive.Contains(pindexIter)) {
// Add to the list of blocks to remove
vBlocks.emplace_back(pindexIter);
if (pindexIter == pindexBestInvalid) {
// Reset invalid block marker if it was pointing to this block
pindexBestInvalid = nullptr;
}
// Reduce validity
pindexIter->nStatus = std::min<unsigned int>(pindexIter->nStatus & BLOCK_VALID_MASK, BLOCK_VALID_TREE) | (pindexIter->nStatus & ~BLOCK_VALID_MASK);
// Remove have-data flags.
pindexIter->nStatus &= ~(BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO);
// Remove storage location.
pindexIter->nFile = 0;
pindexIter->nDataPos = 0;
pindexIter->nUndoPos = 0;
// Remove various other things
pindexIter->nTx = 0;
pindexIter->nChainTx = 0;
pindexIter->nSequenceId = 0;
// Update indices
setBlockIndexCandidates.erase(pindexIter);
auto ret = mapBlocksUnlinked.equal_range(pindexIter->pprev);
while (ret.first != ret.second) {
if (ret.first->second == pindexIter) {
ret.first = mapBlocksUnlinked.erase(ret.first);
} else {
++ret.first;
}
}
} else if (pindexIter->IsValid(BLOCK_VALID_TRANSACTIONS) && pindexIter->nChainTx) {
setBlockIndexCandidates.insert(pindexIter);
}
}

// Set pindexBestHeader to the current chain tip
// (since we are about to delete the block it is pointing to)
pindexBestHeader = chainActive.Tip();

// Erase block indices on-disk
if (!pblocktree->EraseBatchSync(vBlocks)) {
return AbortNode(state, "Failed to erase from block index database");
}

// Erase block indices in-memory
for (auto pindex : vBlocks) {
auto ret = mapBlockIndex.find(*pindex->phashBlock);
if (ret != mapBlockIndex.end()) {
mapBlockIndex.erase(ret);
delete pindex;
}
}

CheckBlockIndex();

if (!FlushStateToDisk(state, FLUSH_STATE_ALWAYS)) {
return false;
}

return true;
}


/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */
static bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
Expand Down
3 changes: 3 additions & 0 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ class CVerifyDB
bool VerifyDB(CCoinsView* coinsview, int nCheckLevel, int nCheckDepth);
};

/** Rewind chain up to the last checkpoint */
bool RewindBlockIndexToLastCheckpoint(const CChainParams& chainparams, bool& clearWitnessCaches, bool fJustCheck);

/** Replay blocks that aren't fully applied to the database. */
bool ReplayBlocks(const CChainParams& params, CCoinsView* view);

Expand Down
4 changes: 2 additions & 2 deletions src/wallet/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ bool WalletVerify()
return true;
}

bool InitLoadWallet()
bool InitLoadWallet(bool clearWitnessCaches)
{
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
LogPrintf("Wallet disabled!\n");
Expand All @@ -214,7 +214,7 @@ bool InitLoadWallet()

for (const std::string& walletFile : gArgs.GetArgs("-wallet")) {
// create/load wallet
CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()));
CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir()), clearWitnessCaches);
if (!pwallet) {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/init.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ bool WalletParameterInteraction();
bool WalletVerify();

//! Load wallet databases.
bool InitLoadWallet();
bool InitLoadWallet(bool clearWitnessCaches);

#endif // PIVX_WALLET_INIT_H
Loading