Skip to content

Commit 469524b

Browse files
committed
fix: do not trust legacy diffs, detect correct nVersion on migration
1 parent 80906fa commit 469524b

File tree

4 files changed

+65
-38
lines changed

4 files changed

+65
-38
lines changed

src/evo/deterministicmns.cpp

Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,7 @@ bool CDeterministicMNManager::IsMigrationRequired() const
13641364
return false; // No legacy format found
13651365
}
13661366

1367-
bool CDeterministicMNManager::MigrateLegacyDiffs()
1367+
bool CDeterministicMNManager::MigrateLegacyDiffs(const CBlockIndex* const tip_index)
13681368
{
13691369
// CRITICAL: This migration converts ALL stored CDeterministicMNListDiff entries
13701370
// from legacy database key (DB_LIST_DIFF_LEGACY) to new key (DB_LIST_DIFF) format
@@ -1373,75 +1373,111 @@ bool CDeterministicMNManager::MigrateLegacyDiffs()
13731373

13741374
LogPrintf("CDeterministicMNManager::%s -- Starting migration to nVersion-first format\n", __func__);
13751375

1376-
std::vector<uint256> keys_to_erase;
1376+
std::vector<const CBlockIndex*> keys_to_erase;
13771377

13781378
CDBBatch batch(m_evoDb.GetRawDB());
13791379
std::unique_ptr<CDBIterator> pcursor{m_evoDb.GetRawDB().NewIterator()};
1380-
auto start{std::make_tuple(DB_LIST_DIFF_LEGACY, uint256())};
1381-
pcursor->Seek(start);
13821380

1383-
while (pcursor->Valid()) {
1384-
decltype(start) k;
1385-
if (!pcursor->GetKey(k) || std::get<0>(k) != DB_LIST_DIFF_LEGACY) {
1381+
// Keep track of the list to get correct nVersion at the current height
1382+
CDeterministicMNList snapshot;
1383+
1384+
const auto start_height{Params().GetConsensus().DeploymentHeight(Consensus::DEPLOYMENT_DIP0003)};
1385+
for (auto current_height : irange::range(start_height, tip_index->nHeight + 1)) {
1386+
auto current_index = tip_index->GetAncestor(current_height);
1387+
auto target_key{std::make_tuple(DB_LIST_DIFF_LEGACY, current_index->GetBlockHash())};
1388+
pcursor->Seek(target_key);
1389+
1390+
decltype(target_key) key;
1391+
if (!pcursor->Valid() || !pcursor->GetKey(key) || std::get<0>(key) != DB_LIST_DIFF_LEGACY) {
13861392
break;
13871393
}
13881394

1395+
if (std::get<1>(key) != current_index->GetBlockHash()) {
1396+
// This happens because Seek() stops at equal or larger key but we only want equal one
1397+
continue;
1398+
}
1399+
13891400
// Use legacy-aware deserialization for DB_LIST_DIFF_LEGACY entries
13901401
CDataStream s(SER_DISK, CLIENT_VERSION);
1391-
if (!m_evoDb.GetRawDB().ReadDataStream(k, s)) {
1402+
if (!m_evoDb.GetRawDB().ReadDataStream(key, s)) {
13921403
break;
13931404
}
13941405

13951406
CDeterministicMNListDiff legacyDiff;
13961407
legacyDiff.UnserializeLegacyFormat(s); // Use legacy format deserializer
1408+
snapshot.ApplyDiff(current_index, legacyDiff);
13971409

13981410
CDeterministicMNListDiff convertedDiff;
1399-
convertedDiff.nHeight = legacyDiff.nHeight;
14001411
convertedDiff.addedMNs = legacyDiff.addedMNs;
14011412
convertedDiff.removedMns = legacyDiff.removedMns;
14021413

14031414
// The conversion is already done by UnserializeLegacyFormat()!
14041415
// CDeterministicMNStateDiffLegacy.ToNewFormat() was called during deserialization
14051416
// So legacyDiff.updatedMNs already contains properly converted CDeterministicMNStateDiff objects
14061417

1407-
// Simply copy the already-converted state diffs
1408-
for (const auto& [internalId, stateDiff] : legacyDiff.updatedMNs) {
1418+
// Copy the already-converted state diffs but make sure pubKeyOperator, nVersion and fields are set properly
1419+
for (auto& [internalId, stateDiff] : legacyDiff.updatedMNs) {
1420+
auto dmn = snapshot.GetMNByInternalId(internalId);
1421+
if (!dmn) {
1422+
// shouldn't happen
1423+
throw std::runtime_error(strprintf("%s: can't find an updated masternode, id=%d", __func__, internalId));
1424+
}
1425+
if (!(stateDiff.fields & CDeterministicMNStateDiff::Field_nVersion)) {
1426+
if ((stateDiff.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) ||
1427+
(stateDiff.fields & CDeterministicMNStateDiff::Field_netInfo)) {
1428+
stateDiff.fields |= CDeterministicMNStateDiff::Field_nVersion;
1429+
stateDiff.state.nVersion = dmn->pdmnState->nVersion;
1430+
}
1431+
if (stateDiff.fields & CDeterministicMNStateDiff::Field_pubKeyOperator) {
1432+
stateDiff.state.pubKeyOperator.SetLegacy(stateDiff.state.nVersion == ProTxVersion::LegacyBLS);
1433+
}
1434+
}
14091435
convertedDiff.updatedMNs.emplace(internalId, stateDiff);
14101436
}
14111437

14121438
// Write the converted diff to new database key
1413-
batch.Write(std::make_pair(DB_LIST_DIFF, std::get<1>(k)), convertedDiff);
1414-
keys_to_erase.push_back(std::get<1>(k));
1439+
batch.Write(std::make_pair(DB_LIST_DIFF, std::get<1>(key)), convertedDiff);
1440+
keys_to_erase.push_back(current_index);
14151441

14161442
if (batch.SizeEstimate() >= (1 << 24)) {
1417-
LogPrintf("CDeterministicMNManager::%s -- Writing new diffs...\n", __func__);
1443+
LogPrintf("CDeterministicMNManager::%s -- Writing new diffs, height=%d...\n", __func__, current_height);
14181444
m_evoDb.GetRawDB().WriteBatch(batch);
14191445
batch.Clear();
14201446
}
1421-
1422-
pcursor->Next();
14231447
}
1424-
pcursor.reset();
14251448

1426-
LogPrintf("CDeterministicMNManager::%s -- Writing new diffs...\n", __func__);
1449+
LogPrintf("CDeterministicMNManager::%s -- Writing new diffs, height=%d...\n", __func__, tip_index->nHeight);
14271450
m_evoDb.GetRawDB().WriteBatch(batch);
14281451
batch.Clear();
14291452

14301453
LogPrintf("CDeterministicMNManager::%s -- Erasing %d legacy database entries after successful migration\n",
14311454
__func__, keys_to_erase.size());
14321455

1433-
// Delete all legacy format entries
1434-
for (const auto& blockHash : keys_to_erase) {
1435-
batch.Erase(std::make_pair(DB_LIST_DIFF_LEGACY, blockHash));
1456+
// Delete all found legacy format entries
1457+
for (const auto& index : keys_to_erase) {
1458+
batch.Erase(std::make_pair(DB_LIST_DIFF_LEGACY, index->GetBlockHash()));
14361459

14371460
if (batch.SizeEstimate() >= (1 << 24)) {
1438-
LogPrintf("CDeterministicMNManager::%s -- Erasing legacy diffs...\n", __func__);
1461+
LogPrintf("CDeterministicMNManager::%s -- Erasing legacy diffs, height=%d...\n", __func__, index->nHeight);
14391462
m_evoDb.GetRawDB().WriteBatch(batch);
14401463
batch.Clear();
14411464
}
14421465
}
14431466

1444-
LogPrintf("CDeterministicMNManager::%s -- Erasing legacy diffs...\n", __func__);
1467+
// Delete all dangling legacy format entries
1468+
auto start{std::make_tuple(DB_LIST_DIFF_LEGACY, uint256())};
1469+
pcursor->Seek(start);
1470+
while (pcursor->Valid()) {
1471+
decltype(start) key;
1472+
if (!pcursor->GetKey(key) || std::get<0>(key) != DB_LIST_DIFF_LEGACY) {
1473+
break;
1474+
}
1475+
batch.Erase(std::make_pair(DB_LIST_DIFF_LEGACY, std::get<1>(key)));
1476+
pcursor->Next();
1477+
}
1478+
pcursor.reset();
1479+
1480+
LogPrintf("CDeterministicMNManager::%s -- Erasing legacy diffs, height=%d...\n", __func__, tip_index->nHeight);
14451481
m_evoDb.GetRawDB().WriteBatch(batch);
14461482
batch.Clear();
14471483

src/evo/deterministicmns.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ class CDeterministicMNManager
659659

660660
// Migration support for nVersion-first CDeterministicMNStateDiff format
661661
[[nodiscard]] bool IsMigrationRequired() const EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);
662-
[[nodiscard]] bool MigrateLegacyDiffs() EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);
662+
[[nodiscard]] bool MigrateLegacyDiffs(const CBlockIndex* const tip_index) EXCLUSIVE_LOCKS_REQUIRED(!cs, ::cs_main);
663663

664664
private:
665665
void CleanupCache(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);

src/evo/dmnstate.h

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -350,42 +350,33 @@ class CDeterministicMNStateDiffLegacy
350350
CDeterministicMNStateDiffLegacy(deserialize_type, Stream& s)
351351
{
352352
s >> *this;
353-
if ((fields & LegacyField_pubKeyOperator) || (fields & LegacyField_netInfo)) {
354-
// pubKeyOperator and netInfo need nVersion
355-
fields |= LegacyField_nVersion;
356-
}
357353
}
358354

359355
// Deserialize using legacy format
360356
SERIALIZE_METHODS(CDeterministicMNStateDiffLegacy, obj)
361357
{
362358
READWRITE(VARINT(obj.fields));
363359

364-
bool read_pubkey{false};
365360
boost::hana::for_each(legacy_members, [&](auto&& member) {
366361
using BaseType = std::decay_t<decltype(member)>;
367362
if constexpr (BaseType::mask == LegacyField_pubKeyOperator) {
368363
if (obj.fields & member.mask) {
369-
SER_READ(obj, read_pubkey = true);
364+
// We'll set proper scheme later in MigrateLegacyDiffs()
370365
READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast<CBLSLazyPublicKey&>(obj.state.pubKeyOperator),
371-
obj.state.nVersion == ProTxVersion::LegacyBLS));
366+
/*legacy=*/true));
372367
}
373368
} else if constexpr (BaseType::mask == LegacyField_netInfo) {
374369
if (obj.fields & member.mask) {
375370
// Legacy format supports non-extended addresses only
376-
READWRITE(NetInfoSerWrapper(const_cast<std::shared_ptr<NetInfoInterface>&>(obj.state.netInfo), false));
371+
READWRITE(NetInfoSerWrapper(const_cast<std::shared_ptr<NetInfoInterface>&>(obj.state.netInfo),
372+
/*is_extended=*/false));
377373
}
378374
} else {
379375
if (obj.fields & member.mask) {
380376
READWRITE(member.get(obj.state));
381377
}
382378
}
383379
});
384-
385-
if (read_pubkey) {
386-
SER_READ(obj, obj.fields |= LegacyField_nVersion);
387-
SER_READ(obj, obj.state.pubKeyOperator.SetLegacy(obj.state.nVersion == ProTxVersion::LegacyBLS));
388-
}
389380
}
390381

391382
// Convert to new format

src/node/chainstate.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
193193
}
194194

195195
// Check if nVersion-first migration is needed and perform it
196-
if (dmnman->IsMigrationRequired() && !dmnman->MigrateLegacyDiffs()) {
196+
if (dmnman->IsMigrationRequired() && !dmnman->MigrateLegacyDiffs(chainman.ActiveChainstate().m_chain.Tip())) {
197197
return ChainstateLoadingError::ERROR_UPGRADING_EVO_DB;
198198
}
199199

0 commit comments

Comments
 (0)