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

Clean up and add tests around beacon pointers #4035

Merged
merged 1 commit into from
May 21, 2022
Merged
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
91 changes: 58 additions & 33 deletions src/Nethermind/Nethermind.Blockchain/BlockTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -348,32 +348,28 @@ private bool BodyExists(long blockNumber, bool findBeacon = false)
return false;
}

private void LoadBestKnown(bool findBeacon = false)
private void LoadBestKnown()
{
long lowestInserted =
findBeacon ? (LowestInsertedBeaconHeader?.Number ?? 0) : (LowestInsertedHeader?.Number ?? 0);
long left = (Head?.Number ?? 0) == 0
? Math.Max(_syncConfig.PivotNumberParsed, lowestInserted) - 1
? Math.Max(_syncConfig.PivotNumberParsed, LowestInsertedHeader?.Number ?? 0) - 1
: Head.Number;

long right = Math.Max(0, left) + BestKnownSearchLimit;

long bestKnownNumberFound =
BinarySearchBlockNumber(1, left, LevelExists, findBeacon: findBeacon) ?? 0;
BinarySearchBlockNumber(1, left, LevelExists) ?? 0;
long bestKnownNumberAlternative =
BinarySearchBlockNumber(left, right, LevelExists, findBeacon: findBeacon) ?? 0;
BinarySearchBlockNumber(left, right, LevelExists) ?? 0;

long bestSuggestedHeaderNumber =
BinarySearchBlockNumber(1, left, HeaderExists, findBeacon: findBeacon) ?? 0;
BinarySearchBlockNumber(1, left, HeaderExists) ?? 0;
long bestSuggestedHeaderNumberAlternative
= BinarySearchBlockNumber(left, right, HeaderExists, findBeacon: findBeacon) ?? 0;
= BinarySearchBlockNumber(left, right, HeaderExists) ?? 0;

long? beaconPivotNumber = _metadataDb.Get(MetadataDbKeys.BeaconSyncPivotNumber)?.AsRlpValueContext().DecodeLong();
long leftForBody = (findBeacon && beaconPivotNumber.HasValue) ? beaconPivotNumber.Value : left;
long bestSuggestedBodyNumber
= BinarySearchBlockNumber(1, leftForBody, BodyExists, findBeacon: findBeacon) ?? 0;
= BinarySearchBlockNumber(1, left, BodyExists) ?? 0;
long bestSuggestedBodyNumberAlternative
= BinarySearchBlockNumber(leftForBody, right, BodyExists, findBeacon: findBeacon) ?? 0;
= BinarySearchBlockNumber(left, right, BodyExists) ?? 0;

if (_logger.IsInfo)
_logger.Info("Numbers resolved, " +
Expand Down Expand Up @@ -406,32 +402,61 @@ long bestSuggestedBodyNumberAlternative
$"best body: {bestSuggestedBodyNumber}|");
}
}

if (findBeacon)
{
BestKnownBeaconNumber = Math.Max(bestKnownNumberFound, bestKnownNumberAlternative);
BestSuggestedBeaconHeader = FindHeader(bestSuggestedHeaderNumber, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
BlockHeader? bestSuggestedBodyHeader = FindHeader(bestSuggestedBodyNumber, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
BestSuggestedBeaconBody = bestSuggestedBodyHeader is null
? null
: FindBlock(bestSuggestedBodyHeader.Hash, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
}
else
{
BestKnownNumber = Math.Max(bestKnownNumberFound, bestKnownNumberAlternative);
BestSuggestedHeader = FindHeader(bestSuggestedHeaderNumber, BlockTreeLookupOptions.None);
BlockHeader? bestSuggestedBodyHeader = FindHeader(bestSuggestedBodyNumber, BlockTreeLookupOptions.None);
BestSuggestedBody = bestSuggestedBodyHeader is null
? null
: FindBlock(bestSuggestedBodyHeader.Hash, BlockTreeLookupOptions.None);
}

BestKnownNumber = Math.Max(bestKnownNumberFound, bestKnownNumberAlternative);
BestSuggestedHeader = FindHeader(bestSuggestedHeaderNumber, BlockTreeLookupOptions.None);
BlockHeader? bestSuggestedBodyHeader = FindHeader(bestSuggestedBodyNumber, BlockTreeLookupOptions.None);
BestSuggestedBody = bestSuggestedBodyHeader is null
? null
: FindBlock(bestSuggestedBodyHeader.Hash, BlockTreeLookupOptions.None);
}

private void LoadBeaconBestKnown()
{
long left = Math.Max(Head?.Number ?? 0, (LowestInsertedBeaconHeader?.Number ?? 0) - 1);
long right = Math.Max(0, left) + BestKnownSearchLimit;

long bestKnownNumberFound = BinarySearchBlockNumber(left, right, LevelExists, findBeacon: true) ?? 0;
long bestBeaconHeaderNumber = BinarySearchBlockNumber(left, right, HeaderExists, findBeacon: true) ?? 0;

long? beaconPivotNumber = _metadataDb.Get(MetadataDbKeys.BeaconSyncPivotNumber)?.AsRlpValueContext().DecodeLong();
long bestBeaconBodyNumber = BinarySearchBlockNumber(
beaconPivotNumber.HasValue ? beaconPivotNumber.Value : left, right, BodyExists, findBeacon: true) ?? 0;

if (_logger.IsInfo)
_logger.Info("Finding beacon block best pointers");
LoadBestKnown(true);
_logger.Info("Beacon Numbers resolved, " +
$"level = {bestKnownNumberFound}, " +
$"header = {bestBeaconHeaderNumber}, " +
$"body = {bestBeaconBodyNumber}");

if (bestKnownNumberFound < 0 ||
bestBeaconHeaderNumber < 0 ||
bestBeaconBodyNumber < 0 ||
bestBeaconHeaderNumber < bestBeaconBodyNumber)
{
if (_logger.IsWarn)
_logger.Warn(
$"Detected corrupted block tree data ({bestBeaconHeaderNumber} < {bestBeaconBodyNumber}) (possibly due to an unexpected shutdown). Attempting to fix by moving head backwards. This may fail and you may need to resync the node.");
if (bestBeaconHeaderNumber < bestBeaconBodyNumber)
{
bestBeaconBodyNumber = bestBeaconHeaderNumber;
_tryToRecoverFromHeaderBelowBodyCorruption = true;
}
else
{
throw new InvalidDataException("Invalid initial block tree state loaded - " +
$"best known: {bestKnownNumberFound}|" +
$"best header: {bestBeaconHeaderNumber}|" +
$"best body: {bestBeaconBodyNumber}|");
}
}

BestKnownBeaconNumber = bestKnownNumberFound;
BestSuggestedBeaconHeader = FindHeader(bestBeaconHeaderNumber, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
BlockHeader? bestBeaconBodyHeader = FindHeader(bestBeaconBodyNumber, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
BestSuggestedBeaconBody = bestBeaconBodyHeader is null
? null
: FindBlock(bestBeaconBodyHeader.Hash, BlockTreeLookupOptions.TotalDifficultyNotNeeded);
}

private enum BinarySearchDirection
Expand Down
68 changes: 59 additions & 9 deletions src/Nethermind/Nethermind.Merge.Plugin.Test/BlockTreeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
using Nethermind.Blockchain.Synchronization;
using Nethermind.Core;
using Nethermind.Core.Test.Builders;
using Nethermind.Db;
using Nethermind.Db.Blooms;
using Nethermind.Int256;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.Synchronization;
using Nethermind.Serialization.Rlp;
using Nethermind.Specs;
using NUnit.Framework;

Expand Down Expand Up @@ -198,10 +200,19 @@ private void OnNewBestSuggestedBlock(object? sender, BlockEventArgs e)

public ScenarioBuilder InsertBeaconPivot(long num)
{
Block? beaconBlock = SyncedTree!.FindBlock(num, BlockTreeLookupOptions.None);
AddBlockResult insertResult = NotSyncedTree!.Insert(beaconBlock!, true,
Block? beaconBlock = SyncedTree.FindBlock(num, BlockTreeLookupOptions.None);
AddBlockResult insertResult = NotSyncedTree.Insert(beaconBlock!, true,
BlockTreeInsertOptions.BeaconBlockInsert);
Assert.AreEqual(AddBlockResult.Added, insertResult);
NotSyncedTreeBuilder.MetadataDb.Set(MetadataDbKeys.LowestInsertedBeaconHeaderHash, Rlp.Encode(beaconBlock!.Hash).Bytes);
NotSyncedTreeBuilder.MetadataDb.Set(MetadataDbKeys.BeaconSyncPivotNumber, Rlp.Encode(beaconBlock.Number ).Bytes);
return this;
}

public ScenarioBuilder ClearBeaconPivot()
{
NotSyncedTreeBuilder.MetadataDb.Delete(MetadataDbKeys.BeaconSyncPivotNumber);

return this;
}

Expand Down Expand Up @@ -320,6 +331,22 @@ public ScenarioBuilder AssertLowestInsertedBeaconHeader(long expected)
Console.WriteLine("LowestInsertedBeaconHeader:"+NotSyncedTree!.LowestInsertedBeaconHeader!.Number);
return this;
}

public ScenarioBuilder AssertBestBeaconHeader(long expected)
{
Assert.IsNotNull(NotSyncedTree);
Assert.IsNotNull(NotSyncedTree.BestSuggestedBeaconHeader);
Assert.AreEqual(expected, NotSyncedTree.BestSuggestedBeaconHeader?.Number);
return this;
}

public ScenarioBuilder AssertBestBeaconBody(long expected)
{
Assert.IsNotNull(NotSyncedTree);
Assert.IsNotNull(NotSyncedTree.BestSuggestedBeaconBody);
Assert.AreEqual(expected, NotSyncedTree.BestSuggestedBeaconBody?.Number);
return this;
}

public ScenarioBuilder AssertChainLevel(int startNumber, int finalNumber)
{
Expand Down Expand Up @@ -369,6 +396,8 @@ public void Best_pointers_are_set_on_restart_with_gap()
.WithBlockTrees(10, 20)
.InsertBeaconPivot(14)
.Restart()
.AssertBestBeaconBody(14)
.AssertBestBeaconHeader(14)
.AssertBestKnownNumber(9)
.AssertBestSuggestedHeader(9)
.AssertBestSuggestedBody(9);
Expand All @@ -382,7 +411,12 @@ public void pointers_are_set_on_restart_during_header_sync()
.InsertBeaconPivot(7)
.InsertHeaders(6, 6)
.Restart()
.AssertLowestInsertedBeaconHeader(6);
.AssertBestBeaconBody(7)
.AssertBestBeaconHeader(7)
.AssertLowestInsertedBeaconHeader(6)
.AssertBestKnownNumber(3)
.AssertBestSuggestedHeader(3)
.AssertBestSuggestedBody(3);
}

[Test]
Expand All @@ -393,20 +427,28 @@ public void pointers_are_set_on_restart_after_header_sync_finished()
.InsertBeaconPivot(7)
.InsertHeaders(4, 6)
.Restart()
.AssertLowestInsertedBeaconHeader(4);
.AssertBestBeaconBody(7)
.AssertBestBeaconHeader(7)
.AssertLowestInsertedBeaconHeader(4)
.AssertBestKnownNumber(3)
.AssertBestSuggestedHeader(3)
.AssertBestSuggestedBody(3);
}

[Test]
public void pointers_are_set_on_restart_during_filling_block_gap()
{
BlockTreeTestScenario.ScenarioBuilder scenario = BlockTreeTestScenario.GoesLikeThis()
BlockTreeTestScenario.ScenarioBuilder scenario = BlockTreeTestScenario.GoesLikeThis()
.WithBlockTrees(4, 10)
.InsertBeaconPivot(7)
.InsertHeaders(4, 6)
.SuggestBlocks(4, 4)
.Restart()
.AssertBestSuggestedBody(4)
.AssertLowestInsertedBeaconHeader(4);
.AssertBestBeaconBody(7)
.AssertBestBeaconHeader(7)
.AssertLowestInsertedBeaconHeader(4)
.AssertBestSuggestedHeader(4)
.AssertBestSuggestedBody(4);
}

[Test]
Expand All @@ -416,9 +458,14 @@ public void pointers_are_set_on_restart_after_filling_block_gap_finished()
.WithBlockTrees(4, 10)
.InsertBeaconPivot(7)
.InsertHeaders(4, 6)
.SuggestBlocks(4, 6)
.SuggestBlocks(4, 7)
.ClearBeaconPivot()
.Restart()
.AssertBestSuggestedBody(6)
.AssertBestBeaconBody(0)
.AssertBestBeaconHeader(0)
.AssertLowestInsertedBeaconHeader(4)
.AssertBestSuggestedHeader(7)
.AssertBestSuggestedBody(7)
.AssertLowestInsertedBeaconHeader(4);
}

Expand All @@ -431,6 +478,9 @@ public void Best_pointers_should_not_move_if_sync_is_not_finished()
.InsertHeaders(5, 6)
.InsertBeaconBlocks(7, 9)
.Restart()
.AssertBestBeaconBody(9)
.AssertBestBeaconHeader(9)
.AssertLowestInsertedBeaconHeader(5)
.AssertBestKnownNumber(3)
.AssertBestSuggestedHeader(3)
.AssertBestSuggestedBody(3);
Expand Down