Skip to content

Commit

Permalink
Prevent GetDemandBlockHashes() from being hanged
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlia committed May 28, 2020
1 parent a25dc14 commit 2c23dbd
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ Version 0.9.3

To be released.

- Fixed a `Swarm<T>.PreloadAsync()` method's bug that had hanged in a state
downloading block hashes and finally unexpectedly terminated when a peer's
chain had gotten reorged. [[#880], [#884]]

[#880]: https://github.com/planetarium/libplanet/issues/880
[#884]: https://github.com/planetarium/libplanet/pull/884


Version 0.9.2
-------------
Expand Down
15 changes: 15 additions & 0 deletions Libplanet.Tests/ActionProgress.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;

namespace Libplanet.Tests
{
public sealed class ActionProgress<T> : IProgress<T>
{
private Action<T> _action;

public ActionProgress(Action<T> action) =>
_action = action ?? throw new ArgumentNullException(nameof(action));

public void Report(T value) =>
_action(value);
}
}
52 changes: 52 additions & 0 deletions Libplanet.Tests/Net/SwarmTest.Preload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1207,5 +1207,57 @@ public async void GetDemandBlockHashes()
.Select(b => (b.Index, b.Hash));
Assert.Equal(expectedBlocks, demands);
}

[Fact(Timeout = Timeout)]
public async void GetDemandBlockHashesDuringReorg()
{
Swarm<DumbAction> minerSwarm = _swarms[0];
Swarm<DumbAction> receiverSwarm = _swarms[1];
Log.Logger.Information("Miner: {0}", minerSwarm.Address);
Log.Logger.Information("Receiver: {0}", receiverSwarm.Address);

BlockChain<DumbAction> minerChain = _blockchains[0];
BlockChain<DumbAction> receiverChain = _blockchains[1];

Block<DumbAction>[] blocks =
(await MakeFixtureBlocksForPreloadAsyncCancellationTest()).Item2;

foreach (Block<DumbAction> block in blocks)
{
minerChain.Append(block);
}

BlockChain<DumbAction> forked = minerChain.Fork(minerChain.Genesis.Hash);
while (forked.Count <= minerChain.Count)
{
await forked.MineBlock(minerSwarm.Address);
}

minerSwarm.FindNextHashesChunkSize = 2;
await StartAsync(minerSwarm);

(BoundPeer, long?)[] peers =
{
((BoundPeer)minerSwarm.AsPeer, minerChain.Count - 1),
};

long receivedCount = 0;
(long, HashDigest<SHA256>)[] demands = await receiverSwarm.GetDemandBlockHashes(
receiverChain,
peers,
progress: new ActionProgress<PreloadState>(state =>
{
if (state is BlockHashDownloadState s &&
s.ReceivedBlockHashCount > minerChain.Count / 2)
{
receivedCount = s.ReceivedBlockHashCount;
minerChain.Swap(forked, render: false);
}
}),
cancellationToken: CancellationToken.None
).ToArrayAsync();

Assert.Equal(receivedCount, demands.LongLength);
}
}
}
12 changes: 12 additions & 0 deletions Libplanet/Net/Swarm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,8 @@ [EnumeratorCancellation] CancellationToken cancellationToken
i++;
long peerIndex = peerHeight ?? -1;

// FIXME: The following condition should be fixed together when the issue #459 is
// fixed. https://github.com/planetarium/libplanet/issues/459
if (peer is null || currentTipIndex >= peerIndex)
{
continue;
Expand All @@ -1066,8 +1068,18 @@ [EnumeratorCancellation] CancellationToken cancellationToken
try
{
var downloaded = new List<HashDigest<SHA256>>();
int previousDownloadedCount = -1;
int stagnant = 0;
const int stagnationLimit = 3;
while (downloaded.Count < totalBlocksToDownload)
{
if (previousDownloadedCount == downloaded.Count &&
++stagnant > stagnationLimit)
{
break;
}

previousDownloadedCount = downloaded.Count;
_logger.Verbose(
"Request block hashes to {Peer} (height: {PeerHeight}) using " +
"the locator {@Locator}... ({CurrentIndex}/{EstimatedTotalCount})",
Expand Down

0 comments on commit 2c23dbd

Please sign in to comment.