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

Download trusted states from a likely branchpoint #481

Merged
merged 1 commit into from
Sep 2, 2019
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
10 changes: 10 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ To be released.

### Behavioral changes

- `Swarm<T>.PreloadAsync()` method became to download precalculated states
of blocks from a likely branchpoint instead of a genesis block from
a trusted peer (i.e., `trustedStateValidators`) where there are branches
between peers. [[#465], [#481]]
- `Swarm<T>`'s internal `GetRecentStates` message became to take
`BlockLocator`, an internal data type to approximates a path of
a chain of blocks for heuristics to search a likely branchpoint,
instead of `HashDigest<SHA256>`. [[#465], [#481]]

### Bug fixes

- Fixed a bug that `Swarm<T>` hadn't released its TURN related resources on
Expand All @@ -41,6 +50,7 @@ To be released.
[#420]: https://github.com/planetarium/libplanet/pull/420
[#450]: https://github.com/planetarium/libplanet/pull/450
[#470]: https://github.com/planetarium/libplanet/pull/470
[#481]: https://github.com/planetarium/libplanet/pull/481


Version 0.5.2
Expand Down
64 changes: 59 additions & 5 deletions Libplanet.Tests/Net/SwarmTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ public async Task PreloadWithTrustedPeers(bool trust)

HashDigest<SHA256>? deepBlockHash = null;

for (int i = 0; i < 2; i++)
for (int i = 0; i < 3; i++)
{
int j = 0;
Block<DumbAction> block = null;
Expand All @@ -1106,7 +1106,7 @@ public async Task PreloadWithTrustedPeers(bool trust)
j++;
}

if (i < 1)
if (i < 2)
{
deepBlockHash = block?.Hash;
}
Expand Down Expand Up @@ -1142,7 +1142,7 @@ public async Task PreloadWithTrustedPeers(bool trust)
);
Assert.Single(states);
Assert.Equal(
$"({chainType}) Item0.{i},Item1.{i}",
$"({chainType}) Item0.{i},Item1.{i},Item2.{i}",
$"({chainType}) {states[target]}"
);
}
Expand All @@ -1163,7 +1163,7 @@ AddressStateMap TryToGetDeepStates() => receiverChain.GetStates(
{
var deepStates = TryToGetDeepStates();
Assert.Single(deepStates);
Assert.Equal($"Item0.{i}", deepStates[target]);
Assert.Equal($"Item0.{i},Item1.{i}", deepStates[target]);
}

i++;
Expand All @@ -1175,7 +1175,7 @@ AddressStateMap TryToGetDeepStates() => receiverChain.GetStates(
new[] { minerSwarm.Address },
completeStates: false);
Assert.Single(minerState);
Assert.Equal(20, minerState[minerSwarm.Address]);
Assert.Equal(30, minerState[minerSwarm.Address]);
}
}
finally
Expand All @@ -1186,6 +1186,60 @@ AddressStateMap TryToGetDeepStates() => receiverChain.GetStates(
}
}

[Fact]
public async Task PreloadWithBranchesAndTrustedPeers()
{
// Two miners; one trusts other one. Test if it downloads the minimum range of blocks
// (instead of the entire chain from the genesis to the tip) when there are branches.
// See also: https://github.com/planetarium/libplanet/issues/465#issuecomment-525682219
Swarm<DumbAction> senderSwarm = _swarms[0];
Swarm<DumbAction> receiverSwarm = _swarms[1];

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

Block<DumbAction> g = TestUtils.MineGenesis<DumbAction>(senderSwarm.Address),
bp = TestUtils.MineNext(g, difficulty: 1024),
b2send = TestUtils.MineNext(bp, difficulty: 1024),
b2recv = TestUtils.MineNext(bp, difficulty: 1024),
b3 = TestUtils.MineNext(b2send, difficulty: 1024);

senderChain.Append(g);
senderChain.Append(bp);
senderChain.Append(b2send);
senderChain.Append(b3);

receiverChain.Append(g);
receiverChain.Append(bp);
receiverChain.Append(b2recv);

var receivedBlockStates = new HashSet<HashDigest<SHA256>>();

try
{
await StartAsync(senderSwarm);
await receiverSwarm.AddPeersAsync(new[] { senderSwarm.AsPeer });
await receiverSwarm.PreloadAsync(
progress: new Progress<PreloadState>(state =>
{
if (state is BlockStateDownloadState s)
{
receivedBlockStates.Add(s.ReceivedBlockHash);
}
}),
trustedStateValidators: ImmutableHashSet<Address>.Empty.Add(senderSwarm.Address)
);
}
finally
{
await senderSwarm.StopAsync();
}

Assert.Equal(senderChain, receiverChain);
Assert.DoesNotContain(g.Hash, receivedBlockStates);
Assert.DoesNotContain(bp.Hash, receivedBlockStates);
}

[Theory]
[InlineData(0)]
[InlineData(50)]
Expand Down
16 changes: 8 additions & 8 deletions Libplanet/Net/Messages/GetRecentStates.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using Libplanet.Blockchain;
using NetMQ;

namespace Libplanet.Net.Messages
{
internal class GetRecentStates : Message
{
public GetRecentStates(HashDigest<SHA256>? @base, HashDigest<SHA256> target)
public GetRecentStates(BlockLocator baseLocator, HashDigest<SHA256> target)
{
BaseBlockHash = @base;
BaseLocator = baseLocator;
TargetBlockHash = target;
}

public GetRecentStates(NetMQFrame[] frames)
: this(
frames.Length > 1
? new HashDigest<SHA256>(frames[1].Buffer)
: (HashDigest<SHA256>?)null,
new BlockLocator(frames.Skip(1).Select(f => new HashDigest<SHA256>(f.Buffer))),
new HashDigest<SHA256>(frames[0].Buffer)
)
{
}

public HashDigest<SHA256>? BaseBlockHash { get; }
public BlockLocator BaseLocator { get; }

public HashDigest<SHA256> TargetBlockHash { get; }

Expand All @@ -33,9 +33,9 @@ protected override IEnumerable<NetMQFrame> DataFrames
get
{
yield return new NetMQFrame(TargetBlockHash.ToByteArray());
if (BaseBlockHash is HashDigest<SHA256> @base)
foreach (HashDigest<SHA256> hash in BaseLocator)
{
yield return new NetMQFrame(@base.ToByteArray());
yield return new NetMQFrame(hash.ToByteArray());
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions Libplanet/Net/Swarm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ await DialToExistingPeers(cancellationToken).Select(pp =>
}

Block<T> initialTip = _blockChain.Tip;
BlockLocator initialLocator = _blockChain.GetBlockLocator();

// As preloading takes long, the blockchain data can corrupt if a program suddenly
// terminates during preloading is going on. In order to make preloading done
Expand Down Expand Up @@ -639,7 +640,7 @@ await SyncBehindsBlocksFromPeersAsync(
workspace,
progress,
trustedPeersWithTip.ToImmutableList(),
initialTip?.Hash,
initialLocator,
cancellationToken
);

Expand Down Expand Up @@ -991,7 +992,7 @@ private async Task<bool> SyncRecentStatesFromTrustedPeersAsync(
BlockChain<T> blockChain,
IProgress<PreloadState> progress,
IReadOnlyList<(Peer, HashDigest<SHA256>)> trustedPeersWithTip,
HashDigest<SHA256>? baseBlockHash,
BlockLocator baseLocator,
CancellationToken cancellationToken)
{
_logger.Debug(
Expand All @@ -1001,7 +1002,7 @@ private async Task<bool> SyncRecentStatesFromTrustedPeersAsync(
foreach ((Peer peer, var blockHash) in trustedPeersWithTip)
{
cancellationToken.ThrowIfCancellationRequested();
var request = new GetRecentStates(baseBlockHash, blockHash);
var request = new GetRecentStates(baseLocator, blockHash);
_logger.Debug("Makes a dealer socket to a trusted peer ({0}).", peer);
using (var socket = new DealerSocket(ToNetMQAddress(peer)))
{
Expand Down Expand Up @@ -1663,7 +1664,8 @@ private void TransferBlocks(GetBlocks getData)

private void TransferRecentStates(GetRecentStates getRecentStates)
{
HashDigest<SHA256>? @base = getRecentStates.BaseBlockHash;
BlockLocator baseLocator = getRecentStates.BaseLocator;
HashDigest<SHA256> @base = _blockChain.FindBranchPoint(baseLocator);
HashDigest<SHA256> target = getRecentStates.TargetBlockHash;
IImmutableDictionary<HashDigest<SHA256>,
IImmutableDictionary<Address, object>
Expand Down