Skip to content

Commit

Permalink
Merge pull request #798 from dahlia/block-completion
Browse files Browse the repository at this point in the history
Restore BlockCompletion<T> (#707) and more
  • Loading branch information
dahlia committed Feb 24, 2020
2 parents aea1682 + 2a13ae7 commit 93fd48b
Show file tree
Hide file tree
Showing 23 changed files with 2,034 additions and 387 deletions.
37 changes: 29 additions & 8 deletions CHANGES.md
Expand Up @@ -8,14 +8,24 @@ To be released.

### Backward-incompatible API changes

- `BaseStore` class became to implement `IDisposable`. [[#789]]

### Backward-incompatible network protocol changes

- `BaseStore` class became to implement `IDisposable`. [[#789]]
- The existing `BlockHashes` message type (with the type number `0x05`) was
replaced by a new `BlockHashes` message type (with type number `0x0e`)
in order to include an offset block index besides block hashes
so that a receiver is able to determine their block indices too.
[[#707], [#798]]

### Backward-incompatible storage format changes

### Added APIs

- Added `BlockHashDownloadState` class, a subclass of `PreloadState`.
[[#707], [#798]]
- Added `BlockVerificationState` class, a subclass of `PreloadState`.
[[#798]]
- Added `BlockDigest` struct. [[#785]]
- Added `BlockHeader` struct. [[#785]]
- Added `IStore.GetBlockDigest(HashDigest<SHA256>)` method. [[#785]]
Expand All @@ -25,22 +35,33 @@ To be released.

- `BlockChain.MineBlock()` method became to ignore transactions having
lower nonce than the expected nonce in the chain. [[#791]]
- `Swarm<T>.PreloadAsync()` and `Swarm<T>.StartAsync()` became to download
only a list of block hashes first and then download blocks from
simultaneously multiple peers. [[#707], [#798]]

### Bug fixes

- `Swarm<T>` became not to sync the same `Block<T>`s or `Transaction<T>`s
multiple times. [[#784]]
- Fixed a `Swarm<T>`'s bug that had broadcasted a message to its source peer when
the number of peers is not enough (less than the minimum number). [[#788]]
- Fixed a `Swarm<T>`'s bug that had broadcasted a message to its source peer
when the number of peers is not enough (less than the minimum number).
[[#788]]
- Fixed a bug where `BlockChain.MineBlock()` had produced an invalid block
when there is any staged transaction which has lower nonce than the expected nonce,
that means, shares an already taken nonce by the same signer. [[#791]]

when there is any staged transaction which has lower nonce than the expected
nonce, that means, shares an already taken nonce by the same signer.
[[#791]]
- Fixed a `Swarm<T>.PreloadAsync()` method's bug that temporary chain IDs
in the store had been completely cleaned up in some corner cases
if `cancellationToken` was requested. [[#798]]

[#707]: https://github.com/planetarium/libplanet/pull/707
[#784]: https://github.com/planetarium/libplanet/pull/784
[#785]: https://github.com/planetarium/libplanet/pull/785
[#788]: https://github.com/planetarium/libplanet/pull/788
[#789]: https://github.com/planetarium/libplanet/pull/789
[#791]: https://github.com/planetarium/libplanet/pull/791
[#798]: https://github.com/planetarium/libplanet/pull/798


Version 0.8.0
-------------
Expand Down Expand Up @@ -133,8 +154,8 @@ Released on February 4, 2020.
- Added `BlockChain<T>.Genesis` property. [[#688]]
- Added `BlockChain<T>.MakeGenesisBlock()` static method. [[#688]]
- Added `InvalidGenesisBlockException` class. [[#688]]
- Added `StateDownloadState` class which reports state preloading iteration
progress. [[#703]]
- Added `StateDownloadState` class, a subclass of `PreloadState`,
which reports state preloading iteration progress. [[#703]]
- Added `PeerDiscoveryException` class which inherits `SwarmException`
class. [[#604], [#726]]
- Added `Swarm<T>.Peers` property which returns an enumerable of peers in
Expand Down
55 changes: 30 additions & 25 deletions Libplanet.Tests/Blockchain/BlockChainTest.cs
Expand Up @@ -598,33 +598,36 @@ public void ExecuteActions()
[Fact]
public async void FindNextHashes()
{
Assert.Single(_blockChain.FindNextHashes(
new BlockLocator(new HashDigest<SHA256>[] { })
));
long? offsetIndex;
IReadOnlyList<HashDigest<SHA256>> hashes;

_blockChain.FindNextHashes(new BlockLocator(new HashDigest<SHA256>[] { }))
.Deconstruct(out offsetIndex, out hashes);
Assert.Single(hashes);
var block0 = _blockChain.Genesis;
var block1 = await _blockChain.MineBlock(_fx.Address1);
var block2 = await _blockChain.MineBlock(_fx.Address1);
var block3 = await _blockChain.MineBlock(_fx.Address1);

Assert.Equal(
new[] { block0.Hash, block1.Hash, block2.Hash, block3.Hash, },
_blockChain.FindNextHashes(
new BlockLocator(new[] { block0.Hash })));
Assert.Equal(
new[] { block1.Hash, block2.Hash, block3.Hash },
_blockChain.FindNextHashes(
new BlockLocator(new[] { block1.Hash, block0.Hash })));
Assert.Equal(
new[] { block0.Hash, block1.Hash, block2.Hash },
_blockChain.FindNextHashes(
new BlockLocator(new[] { block0.Hash }),
stop: block2.Hash));

Assert.Equal(
new[] { block0.Hash, block1.Hash },
_blockChain.FindNextHashes(
new BlockLocator(new[] { block0.Hash }),
count: 2));
_blockChain.FindNextHashes(new BlockLocator(new[] { block0.Hash }))
.Deconstruct(out offsetIndex, out hashes);
Assert.Equal(0, offsetIndex);
Assert.Equal(new[] { block0.Hash, block1.Hash, block2.Hash, block3.Hash }, hashes);

_blockChain.FindNextHashes(new BlockLocator(new[] { block1.Hash, block0.Hash }))
.Deconstruct(out offsetIndex, out hashes);
Assert.Equal(1, offsetIndex);
Assert.Equal(new[] { block1.Hash, block2.Hash, block3.Hash }, hashes);

_blockChain.FindNextHashes(new BlockLocator(new[] { block0.Hash }), stop: block2.Hash)
.Deconstruct(out offsetIndex, out hashes);
Assert.Equal(0, offsetIndex);
Assert.Equal(new[] { block0.Hash, block1.Hash, block2.Hash }, hashes);

_blockChain.FindNextHashes(new BlockLocator(new[] { block0.Hash }), count: 2)
.Deconstruct(out offsetIndex, out hashes);
Assert.Equal(0, offsetIndex);
Assert.Equal(new[] { block0.Hash, block1.Hash }, hashes);
}

[Fact]
Expand All @@ -638,8 +641,10 @@ public async void FindNextHashesAfterFork()
await forked.MineBlock(_fx.Address1);

BlockLocator locator = _blockChain.GetBlockLocator();
IEnumerable<HashDigest<SHA256>> hashes = forked.FindNextHashes(locator);
forked.FindNextHashes(locator)
.Deconstruct(out long? offset, out IReadOnlyList<HashDigest<SHA256>> hashes);

Assert.Equal(forked[0].Index, offset);
Assert.Equal(new[] { forked[0].Hash, forked[1].Hash }, hashes);
}

Expand Down Expand Up @@ -956,7 +961,7 @@ public void ForkTxNonce()
}

[Fact]
public async void CanGetBlockLocator()
public async void GetBlockLocator()
{
List<Block<DumbAction>> blocks = new List<Block<DumbAction>>();
foreach (var i in Enumerable.Range(0, 10))
Expand All @@ -965,7 +970,7 @@ public async void CanGetBlockLocator()
blocks.Add(block);
}

BlockLocator actual = _blockChain.GetBlockLocator(threshold: 2);
BlockLocator actual = _blockChain.GetBlockLocator(threshold: 3);
BlockLocator expected = new BlockLocator(new[]
{
blocks[9].Hash,
Expand Down
62 changes: 62 additions & 0 deletions Libplanet.Tests/Blockchain/BlockLocatorTest.cs
@@ -0,0 +1,62 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using Libplanet.Blockchain;
using NetMQ;
using Xunit;
using Xunit.Abstractions;

namespace Libplanet.Tests.Blockchain
{
public class BlockLocatorTest
{
private readonly ITestOutputHelper _output;

public BlockLocatorTest(ITestOutputHelper output)
{
_output = output;
}

[Fact]
public void Constructor()
{
// Generate fixture block hashes looks like 0x000...1, 0x000...2, 0x000...3, and so on,
// for the sake of easier debugging.
ImmutableArray<HashDigest<SHA256>> blocks = Enumerable.Range(0, 0x10).Select(i =>
{
byte[] bytes = NetworkOrderBitsConverter.GetBytes(i);
var buffer = new byte[HashDigest<SHA256>.Size];
bytes.CopyTo(buffer, buffer.Length - bytes.Length);
return new HashDigest<SHA256>(buffer);
}).ToImmutableArray();

var locator = new BlockLocator(
indexBlockHash: idx => blocks[(int)(idx < 0 ? blocks.Length + idx : idx)],
indexByBlockHash: hash => blocks.IndexOf(hash),
sampleAfter: 5
);

foreach (HashDigest<SHA256> hash in locator)
{
_output.WriteLine(hash.ToString());
}

Assert.Equal(
new[]
{
blocks[0xf],
blocks[0xe],
blocks[0xd],
blocks[0xc],
blocks[0xb],
blocks[0xa],
blocks[0x8],
blocks[0x4],
blocks[0x0],
},
locator
);
}
}
}
1 change: 1 addition & 0 deletions Libplanet.Tests/Libplanet.Tests.csproj
Expand Up @@ -33,6 +33,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AsyncEnumerator" Version="4.0.2" />
<PackageReference Include="Menees.Analyzers.2017" Version="2.0.3">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
57 changes: 57 additions & 0 deletions Libplanet.Tests/LoggerExtensions.cs
@@ -0,0 +1,57 @@
using System;
using Libplanet.Action;
using Libplanet.Blockchain;
using Serilog;
using Serilog.Events;

namespace Libplanet.Tests
{
public static class LoggerExtensions
{
public static void CompareBothChains<T>(
this ILogger logger,
LogEventLevel logLevel,
string labelA,
BlockChain<T> chainA,
string labelB,
BlockChain<T> chainB
)
where T : IAction, new()
{
if (chainA is null)
{
throw new ArgumentNullException(nameof(chainA));
}
else if (chainB is null)
{
throw new ArgumentNullException(nameof(chainB));
}

if (!logger.IsEnabled(logLevel))
{
return;
}

void Print(string i, string x, string y)
{
char bar = x.Equals(y) ? '|' : ':';
logger.Write(logLevel, $"{bar} {i,3} {bar} {x,-64} {bar} {y,-64} {bar}");
}

long aTipIdx = chainA.Tip.Index;
long bTipIdx = chainB.Tip.Index;
Print("Idx", $"{labelA} (tip: {aTipIdx})", $"{labelB} (tip: {bTipIdx})");
long tipIdx = Math.Max(aTipIdx, bTipIdx);
long idx = 0;
while (idx <= tipIdx)
{
Print(
$"#{idx}",
aTipIdx >= idx ? chainA[idx].ToString() : string.Empty,
bTipIdx >= idx ? chainB[idx].ToString() : string.Empty
);
idx++;
}
}
}
}

0 comments on commit 93fd48b

Please sign in to comment.