Giter Club home page Giter Club logo

libplanet's Introduction

Libplanet

Discord Build Status (CircleCI) Codecov NuGet NuGet (prerelease)

Libplanet is a .NET library for creating multiplayer online game in decentralized fashion, which means the whole gameplay occurs on a peer-to-peer network among equal nodes rather than an authorized central server. Under the hood, it incorporates many features (e.g., digital signature, BFT consensus, data replication) of a blockchain.

It has competitive advantages over other solutions for decentralized gaming:

  • Embeddable: A game app does not have to communicate with another running process, hence it doesn't require extra marshaling or processes management. To draw a parallel, Libplanet is closer to SQLite than MySQL or PostgreSQL.

  • Isomorphic: Libplanet is a .NET library, so every game logic can be written in the same language, C#, and run on the blockchain. No glue code or "smart contracts" are needed.

  • Token-independent: Unlike almost every blockchain system, it does not force users to create and deal with yet-another-cryptocurrency. Your game can be free to play, and enjoyed by regular gamers.

To learn more about why Planetarium is creating technology for fully decentralized games, please refer to our blog post.

NuGet

For every stable release, we pack Libplanet into a .nupkg and upload it to NuGet and GitHub releases page. (You can find the changelog for versions from releases page.) To use Libplanet in your game, your project needs to add a dependency to Libplanet package. On Visual Studio IDE, run the following command in Package Manager Console:

Install-Package Libplanet

If you prefer dotnet CLI run the following command instead:

dotnet add package Libplanet

See also Microsoft's docs on different ways to install NuGet package.

In addition to stable releases, we also provide pre-release packages. For every day and every merge commit, it is packed into a .nupkg and uploaded to NuGet with a hyphen-suffixed version name.

For a merge commit build, a version name looks like 0.1.0-dev.20181231235959+a0b1c2d where 20181231235959 is a UTC timestamp of the build and a0b1c2d is the first 7 hexadecimals of the Git commit hash. For a daily build, a version name is like 0.1.0-nightly.20181231+a0b1c2d.

Unfortunately, Unity currently does not support NuGet. There are some Unity plug-ins to deal with NuGet package system, and these seem immature at present. To use Libplanet on Unity, you need to manually extract Libplanet.dll from Libplanet.*.nupkg file and place it inside of your Unity project. We are acknowledging the fact Libplanet is currently not very usable together with Unity, and promise to make it better in the next few minor releases. Until then, you could try MSBuildForUnity which is experimental as of January 2020.

Build

You could build Libplanet.dll and Libplanet.Stun.dll assemblies from the source code.

The following command installs dependencies (required library packages) and builds the whole Libplanet solution:

dotnet build

Note that dotnet command is distributed together with .NET Core SDK.

If you'd like to contribute code to the Libplanet project in earnest, please read our contributor guide.

libplanet's People

Contributors

akamig avatar andrewyavors avatar area363 avatar atralupus avatar chekkan avatar colibrishin avatar dahlia avatar earlbread avatar francescobonizzi avatar greymistcube avatar hardywood-k avatar hunkim98 avatar ipdae avatar jckdotim avatar kfangw avatar kijun avatar limebell avatar longfin avatar moreal avatar mu-hun avatar nebu1eto avatar oaroomy avatar onedgelee avatar qtuu avatar reenine99 avatar riemannulus avatar rozzaysred avatar s2quake avatar tkiapril avatar unengine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libplanet's Issues

Missing NuGet package metadata

After we've used nuget pack command instead of msbuild -t:pack task, package metadata described in Libplanet/Libplanet.csproj file seems ignored.

Block explorer

Although there has been Libplanet.Explorer project, it is virtually not usable. We need to make it actually usable by Libplanet developers and users.

  • Check if the existing Libplanet.Explorer builds.
  • Try to make it standalone. (Currently as it requires a concrete IAction implementation it cannot be a standalone executable but only a library.)

Turn uint to int in Blockchain/Block

Currently, we're using uint / ulong to represent Block.Index and other place that contextually negative numbers are not needed.

  • but In C# / .NET, these unsigned type are not compatible with common language specification(CPS) and aren't recommended for general use. (see also)
  • and, it causes unnecessary casting to make compatible with other API(e.g. LINQ).

So, I suggest changing these unsigned type to normal type in public interfaces.

@dahlia @ipdae @kijun any ideas?

SwarmTest.CanBroadcastWhlieMining test fails with IOException on Windows

See also https://travis-ci.com/dahlia/libplanet/jobs/187118894#L2315-L2322.

    System.IO.IOException : The process cannot access the file 'index' because it is being used by another process.
    Stack Trace:
         at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive, Boolean throwOnTopLevelDirectoryNotFound, WIN32_FIND_DATA& data)
         at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive, Boolean checkHost)
         at Libplanet.Tests.Store.FileStoreFixture.Dispose()
         at Libplanet.Tests.Net.SwarmTest.Dispose()
         at ReflectionAbstractionExtensions.DisposeTestClass(ITest test, Object testClass, IMessageBus messageBus, ExecutionTimer timer, CancellationTokenSource cancellationTokenSource)

Renaming the repository name to libplanet (no trailing “.net”)?

We do not have any plan to support or rewrite Libplanet in other languages than C# in the near future. To be consistent with the names for branding, NuGet package, namespace, and so on, I suggest rename the repository also to planetarium/libplanet, no “.net” postfix.

Replace BouncyCastle with System.Cryptography

Currently, we use BouncyCastle as cryptographic backend. when we introduced it for the first time, we considered the .NET Framework 3.5 as the lowest version, so it was an inevitable.

However, as the project's target moves to .NET standard 2.0, we need to review it again. since .NET Standard 1.6 seems to have support for elliptic curves, we might be able to use it instead of BouncyCastle. This has the following benefits:

  • We can reduce dependency on BouncyCastle.
  • System.Cryptography seems to use native DLL so it can show better performance.

Using BouncyCastle directly is the Libplanet.Crypto namespace, and I don't think it would be a big problem if unit tests for these namespaces pass.

Exceptions should be public too if it's thrown by any public interface

Currently, some exposed public APIs (e.g., BlockPolicy.ValidateBlocks()) return or throw non-public exception (e.g., InvalidBlockTimestampException).

If there is any public APIs that throw an exception that exception also should be the same visibility so that library users can catch them.

Pluggable PoW interface.

Note: it's suggestion about only Proof-of-work style interface (as is), not other consensus like PoA, DPoS.

We're using HashCash as consensus algorithm. although this is simple and easy to implement, it has the disadvantage that it can easily control the hash power through the existing ASICs.

So, we need to adjust interface to make it possible to change to different consensus algorithms (e.g. Ethash, Cuckoo) even if we keep the PoW style.

SwarmTest.CanBroadcastBlock test fails with InvalidOperationException

Libplanet.Tests.Net.SwarmTest.CanBroadcastBlock test fails with the following error:

Failed   Libplanet.Tests.Net.SwarmTest.CanBroadcastBlock
Error Message:
 System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
Stack Trace:
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.ValueCollection.Enumerator.MoveNext()
   at Libplanet.Net.Swarm.<StopAsync>d__50.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at Libplanet.Tests.Net.SwarmTest.<CanBroadcastBlock>d__17.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Standard Output Messages:
 20:48:58[@inproc://swarmtest.c/][6] - Trying to close dealers associated 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/.
 20:48:58[@inproc://swarmtest.c/][6] - Dealers associated 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/ were closed.
 20:48:58[@inproc://swarmtest.c/][6] - Trying to add peers...
 20:48:58[@inproc://swarmtest.c/][6] - 2/19/2019 8:48:57 PM +00:00 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/ > -0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/
 20:48:58[@inproc://swarmtest.c/][6] - The delta[[Sender: 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/, +: 0, -: 1]] has been applied.
 20:48:58[@inproc://swarmtest.b/][16] - Trying to close dealers associated 0xf76efC671CfD7bEE29217a52c8Ca62281559a165,inproc://swarmtest.c/.
 20:48:58[@inproc://swarmtest.b/][16] - Dealers associated 0xf76efC671CfD7bEE29217a52c8Ca62281559a165,inproc://swarmtest.c/ were closed.
 20:48:58[@inproc://swarmtest.b/][16] - Trying to add peers...
 20:48:58[@inproc://swarmtest.b/][16] - 2/19/2019 8:48:57 PM +00:00 0xf76efC671CfD7bEE29217a52c8Ca62281559a165,inproc://swarmtest.c/ > -0xf76efC671CfD7bEE29217a52c8Ca62281559a165,inproc://swarmtest.c/
 20:48:58[@inproc://swarmtest.b/][16] - The delta[[Sender: 0xf76efC671CfD7bEE29217a52c8Ca62281559a165,inproc://swarmtest.c/, +: 0, -: 1]] has been applied.
 20:48:58[@inproc://swarmtest.b/][16] - Trying to apply the delta[[Sender: 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/, +: 0, -: 1]]...
 20:48:58[@inproc://swarmtest.b/][16] - Trying to close dealers associated 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/.
 20:48:58[@inproc://swarmtest.b/][16] - Dealers associated 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/ were closed.
 20:48:58[@inproc://swarmtest.b/][16] - Trying to add peers...
 20:48:58[@inproc://swarmtest.b/][16] - 2/19/2019 8:48:57 PM +00:00 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/ > -0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/
 20:48:58[@inproc://swarmtest.b/][16] - The delta[[Sender: 0xd72Bb345Fb38C5F4B47B977CF5128C557a1553d9,inproc://swarmtest.a/, +: 0, -: 1]] has been applied.
 20:48:59[@inproc://swarmtest.b/][16] - Stopping...
 20:48:59[@inproc://swarmtest.b/][16] - Stopped.
 20:48:59[@inproc://swarmtest.c/][16] - Stopping...
 20:48:59[@inproc://swarmtest.c/][16] - Stopped.
 20:49:01[@inproc://swarmtest.b/][6] - Trying to distribute own delta (0)...
 20:49:01[@inproc://swarmtest.a/][19] - Trying to distribute own delta (0)...
 20:49:01[@inproc://swarmtest.c/][16] - Trying to distribute own delta (0)...
 20:49:01[@inproc://swarmtest.a/][12] - Trying to DialPeerAsync(0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/)...
 20:49:01[@inproc://swarmtest.a/][12] - Trying to DialAsync(inproc://swarmtest.b/)...
 20:49:01[@inproc://swarmtest.a/][12] - Trying to Ping to [inproc://swarmtest.b/]...
 20:49:01[@inproc://swarmtest.a/][12] - Waiting for Pong from [inproc://swarmtest.b/]...
 20:49:02[@inproc://swarmtest.b/][6] - Message[Libplanet.Net.Messages.Ping] received.
 20:49:02[@inproc://swarmtest.b/][6] - Ping received.
 20:49:02[@inproc://swarmtest.a/][13] - Pong received.
 20:49:02[@inproc://swarmtest.a/][13] - DialAsync(inproc://swarmtest.b/) is complete.
 20:49:02[@inproc://swarmtest.a/][13] - DialPeerAsync(0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/) is complete.
 20:49:02[@inproc://swarmtest.a/][13] - Trying to DialPeerAsync(0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/)...
 20:49:02[@inproc://swarmtest.a/][13] - Trying to DialAsync(inproc://swarmtest.c/)...
 20:49:02[@inproc://swarmtest.a/][13] - Trying to Ping to [inproc://swarmtest.c/]...
 20:49:02[@inproc://swarmtest.a/][13] - Waiting for Pong from [inproc://swarmtest.c/]...
 20:49:02[@inproc://swarmtest.c/][6] - Message[Libplanet.Net.Messages.Ping] received.
 20:49:02[@inproc://swarmtest.c/][6] - Ping received.
 20:49:02[@inproc://swarmtest.a/][12] - Pong received.
 20:49:02[@inproc://swarmtest.a/][12] - DialAsync(inproc://swarmtest.c/) is complete.
 20:49:02[@inproc://swarmtest.a/][12] - DialPeerAsync(0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/) is complete.
 20:49:02[@][12] - Waiting for a[0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/] to distribute... 
 20:49:03[@inproc://swarmtest.a/][16] - Trying to distribute own delta (2)...
 20:49:03[@inproc://swarmtest.a/][16] - Send the delta to dealers...
 20:49:03[@inproc://swarmtest.b/][9] - Trying to distribute own delta (0)...
 20:49:03[@inproc://swarmtest.c/][19] - Trying to distribute own delta (0)...
 20:49:03[@inproc://swarmtest.a/][16] - The delta has been sent.
 20:49:03[@][13] - Waiting for b[0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/] to receive a[0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/]...
 20:49:03[@][13] - Waiting for receive event... [0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/]
 20:49:03[@inproc://swarmtest.b/][9] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:03[@inproc://swarmtest.c/][6] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:03[@inproc://swarmtest.c/][6] - Received the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 3, -: 0]].
 20:49:03[@inproc://swarmtest.c/][6] - Trying to apply the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 3, -: 0]]...
 20:49:03[@inproc://swarmtest.b/][9] - Received the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 3, -: 0]].
 20:49:03[@inproc://swarmtest.b/][9] - Trying to apply the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 3, -: 0]]...
 20:49:03[@inproc://swarmtest.c/][6] - Trying to add peers...
 20:49:03[@inproc://swarmtest.b/][9] - Trying to add peers...
 20:49:03[@inproc://swarmtest.c/][6] - Trying to DialPeerAsync(0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/)...
 20:49:03[@inproc://swarmtest.b/][9] - Trying to DialPeerAsync(0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/)...
 20:49:03[@inproc://swarmtest.c/][6] - Trying to DialAsync(inproc://swarmtest.a/)...
 20:49:03[@inproc://swarmtest.c/][6] - Trying to Ping to [inproc://swarmtest.a/]...
 20:49:03[@inproc://swarmtest.b/][9] - Trying to DialAsync(inproc://swarmtest.c/)...
 20:49:03[@inproc://swarmtest.b/][9] - Trying to Ping to [inproc://swarmtest.c/]...
 20:49:03[@inproc://swarmtest.c/][6] - Waiting for Pong from [inproc://swarmtest.a/]...
 20:49:03[@inproc://swarmtest.b/][9] - Waiting for Pong from [inproc://swarmtest.c/]...
 20:49:03[@inproc://swarmtest.c/][17] - Message[Libplanet.Net.Messages.Ping] received.
 20:49:03[@inproc://swarmtest.c/][17] - Ping received.
 20:49:03[@inproc://swarmtest.a/][19] - Message[Libplanet.Net.Messages.Ping] received.
 20:49:03[@inproc://swarmtest.a/][19] - Ping received.
 20:49:03[@inproc://swarmtest.c/][9] - Pong received.
 20:49:03[@inproc://swarmtest.c/][9] - DialAsync(inproc://swarmtest.a/) is complete.
 20:49:03[@inproc://swarmtest.c/][9] - DialPeerAsync(0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/) is complete.
 20:49:03[@inproc://swarmtest.c/][9] - Trying to DialPeerAsync(0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/)...
 20:49:03[@inproc://swarmtest.c/][9] - Trying to DialAsync(inproc://swarmtest.b/)...
 20:49:03[@inproc://swarmtest.c/][9] - Trying to Ping to [inproc://swarmtest.b/]...
 20:49:03[@inproc://swarmtest.c/][9] - Waiting for Pong from [inproc://swarmtest.b/]...
 20:49:03[@inproc://swarmtest.b/][19] - Message[Libplanet.Net.Messages.Ping] received.
 20:49:03[@inproc://swarmtest.b/][19] - Ping received.
 20:49:03[@inproc://swarmtest.c/][6] - Pong received.
 20:49:03[@inproc://swarmtest.b/][15] - Pong received.
 20:49:03[@inproc://swarmtest.c/][6] - DialAsync(inproc://swarmtest.b/) is complete.
 20:49:03[@inproc://swarmtest.b/][15] - DialAsync(inproc://swarmtest.c/) is complete.
 20:49:03[@inproc://swarmtest.c/][6] - DialPeerAsync(0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/) is complete.
 20:49:03[@inproc://swarmtest.b/][15] - DialPeerAsync(0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/) is complete.
 20:49:03[@inproc://swarmtest.b/][15] - Trying to DialPeerAsync(0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/)...
 20:49:03[@inproc://swarmtest.c/][6] - 2/19/2019 8:49:03 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > +0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:03[@inproc://swarmtest.c/][6] - 2/19/2019 8:49:03 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > +0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:03[@inproc://swarmtest.c/][6] - 2/19/2019 8:49:03 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:03[@inproc://swarmtest.c/][6] - Trying to distribute own delta (2)...
 20:49:03[@inproc://swarmtest.c/][6] - Send the delta to dealers...
 20:49:03[@inproc://swarmtest.b/][15] - Trying to DialAsync(inproc://swarmtest.a/)...
 20:49:03[@inproc://swarmtest.b/][15] - Trying to Ping to [inproc://swarmtest.a/]...
 20:49:03[@inproc://swarmtest.b/][15] - Waiting for Pong from [inproc://swarmtest.a/]...
 20:49:03[@inproc://swarmtest.c/][6] - The delta has been sent.
 20:49:03[@inproc://swarmtest.c/][6] - The delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 3, -: 0]] has been applied.
 20:49:03[@inproc://swarmtest.a/][9] - Message[Libplanet.Net.Messages.Ping] received.
 20:49:03[@inproc://swarmtest.b/][16] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:03[@inproc://swarmtest.b/][16] - Received the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 2, -: 0]].
 20:49:03[@inproc://swarmtest.a/][16] - Ping received.
 20:49:03[@inproc://swarmtest.a/][9] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:03[@inproc://swarmtest.a/][9] - Received the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 2, -: 0]].
 20:49:03[@inproc://swarmtest.a/][9] - Trying to apply the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 2, -: 0]]...
 20:49:03[@inproc://swarmtest.b/][17] - Pong received.
 20:49:03[@inproc://swarmtest.b/][17] - DialAsync(inproc://swarmtest.a/) is complete.
 20:49:03[@inproc://swarmtest.b/][17] - DialPeerAsync(0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/) is complete.
 20:49:03[@inproc://swarmtest.b/][17] - 2/19/2019 8:49:03 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > +0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:03[@inproc://swarmtest.b/][17] - 2/19/2019 8:49:03 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > +0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:03[@inproc://swarmtest.b/][17] - 2/19/2019 8:49:03 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:03[@inproc://swarmtest.a/][9] - Trying to add peers...
 20:49:03[@inproc://swarmtest.b/][17] - Trying to distribute own delta (2)...
 20:49:03[@inproc://swarmtest.b/][17] - Send the delta to dealers...
 20:49:03[@inproc://swarmtest.a/][9] - 2/19/2019 8:49:03 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:03[@inproc://swarmtest.a/][9] - 2/19/2019 8:49:03 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:03[@inproc://swarmtest.a/][9] - The delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 2, -: 0]] has been applied.
 20:49:03[@inproc://swarmtest.b/][17] - The delta has been sent.
 20:49:03[@inproc://swarmtest.b/][17] - The delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 3, -: 0]] has been applied.
 20:49:03[@inproc://swarmtest.b/][17] - Trying to apply the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 2, -: 0]]...
 20:49:03[@inproc://swarmtest.b/][17] - Trying to add peers...
 20:49:03[@][12] - Event received. [0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/]
 20:49:03[@inproc://swarmtest.b/][17] - 2/19/2019 8:49:03 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:03[@][12] - Waiting for b[0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/] to distribute...
 20:49:03[@inproc://swarmtest.b/][17] - 2/19/2019 8:49:03 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:03[@inproc://swarmtest.b/][17] - The delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 2, -: 0]] has been applied.
 20:49:03[@][12] - Waiting for a[0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/] to receive b[0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/]...
 20:49:03[@][12] - Waiting for receive event... [0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/]
 20:49:03[@][12] - Event received. [0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/]
 20:49:03[@][12] - Waiting for a[0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/] to distribute... 
 20:49:03[@inproc://swarmtest.c/][9] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:03[@inproc://swarmtest.a/][6] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:03[@inproc://swarmtest.c/][9] - Received the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 2, -: 0]].
 20:49:03[@inproc://swarmtest.a/][6] - Received the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 2, -: 0]].
 20:49:03[@inproc://swarmtest.c/][9] - Trying to apply the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 2, -: 0]]...
 20:49:03[@inproc://swarmtest.a/][6] - Trying to apply the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 2, -: 0]]...
 20:49:03[@inproc://swarmtest.c/][9] - Trying to add peers...
 20:49:03[@inproc://swarmtest.a/][6] - Trying to add peers...
 20:49:03[@inproc://swarmtest.c/][9] - 2/19/2019 8:49:03 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:03[@inproc://swarmtest.c/][9] - 2/19/2019 8:49:03 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:03[@inproc://swarmtest.c/][9] - The delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 2, -: 0]] has been applied.
 20:49:03[@inproc://swarmtest.a/][6] - 2/19/2019 8:49:03 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:03[@inproc://swarmtest.a/][6] - 2/19/2019 8:49:03 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:03[@inproc://swarmtest.a/][6] - The delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 2, -: 0]] has been applied.
 20:49:04[@inproc://swarmtest.c/][9] - Trying to distribute own delta (0)...
 20:49:04[@inproc://swarmtest.b/][15] - Trying to distribute own delta (0)...
 20:49:04[@inproc://swarmtest.a/][17] - Trying to distribute own delta (0)...
 20:49:06[@inproc://swarmtest.b/][6] - Trying to distribute own delta (0)...
 20:49:06[@inproc://swarmtest.c/][16] - Trying to distribute own delta (0)...
 20:49:06[@inproc://swarmtest.a/][9] - Trying to distribute own delta (0)...
 20:49:07[@inproc://swarmtest.a/][6] - Trying to distribute own delta (0)...
 20:49:07[@inproc://swarmtest.c/][16] - Trying to distribute own delta (0)...
 20:49:07[@inproc://swarmtest.b/][18] - Trying to distribute own delta (0)...
 20:49:09[@inproc://swarmtest.c/][16] - Trying to distribute own delta (0)...
 20:49:09[@inproc://swarmtest.b/][18] - Trying to distribute own delta (0)...
 20:49:09[@inproc://swarmtest.a/][16] - Trying to distribute own delta (0)...
 20:49:10[@inproc://swarmtest.a/][18] - Trying to distribute own delta (0)...
 20:49:10[@inproc://swarmtest.b/][19] - Trying to distribute own delta (0)...
 20:49:10[@inproc://swarmtest.c/][18] - Trying to distribute own delta (0)...
 20:49:12[@inproc://swarmtest.c/][15] - Trying to distribute own delta (0)...
 20:49:12[@inproc://swarmtest.b/][15] - Trying to distribute own delta (0)...
 20:49:12[@inproc://swarmtest.a/][19] - Trying to distribute own delta (0)...
 20:49:13[@inproc://swarmtest.b/][16] - Trying to distribute own delta (0)...
 20:49:13[@inproc://swarmtest.a/][19] - Trying to distribute own delta (0)...
 20:49:13[@inproc://swarmtest.c/][6] - Trying to distribute own delta (0)...
 20:49:15[@inproc://swarmtest.a/][6] - Trying to distribute own delta (0)...
 20:49:15[@inproc://swarmtest.a/][6] - Send the delta to dealers...
 20:49:15[@inproc://swarmtest.c/][15] - Trying to distribute own delta (0)...
 20:49:15[@inproc://swarmtest.c/][15] - Send the delta to dealers...
 20:49:15[@inproc://swarmtest.b/][17] - Trying to distribute own delta (0)...
 20:49:15[@inproc://swarmtest.b/][17] - Send the delta to dealers...
 20:49:15[@inproc://swarmtest.c/][15] - The delta has been sent.
 20:49:15[@inproc://swarmtest.b/][17] - The delta has been sent.
 20:49:15[@inproc://swarmtest.a/][6] - The delta has been sent.
 20:49:15[@inproc://swarmtest.a/][6] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:15[@inproc://swarmtest.c/][19] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:15[@inproc://swarmtest.a/][17] - Received the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 0]].
 20:49:15[@inproc://swarmtest.a/][17] - Trying to apply the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 0]]...
 20:49:15[@inproc://swarmtest.a/][6] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:15[@inproc://swarmtest.a/][6] - Received the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 0]].
 20:49:15[@inproc://swarmtest.c/][6] - Received the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 0]].
 20:49:15[@inproc://swarmtest.c/][6] - Trying to apply the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 0]]...
 20:49:15[@inproc://swarmtest.a/][17] - Trying to add peers...
 20:49:15[@inproc://swarmtest.c/][6] - Trying to add peers...
 20:49:15[@inproc://swarmtest.a/][17] - 2/19/2019 8:49:15 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:15[@inproc://swarmtest.a/][17] - 2/19/2019 8:49:15 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:15[@inproc://swarmtest.a/][17] - The delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 0]] has been applied.
 20:49:15[@inproc://swarmtest.a/][17] - Trying to apply the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 0]]...
 20:49:15[@inproc://swarmtest.a/][17] - Trying to add peers...
 20:49:15[@inproc://swarmtest.a/][17] - 2/19/2019 8:49:15 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:15[@inproc://swarmtest.a/][17] - 2/19/2019 8:49:15 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:15[@inproc://swarmtest.a/][17] - The delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 0]] has been applied.
 20:49:15[@][13] - Waiting for b[0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/] to receive a[0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/]...
 20:49:15[@inproc://swarmtest.c/][19] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:15[@inproc://swarmtest.c/][19] - Received the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 0]].
 20:49:15[@inproc://swarmtest.b/][15] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:15[@inproc://swarmtest.b/][16] - Received the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 0]].
 20:49:15[@inproc://swarmtest.b/][16] - Trying to apply the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 0]]...
 20:49:15[@inproc://swarmtest.b/][15] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:15[@inproc://swarmtest.b/][16] - Trying to add peers...
 20:49:15[@inproc://swarmtest.b/][15] - Received the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 0]].
 20:49:15[@][13] - Waiting for receive event... [0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/]
 20:49:15[@inproc://swarmtest.b/][16] - 2/19/2019 8:49:15 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:15[@inproc://swarmtest.b/][16] - 2/19/2019 8:49:15 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:15[@inproc://swarmtest.b/][16] - The delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 0]] has been applied.
 20:49:15[@inproc://swarmtest.b/][16] - Trying to apply the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 0]]...
 20:49:15[@inproc://swarmtest.c/][6] - 2/19/2019 8:49:15 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:15[@inproc://swarmtest.c/][6] - 2/19/2019 8:49:15 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:15[@inproc://swarmtest.c/][6] - The delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 0]] has been applied.
 20:49:15[@inproc://swarmtest.c/][6] - Trying to apply the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 0]]...
 20:49:15[@inproc://swarmtest.b/][16] - Trying to add peers...
 20:49:15[@inproc://swarmtest.c/][6] - Trying to add peers...
 20:49:15[@inproc://swarmtest.b/][16] - 2/19/2019 8:49:15 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:15[@inproc://swarmtest.b/][16] - 2/19/2019 8:49:15 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:15[@inproc://swarmtest.b/][16] - The delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 0]] has been applied.
 20:49:15[@inproc://swarmtest.c/][6] - 2/19/2019 8:49:15 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:15[@inproc://swarmtest.c/][6] - 2/19/2019 8:49:15 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:15[@inproc://swarmtest.c/][6] - The delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 0]] has been applied.
 20:49:15[@][13] - Event received. [0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/]
 20:49:15[@][13] - Waiting for b[0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/] to distribute...
 20:49:15[@][13] - Waiting for a[0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/] to receive b[0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/]...
 20:49:15[@][13] - Waiting for receive event... [0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/]
 20:49:15[@][13] - Event received. [0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/]
 20:49:15[@inproc://swarmtest.b/][13] - Broadcast Blocks.
 20:49:15[@inproc://swarmtest.a/][17] - Message[Libplanet.Net.Messages.BlockHashes] received.
 20:49:15[@inproc://swarmtest.c/][16] - Message[Libplanet.Net.Messages.BlockHashes] received.
 20:49:15[@inproc://swarmtest.b/][6] - Message[Libplanet.Net.Messages.GetBlocks] received.
 20:49:15[@inproc://swarmtest.b/][6] - Message[Libplanet.Net.Messages.GetBlocks] received.
 20:49:15[@inproc://swarmtest.a/][21] - Received index is older than current chain's tip. ignored.
 20:49:15[@inproc://swarmtest.b/][19] - Message[Libplanet.Net.Messages.GetBlockHashes] received.
 20:49:16[@inproc://swarmtest.b/][9] - Message[Libplanet.Net.Messages.GetBlocks] received.
 20:49:16[@inproc://swarmtest.c/][6] - Received index is older than current chain's tip. ignored.
 20:49:16[@inproc://swarmtest.b/][6] - Trying to distribute own delta (0)...
 20:49:16[@inproc://swarmtest.c/][20] - Trying to distribute own delta (0)...
 20:49:16[@inproc://swarmtest.a/][22] - Trying to distribute own delta (0)...
 20:49:18[@inproc://swarmtest.a/][17] - Trying to distribute own delta (0)...
 20:49:18[@inproc://swarmtest.b/][17] - Trying to distribute own delta (0)...
 20:49:18[@inproc://swarmtest.c/][21] - Trying to distribute own delta (0)...
 20:49:19[@inproc://swarmtest.b/][16] - Trying to distribute own delta (0)...
 20:49:19[@inproc://swarmtest.c/][17] - Trying to distribute own delta (0)...
 20:49:19[@inproc://swarmtest.a/][16] - Trying to distribute own delta (0)...
 20:49:20[@inproc://swarmtest.a/][12] - Broadcast Blocks.
 20:49:20[@inproc://swarmtest.c/][17] - Message[Libplanet.Net.Messages.BlockHashes] received.
 20:49:20[@inproc://swarmtest.b/][21] - Message[Libplanet.Net.Messages.BlockHashes] received.
 20:49:20[@inproc://swarmtest.a/][20] - Message[Libplanet.Net.Messages.GetBlocks] received.
 20:49:20[@inproc://swarmtest.a/][20] - Message[Libplanet.Net.Messages.GetBlocks] received.
 20:49:20[@inproc://swarmtest.a/][20] - Message[Libplanet.Net.Messages.GetBlockHashes] received.
 20:49:20[@inproc://swarmtest.a/][20] - Message[Libplanet.Net.Messages.GetBlockHashes] received.
 20:49:21[@inproc://swarmtest.a/][22] - Message[Libplanet.Net.Messages.GetBlocks] received.
 20:49:21[@inproc://swarmtest.a/][22] - Message[Libplanet.Net.Messages.GetBlocks] received.
 20:49:21[@inproc://swarmtest.a/][21] - Trying to distribute own delta (0)...
 20:49:21[@inproc://swarmtest.b/][22] - Trying to distribute own delta (0)...
 20:49:21[@inproc://swarmtest.c/][21] - Trying to distribute own delta (0)...
 20:49:21[@inproc://swarmtest.c/][17] - Received index is older than current chain's tip. ignored.
 20:49:21[@inproc://swarmtest.b/][20] - Received index is older than current chain's tip. ignored.
 20:49:23[@inproc://swarmtest.b/][17] - Trying to distribute own delta (0)...
 20:49:23[@inproc://swarmtest.a/][17] - Trying to distribute own delta (0)...
 20:49:23[@inproc://swarmtest.c/][15] - Trying to distribute own delta (0)...
 20:49:24[@inproc://swarmtest.c/][15] - Trying to distribute own delta (0)...
 20:49:24[@inproc://swarmtest.a/][18] - Trying to distribute own delta (0)...
 20:49:24[@inproc://swarmtest.b/][16] - Trying to distribute own delta (0)...
 20:49:25[@inproc://swarmtest.a/][13] - Stopping...
 20:49:25[@inproc://swarmtest.a/][13] - Trying to distribute own delta (0)...
 20:49:25[@inproc://swarmtest.a/][13] - Send the delta to dealers...
 20:49:25[@inproc://swarmtest.a/][13] - The delta has been sent.
 20:49:25[@inproc://swarmtest.a/][13] - Stopped.
 20:49:25[@inproc://swarmtest.b/][13] - Stopping...
 20:49:25[@inproc://swarmtest.b/][13] - Trying to distribute own delta (0)...
 20:49:25[@inproc://swarmtest.b/][13] - Send the delta to dealers...
 20:49:25[@inproc://swarmtest.c/][13] - Stopping...
 20:49:25[@inproc://swarmtest.c/][13] - Trying to distribute own delta (0)...
 20:49:25[@inproc://swarmtest.c/][13] - Send the delta to dealers...
 20:49:25[@inproc://swarmtest.c/][18] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:25[@inproc://swarmtest.b/][16] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:25[@inproc://swarmtest.c/][18] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:25[@inproc://swarmtest.c/][18] - Received the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 1]].
 20:49:25[@inproc://swarmtest.c/][18] - Trying to apply the delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 1]]...
 20:49:25[@inproc://swarmtest.b/][16] - Message[Libplanet.Net.Messages.PeerSetDelta] received.
 20:49:25[@inproc://swarmtest.b/][16] - Received the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 1]].
 20:49:25[@inproc://swarmtest.b/][16] - Trying to apply the delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 1]]...
 20:49:25[@inproc://swarmtest.b/][9] - Received the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 1]].
 20:49:25[@inproc://swarmtest.c/][6] - Received the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 1]].
 20:49:26[@inproc://swarmtest.a/][19] - Stopping...
 20:49:26[@inproc://swarmtest.a/][19] - Stopped.
 20:49:26[@inproc://swarmtest.c/][19] - Trying to distribute own delta (0)...
 20:49:26[@inproc://swarmtest.b/][21] - Trying to distribute own delta (0)...
 20:49:26[@inproc://swarmtest.b/][12] - TimeoutException occured.
 20:49:26[@inproc://swarmtest.b/][12] - The delta has been sent.
 20:49:26[@inproc://swarmtest.b/][12] - Stopped.
 20:49:26[@inproc://swarmtest.b/][16] - Trying to close dealers associated 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/.
 20:49:26[@inproc://swarmtest.b/][16] - Dealers associated 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ were closed.
 20:49:26[@inproc://swarmtest.b/][16] - Trying to add peers...
 20:49:26[@inproc://swarmtest.b/][16] - 2/19/2019 8:49:25 PM +00:00 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/ > -0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/
 20:49:26[@inproc://swarmtest.b/][16] - The delta[[Sender: 0xCC7B600a9CaD130e0B226Abf82EFF4D623684b1d,inproc://swarmtest.c/, +: 0, -: 1]] has been applied.
 20:49:26[@inproc://swarmtest.b/][16] - Trying to apply the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 1]]...
 20:49:26[@inproc://swarmtest.b/][16] - Trying to close dealers associated 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/.
 20:49:26[@inproc://swarmtest.b/][16] - Dealers associated 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ were closed.
 20:49:26[@inproc://swarmtest.b/][16] - Trying to add peers...
 20:49:26[@inproc://swarmtest.b/][16] - 2/19/2019 8:49:25 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > -0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:26[@inproc://swarmtest.b/][16] - The delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 1]] has been applied.
 20:49:26[@inproc://swarmtest.c/][13] - TimeoutException occured.
 20:49:26[@inproc://swarmtest.c/][13] - The delta has been sent.
 20:49:26[@inproc://swarmtest.c/][18] - Trying to close dealers associated 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/.
 20:49:26[@inproc://swarmtest.c/][18] - Dealers associated 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ were closed.
 20:49:26[@inproc://swarmtest.c/][18] - Trying to add peers...
 20:49:26[@inproc://swarmtest.c/][18] - 2/19/2019 8:49:25 PM +00:00 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/ > -0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/
 20:49:26[@inproc://swarmtest.c/][18] - The delta[[Sender: 0xa987244619C15f4002EA1e8C04d2fa5766c44Ee0,inproc://swarmtest.b/, +: 0, -: 1]] has been applied.
 20:49:26[@inproc://swarmtest.c/][18] - Trying to apply the delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 1]]...
 20:49:26[@inproc://swarmtest.c/][18] - Trying to close dealers associated 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/.
 20:49:26[@inproc://swarmtest.c/][18] - Dealers associated 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ were closed.
 20:49:26[@inproc://swarmtest.c/][18] - Trying to add peers...
 20:49:26[@inproc://swarmtest.c/][18] - 2/19/2019 8:49:25 PM +00:00 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/ > -0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/
 20:49:26[@inproc://swarmtest.c/][18] - The delta[[Sender: 0x4d031021c19793062F9Ea8Ef3541Ca7953a302C3,inproc://swarmtest.a/, +: 0, -: 1]] has been applied.

Loosen constraint about Block.Timestamp

Currently we require that the generated block has a timestamp higher than the timestamp of the previous block. there are a few things to consider.

  • Block.Timestamp isn't always accurate because they are based on the local time of the system.
  • Additional things (like #64 ) are required for automated testing, since the latter block requires a later timestamp.

I think we'd better loosen this constraint like Ethereum or Bitcoin style.

  • In Bitcoin, a timestamp is accepted as valid if it is greater than the median timestamp of previous 11 blocks, and less than the network-adjusted time + 2 hours. (see also)
  • In Ethereum, a timestamp is accepted as valid if block.timestamp <= now + 900 and is block.timestamp >= parent.timestamp. (see also)

There are pros and cons, but I think the Ethereum style is more intuitive and easier to implement.

@dahlia @kijun @ipdae any ideas?

Publish to NuGet

Although Libplanet.csproj file already has metadata for NuGet, it hasn't been published yet. It should be published to NuGet when we release the first version (0.1.0), and it could be automated in the CI build.

Simplify Swarm interface

Swarm currently exposes mainly three methods:

  • InitAsync(): Initializes the internal objects needed for communication and connects to the registered peers.
  • RunAsync(): Executes event loops to process the message.
  • DisposeAsync(): Dispose the internal objects and requests the registered peers to delete it.

This design has the following problems.

  • RunAsync() can be called before InitContextAsync(). but it raises NoSwarmContextException.
  • it may leave Swarm in a strange state because RunAsync() does not cancel automatically after DisposeAsync().

So I suggest the following interface.

  • StartAsync()
    • Initializes the internal objects.
    • Connects to the registered peers.
    • Executes event loops to process the message.
  • StopAsync()
    • Disposes the internal objects.
    • Requests the registered peers to delete it.
    • Stops event loops.

Should check and warn if an action type is not annotated with ActionTypeAttribute

It currently crashes without any warning or meaningful error message if an action type is not annotated with ActionTypeAttribute. This behavior can confuse library users whether they are aware that an action should be annotated with the attribute and just miss it or not aware of it at all.

  • We should makeBlockchain<T> to throw a runtime exception (e.g., InvalidActionTypeException) with a well-guided error message if T is not annotated with the attribute.
  • We could write a small Roslyn Analyzer so that this situation is warned at compile-time.

(Reported by @ipdae.)

Pass a context (environment?) object to IAction.Execute() method

Some operations such as random generation can depend on information determined by a mined block, that a transaction belongs to. However, since simply passing a Block object to IAction.Execute() may accidentally cause unpredictable dependencies in the future, the information should be limited to only what we actually need at present.

A type for a such information unit can be named Context or Environment, I think? @kijun @longfin Any suggestions are welcome.

IAction.Execute(Address, AddressStateMap)'s signature also should be changed like:

AddressStateMap Execute(Address from, Address to, AddressStateMap states, IContext ctx);

The IContext interface would look like at first:

public interface IContext {
    System.Random Random { get; }
}

Change type of Peer.Urls to IPEndPoint

We've chosen Uri as element type of Peer.Urls to represent peer's address. but Uri isn't appropriate type because it also contains information that we do not need. (e.g. path, protocol)

So, I suggest IPEndPoint to peer's endpoint type. it has only IP address and port.

Submit Libplanet to Unity Asset Store

As our primary target platform is Unity, and Unity currently does not treat NuGet as a first-class citizen, we should pack Libplanet as a Unity asset package and submit it to Unity Asset Store. Ideally, it should be automatically submitted to Unity Asset Store every time we pushed a tag to the repository, in the similar way to NuGet (see also #52). First of all, we need some survey about:

  • legal compatibility between our license and the terms and EULA of Unity Asset Store
  • non-interactive way (i.e., non-GUI but CLI) to pack a Unity asset package
  • programmable way (API or CLI) to submit asset packages to Unity Asset Store

Guide to set up development envrionment

We need a minimum guide to set up .NET development environment on major platforms (macOS, Windows?).

  • IDE, or editor with extension set to install
  • How to build the assembly
  • How to run the tests
  • How to build the docs

Codecov submission occasionally fails due to network errors

During builds on Travis CI, submitting a coverage report to codecov.io occasionally fails due to network errors, e.g., HTTP2 framing layer. A quick fix to this seems adding a --retry option to the existing curl command. See .travis.yml file.

Travis CI builds haven't published docs to GitHub Pages

Docs/publish.sh, the script to publish docs, seems to have not executed by Travis CI. I guess it's because Travis CI executes only one instruction for each provider type. Note that we currently two provider: script instructions. One is publish.sh, and other one is Docs/publish.sh.

Add block height to `ActionContext`.

Add block height to ActionContext so that game logic can reference it.

My usecase for this it to make a game where a player is only permitted one action per block.

Actions should be able to know if it's “rehearsal mode” during execution

The previously merged patch #121 introduced the concept of “rehearsal mode” (although I named it so right now) in order to automatically determine the Transaction<T>.UpdatedAddresses property. However, as this mode does not provide any PreviousStates and mocks Random and BlockIndex (which is set to 0 during that mode), it tends to lead two quite different consequences for both modes. Although the logical structure of an action should be consistent whether it's rehearsal or not, in some cases we need to make conditions depending on the mode.

In my opinion, the easiest way to implement would be to add a Boolean property like IActionContext.Rehearsal.

Asynchronous IStore methods

Currently methods of IStore are all synchronous. Although it's enough to FileStore, the only implementation at this time, there could be medium that requires to be asynchronous, e.g., relational databases, network file systems.

Replace DateTime with DateTimeOffset

System.DateTime, which has been there since very first of .NET Framework, has a concept of Kind. This property could be one of three states:

  • Local
  • Unspecified → This is default.
  • Utc

It even does not hold any information about time zone even if its Kind is Local. According to the official docs:

In a time zone-aware application, you must rely on some external mechanism to determine the time zone in which a DateTime object was created. You could use a structure that wraps both the DateTime value and the TimeZoneInfo object that represents the DateTime value's time zone.

This leads us bug-prone. Instead of this archaic type, we'd better use System.DateTimeOffset instead. This type actually was born to be an alternative to the existing DateTime and indeed fixed the problem. According to the docs:

Because it exactly defines a date and time relative to UTC, the DateTimeOffset structure does not include a Kind member, as the DateTime structure does.

Note that it is not a pair of date time and time zone, but merely a UTC assuming date time:

Although a DateTimeOffset value includes an offset, it is not a fully time zone-aware data structure. While an offset from UTC is one characteristic of a time zone, it does not unambiguously identify a time zone. Not only do multiple time zones share the same offset from UTC, but the offset of a single time zone changes if it observes daylight saving time. This means that, as soon as a DateTimeOffset value is disassociated from its time zone, it can no longer be unambiguously linked back to its original time zone.

This might be bad for end-user-facing applications, for us this makes things rather simpler.

Although it is relatively newer than DateTime, it still has been in .NET Framework since the version 2.0 and is part of .NET Standard 1.0 as well.

Miners can fabricate transactions (i.e., replay attack) at some degree

Currently miners can fabricate transactions signed by others by replicating them.

Suppose we have a transaction t from to having an action of “ transferring $1 to ” like:

AddressStateMap Execute(IActionContext context)
{
    return (AddressStateMap)context.PreviousStates.SetItem(
        context.To,
        ((int)context.PreviousStates[context.To]) + 1
    ).SetItem(
        context.From,
        ((int)context.PreviousStates[context.From]) - 1
    );
}

In this scenario, if a miner shares incentive with to get more dollars they can replicate t and mine multiple blocks including the transaction, and can more than $1. The current implementation does not find this kind of abusing invalid, because contents of a transaction and the signature derived from it is not affected by which block it belongs to.

In order to prevent this, we may need to add Nonce property referring the previous Block<T>.Hash to Transaction<T>.

Exceptions thrown by actions during rehearsal mode should be wrapped

As I explained in the issue #131, rehearsal mode tends to lead an action to reach an unexpected case. To a library user, it is likely to seem like it's their fault (I mean, it looks like the action won't work without rehearsal mode too, which is not necessarily so).

So that users can distinguishes errors due to rehearsal mode from other general errors, exceptions thrown by actions during that mode should be wrapped, like UnexpectedlyTerminatedTxRehearsalException with an inner exception.

Rename "miner" to "block producer"

For future pluggable consensus architecture which could include PoA or PoS, "mining" might not exactly represent how blocks are created. I suggest using terms "produce block/block producer" before renaming becomes too hard.

Single node configuration for development environment

Currently, the only way to correctly generate a block is to call Blockchain.MineBlock() to mine block. However, in a development environment that typically consists of a single node, mining has a large variation in resource usage, making it difficult to guarantee the block creation time. it sometimes makes development and testing difficult.

So for a development or test chain, we need a method (e.g. configuration) that allows us to create a valid block without these mining processes.

Replace AddressStateMap with IImmutableDictionary<Address, object>

The current implementation of AddressStateMap is just a thin wrapper around ImmutableDictionary<Address, object>, and implements IImmutableDictionary<Address, object> as well at a time. This was intended to mimic typedef IImmutableDictionary<Address, object> AddresStateMap; in C#. (Although C# allows using AddresStateMap = IImmutable<Address, object>;, the alias is scoped to only a file.) However, as AddressStateMap is not completely equivalent to IImmutableDictionary<Address, object> but only a subtype of it, it tends to cast things up and down, which leads runtime type unsafety. For instance, when we implement a IAction.Execute() method, it looks like:

public AddressStateMap Execute(IActionContext context)
{
    AddressStateMap states = context.PreviousStates;
    IImmutableDictionary<Address, object> newStates =
        states.SetItem(context.To, "new value");
    return (AddressStateMap) newStates;  // downcast
}

If we completely replace AddressStateMap with IImmutableDictionary<Address, object> it would look like instead:

public AddressStateMap Execute(IActionContext context)
{
    IImmutableDictionary<Address, object> states = context.PreviousStates;
    IImmutableDictionary<Address, object> newStates =
        states.SetItem(context.To, "new value");
    return newStates;  // no cast
}

Add project features/philosophy to README

Currently our README says libplanet is a blockchain networking project for fully decentralized games, but not much more. We should add our distinguishing features and philosophies to provide more information about the project.

Asynchronous Blockchain.MineBlock()

In Unity Engine the current block mining API blocks (serializes the flow) so that the event loop cannot switch the context to other cooperative tasks. We should prevent it by providing an async interface of Blockchain.MineBlock().

Would TxId be better if it inherits HashDigest<SHA256>?

Since TxId is a SHA-256 digest, the most of things are in common with HashDigest<SHA256>. Would TxId be better to inherit HashDigest<256>? Or, rather, would it be fine even if we get rid of TxId at all and replace them with HashDigest<256>?

Formula to determine required mining difficulty of each block

The current implementation of ExpectDifficulties() function determines each block's required difficulty, and it depends on timestamp interval between B and Bʹ, where Bʹ is a previous block of B. If an interval between B and Bʹ is shorter than 5 seconds it means the current difficulty is too easy, so raises it up. If longer it's too difficult, so drops it.

This current way has several flaws:

  1. Block timestamps that the formula depends on are fixed before they start to be mined. That means, we actually cannot measure the time taken for mining by seeing timestamps, so the current behavior to balance mining difficulties has not been working. We may be possible to fix it by averaging previous intervals.

  2. Block timestamps fluctuate and are untrustworthy. Miners could fabricate block timestamps to have more rewards than they deserve. At present, I have no idea to solve this. We need to research about this.

Provide API to do post-process right after a certain action executed

In games that currently use libplanet, there is no way to know created action has been processed.
so, in the game, we waiting for process has finished like this.
However, this can cause the game to hang if the block containing the Tx is not included in the block chain.
In my opinion, if libplanet provides an event based on the result of the process, the game will be handling the internal state change as event-driven and solved the problem.

Turn Peer.Endpoints to Peer.Endpoint

We've assumed that Peer can have multiple endpoints to publish its own network address. but in #73 and #117 , we planed to using TURN to relay server, and using its relayed address to peer's connectable address. With this plan, it is more natural to assume that Peer has the only one EndPoint to which it can accept other peer.

Connecting P2P over NAT

Currently, the implementation of P2P through Swarm assumes that the peer can connect via TCP with public IP. however, clients targeted by libplanet are not directly connected to the Internet with such public IP, but are often connected through NAT. In such a connection method, it is often difficult for one peer to directly access another peer.

Using techniques such as UDP hole punching, it is necessary to support natural connections in these environments.

Mixed-case checksum address encoding

The current Address object provides hexadecimal formatting, but it always returns only lowercase letters. Since we are following the same address scheme to Ethereum by design, we should follow EIP 55 as well.

This task consists of two subtasks:

  • Making the formatting method to return a mixed-case EIP 55 string. → #43
  • Adding a new constructor to take a hexadecimal address string. If a string is mixed-case and the checksum is invalid it should throw an exception. → #53

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.