From 3d512c8f61151d004d6fd8271cbc44d0f97c5177 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Mon, 1 Apr 2019 15:35:34 +0900 Subject: [PATCH 1/2] Change type of Peer.EndPoint to DnsEndPoint --- CHANGES.md | 7 +-- Libplanet.Tests/Net/PeerSetDeltaTest.cs | 6 +-- Libplanet.Tests/Net/PeerTest.cs | 2 +- Libplanet.Tests/Net/SwarmTest.cs | 42 +++++++-------- Libplanet/Net/Peer.cs | 14 ++--- Libplanet/Net/Swarm.cs | 70 ++++++++++++++++--------- 6 files changed, 82 insertions(+), 59 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 32db528699a..e22e2159234 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -149,8 +149,8 @@ To be released. - Since we decided to depend on TURN ([RFC 5766]) and STUN ([RFC 5389]) to work around NAT so that `Peer`'s endpoints don't have to be multiple, `Peer.Urls` was renamed to `Peer.EndPoint` and its type also was changed - from `IImmutableList` to `IPEndPoint`. - [[#120], [#123] by Yang Chun Ung, [#126], [#127]] + from `IImmutableList` to `DnsEndPoint`. + [[#120], [#123] by Yang Chun Ung, [#126], [#127], [#165]] - `Swarm` became to ignore tip blocks of the same height (`Index`) that it already has and deal with only longer (higher) blocks. - Fixed a bug that occured when `Swarm` was handling multiple responses at the @@ -181,7 +181,6 @@ To be released. [#98]: https://github.com/planetarium/libplanet/issues/98 [#99]: https://github.com/planetarium/libplanet/issues/99 [#120]: https://github.com/planetarium/libplanet/issues/120 -[#167]: https://github.com/planetarium/libplanet/issues/167 [#121]: https://github.com/planetarium/libplanet/pull/121 [#123]: https://github.com/planetarium/libplanet/pull/123 [#124]: https://github.com/planetarium/libplanet/pull/124 @@ -196,6 +195,8 @@ To be released. [#144]: https://github.com/planetarium/libplanet/pull/144 [#151]: https://github.com/planetarium/libplanet/pull/151 [#159]: https://github.com/planetarium/libplanet/pull/159 +[#165]: https://github.com/planetarium/libplanet/issues/165 +[#167]: https://github.com/planetarium/libplanet/issues/167 [#169]: https://github.com/planetarium/libplanet/pull/169 [#170]: https://github.com/planetarium/libplanet/pull/170 [RFC 5389]: https://tools.ietf.org/html/rfc5389 diff --git a/Libplanet.Tests/Net/PeerSetDeltaTest.cs b/Libplanet.Tests/Net/PeerSetDeltaTest.cs index fb2a9b33a71..794f8c6c2f6 100644 --- a/Libplanet.Tests/Net/PeerSetDeltaTest.cs +++ b/Libplanet.Tests/Net/PeerSetDeltaTest.cs @@ -17,20 +17,20 @@ public void Serialize() var peerSetDelta = new PeerSetDelta( new Peer( new PrivateKey().PublicKey, - new IPEndPoint(IPAddress.Parse("0.0.0.0"), 1234) + new DnsEndPoint("0.0.0.0", 1234) ), DateTimeOffset.UtcNow, new[] { new Peer( new PrivateKey().PublicKey, - new IPEndPoint(IPAddress.Parse("1.2.3.4"), 1234)), + new DnsEndPoint("1.2.3.4", 1234)), }.ToImmutableHashSet(), new[] { new Peer( new PrivateKey().PublicKey, - new IPEndPoint(IPAddress.Parse("2.3.4.5"), 1234)), + new DnsEndPoint("2.3.4.5", 1234)), }.ToImmutableHashSet(), null ); diff --git a/Libplanet.Tests/Net/PeerTest.cs b/Libplanet.Tests/Net/PeerTest.cs index 4747d21b383..7011c38dc4c 100644 --- a/Libplanet.Tests/Net/PeerTest.cs +++ b/Libplanet.Tests/Net/PeerTest.cs @@ -17,7 +17,7 @@ public void Serialize() "038f92e8098c897c2a9ae3226eb6337eb" + "7ca8dbad5e1c8c9b130a9d39171a44134" )); - var endPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 1234); + var endPoint = new DnsEndPoint("0.0.0.0", 1234); var peer = new Peer(key, endPoint); var formatter = new BinaryFormatter(); using (var stream = new MemoryStream()) diff --git a/Libplanet.Tests/Net/SwarmTest.cs b/Libplanet.Tests/Net/SwarmTest.cs index 92ab5a0bfc1..407c57891af 100644 --- a/Libplanet.Tests/Net/SwarmTest.cs +++ b/Libplanet.Tests/Net/SwarmTest.cs @@ -56,16 +56,16 @@ public SwarmTest(ITestOutputHelper output) { new Swarm( new PrivateKey(), - 1, - ipAddress: IPAddress.Loopback), + appProtocolVersion: 1, + host: IPAddress.Loopback.ToString()), new Swarm( new PrivateKey(), - 1, - ipAddress: IPAddress.Loopback), + appProtocolVersion: 1, + host: IPAddress.Loopback.ToString()), new Swarm( new PrivateKey(), - 1, - ipAddress: IPAddress.Loopback), + appProtocolVersion: 1, + host: IPAddress.Loopback.ToString()), }; } @@ -313,20 +313,20 @@ public async Task DetectAppProtocolVersion() { var a = new Swarm( new PrivateKey(), - ipAddress: IPAddress.Loopback, + host: IPAddress.Loopback.ToString(), appProtocolVersion: 2); var b = new Swarm( new PrivateKey(), - ipAddress: IPAddress.Loopback, + host: IPAddress.Loopback.ToString(), appProtocolVersion: 3); var c = new Swarm( new PrivateKey(), - ipAddress: IPAddress.Loopback, + host: IPAddress.Loopback.ToString(), appProtocolVersion: 2); var d = new Swarm( new PrivateKey(), - ipAddress: IPAddress.Loopback, + host: IPAddress.Loopback.ToString(), appProtocolVersion: 3); BlockChain chain = _blockchains[0]; @@ -364,18 +364,18 @@ public void BeComparedProperly() var a = new Swarm( pk1, 1, - ipAddress: IPAddress.Parse("0.0.0.0"), + host: "0.0.0.0", listenPort: 5555); var b = new Swarm( pk1, 1, - ipAddress: IPAddress.Parse("0.0.0.0"), + host: "0.0.0.0", listenPort: 5555, createdAt: a.LastDistributed); var c = new Swarm( pk2, 1, - ipAddress: IPAddress.Parse("0.0.0.0"), + host: "0.0.0.0", listenPort: 5555); Assert.Equal(a, b); @@ -638,11 +638,11 @@ public void CanDenyNullParams() [Fact] public void CanResolveEndPoint() { - var expected = new IPEndPoint(IPAddress.Parse("1.2.3.4"), 5678); + var expected = new DnsEndPoint("1.2.3.4", 5678); Swarm s = new Swarm( new PrivateKey(), 1, - ipAddress: IPAddress.Parse("1.2.3.4"), + host: "1.2.3.4", listenPort: 5678); Assert.Equal(expected, s.EndPoint); @@ -665,8 +665,11 @@ public async Task CanStopGracefullyWhileStarting() [Fact] public async Task AsPeerThrowSwarmExceptionWhenUnbound() { - Swarm swarm = - new Swarm(new PrivateKey(), 1, ipAddress: IPAddress.Loopback); + Swarm swarm = new Swarm( + new PrivateKey(), + 1, + host: IPAddress.Loopback.ToString() + ); Assert.Throws(() => swarm.AsPeer); await StartAsync(swarm, _blockchains[0]); @@ -688,10 +691,7 @@ public async Task ExchangeWithIceServer() credential: password), }; - var seed = new Swarm( - new PrivateKey(), - 1, - ipAddress: IPAddress.Loopback); + var seed = new Swarm(new PrivateKey(), 1, host: "localhost"); var swarmA = new Swarm(new PrivateKey(), 1, iceServers: iceServers); var swarmB = new Swarm(new PrivateKey(), 1, iceServers: iceServers); diff --git a/Libplanet/Net/Peer.cs b/Libplanet/Net/Peer.cs index 415d5601683..30560b980dd 100644 --- a/Libplanet/Net/Peer.cs +++ b/Libplanet/Net/Peer.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics.Contracts; -using System.Linq; using System.Net; using System.Runtime.Serialization; using Libplanet.Crypto; @@ -15,7 +12,7 @@ namespace Libplanet.Net [GeneratedEquality] public partial class Peer : ISerializable { - public Peer(PublicKey publicKey, IPEndPoint endPoint) + public Peer(PublicKey publicKey, DnsEndPoint endPoint) { if (publicKey == null) { @@ -33,7 +30,9 @@ public Peer(PublicKey publicKey, IPEndPoint endPoint) protected Peer(SerializationInfo info, StreamingContext context) { PublicKey = new PublicKey(info.GetValue("public_key")); - EndPoint = info.GetValue("end_point"); + EndPoint = new DnsEndPoint( + info.GetString("end_point_host"), + info.GetInt32("end_point_port")); } [EqualityKey] @@ -42,7 +41,7 @@ protected Peer(SerializationInfo info, StreamingContext context) [EqualityKey] [Pure] - public IPEndPoint EndPoint { get; } + public DnsEndPoint EndPoint { get; } [Pure] public Address Address => new Address(PublicKey); @@ -53,7 +52,8 @@ StreamingContext context ) { info.AddValue("public_key", PublicKey.Format(true)); - info.AddValue("end_point", EndPoint); + info.AddValue("end_point_host", EndPoint.Host); + info.AddValue("end_point_port", EndPoint.Port); } public override string ToString() diff --git a/Libplanet/Net/Swarm.cs b/Libplanet/Net/Swarm.cs index 38df391ff4d..08ed85d68c9 100644 --- a/Libplanet/Net/Swarm.cs +++ b/Libplanet/Net/Swarm.cs @@ -46,7 +46,7 @@ public partial class Swarm : ICollection, IDisposable private readonly AsyncLock _distributeMutex; private readonly AsyncLock _receiveMutex; private readonly AsyncLock _blockSyncMutex; - private readonly IPAddress _ipAddress; + private readonly string _host; private readonly TurnClient _turnClient; private readonly ILogger _logger; @@ -60,7 +60,7 @@ public Swarm( PrivateKey privateKey, int appProtocolVersion, int millisecondsDialTimeout = 15000, - IPAddress ipAddress = null, + string host = null, int? listenPort = null, DateTimeOffset? createdAt = null, IEnumerable iceServers = null) @@ -68,7 +68,7 @@ public Swarm( privateKey, appProtocolVersion, TimeSpan.FromMilliseconds(millisecondsDialTimeout), - ipAddress, + host, listenPort, createdAt, iceServers) @@ -79,7 +79,7 @@ public Swarm( PrivateKey privateKey, int appProtocolVersion, TimeSpan dialTimeout, - IPAddress ipAddress = null, + string host = null, int? listenPort = null, DateTimeOffset? createdAt = null, IEnumerable iceServers = null) @@ -111,13 +111,13 @@ public Swarm( _blockSyncMutex = new AsyncLock(); _runningMutex = new AsyncLock(); - _ipAddress = ipAddress; + _host = host; _listenPort = listenPort; _appProtocolVersion = appProtocolVersion; - if (_ipAddress != null && _listenPort != null) + if (_host != null && _listenPort != null) { - EndPoint = new IPEndPoint(_ipAddress, listenPort.Value); + EndPoint = new DnsEndPoint(_host, listenPort.Value); } if (iceServers != null) @@ -125,10 +125,10 @@ public Swarm( _turnClient = IceServer.CreateTurnClient(iceServers).Result; } - if (ipAddress == null && _turnClient == null) + if (_host == null && _turnClient == null) { throw new ArgumentException( - $"Swarm needs {nameof(ipAddress)} or {iceServers}."); + $"Swarm needs {nameof(host)} or {iceServers}."); } string loggerId = _privateKey.PublicKey.ToAddress().ToHex(); @@ -150,7 +150,7 @@ public Swarm( public bool IsReadOnly => false; - public IPEndPoint EndPoint { get; private set; } + public DnsEndPoint EndPoint { get; private set; } [Uno.EqualityKey] public Address Address => _privateKey.PublicKey.ToAddress(); @@ -253,16 +253,7 @@ public async Task> AddPeersAsync( { if (_turnClient != null) { - var ep = peer.EndPoint; - if (IPAddress.IsLoopback(ep.Address)) - { - // This translation is only used in test case - // because a seed node exposes loopback address - // as public address to other node in test case - ep = await _turnClient.GetMappedAddressAsync(); - } - - await _turnClient.CreatePermissionAsync(ep); + await CreatePermission(peer); } _logger.Debug($"Trying to DialPeerAsync({peer})..."); @@ -455,12 +446,15 @@ public async Task StartAsync( if (behindNAT) { - EndPoint = await _turnClient.AllocateRequestAsync( - TurnAllocationLifetime); + IPEndPoint turnEp = await _turnClient.AllocateRequestAsync( + TurnAllocationLifetime + ); + EndPoint = new DnsEndPoint( + turnEp.Address.ToString(), turnEp.Port); } else { - EndPoint = new IPEndPoint(_ipAddress, _listenPort.Value); + EndPoint = new DnsEndPoint(_host, _listenPort.Value); } try @@ -1527,7 +1521,35 @@ private string ToNetMQAddress(Peer peer) throw new ArgumentNullException(nameof(peer)); } - return $"tcp://{peer.EndPoint}"; + return $"tcp://{peer.EndPoint.Host}:{peer.EndPoint.Port}"; + } + + private async Task CreatePermission(Peer peer) + { + var peerHost = peer.EndPoint.Host; + IPAddress[] ips; + if (IPAddress.TryParse(peerHost, out IPAddress asIp)) + { + ips = new[] { asIp }; + } + else + { + ips = await Dns.GetHostAddressesAsync(peerHost); + } + + foreach (IPAddress ip in ips) + { + var ep = new IPEndPoint(ip, peer.EndPoint.Port); + if (IPAddress.IsLoopback(ip)) + { + // This translation is only used in test case because a + // seed node exposes loopback address as public address to + // other node in test case + ep = await _turnClient.GetMappedAddressAsync(); + } + + await _turnClient.CreatePermissionAsync(ep); + } } } } From 179538324e37099808bf3174ac9f932b3fd25af2 Mon Sep 17 00:00:00 2001 From: Swen Mun Date: Mon, 1 Apr 2019 22:40:54 +0900 Subject: [PATCH 2/2] Fix Swarm() hangs --- CHANGES.md | 4 +++- Libplanet.Tests/Net/SwarmTest.cs | 17 ++++++++++++++++- Libplanet/Net/Swarm.cs | 20 ++++++++++++-------- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e22e2159234..f670bafea49 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -150,11 +150,13 @@ To be released. work around NAT so that `Peer`'s endpoints don't have to be multiple, `Peer.Urls` was renamed to `Peer.EndPoint` and its type also was changed from `IImmutableList` to `DnsEndPoint`. - [[#120], [#123] by Yang Chun Ung, [#126], [#127], [#165]] + [[#120], [#123] by Yang Chun Ung, [#126], [#127], [#165], [#166]] - `Swarm` became to ignore tip blocks of the same height (`Index`) that it already has and deal with only longer (higher) blocks. - Fixed a bug that occured when `Swarm` was handling multiple responses at the same time. + - Fixed a bug that the `Swarm` constructor had hanged in certain runtimes + like Unity engine. - Removed `AddressTransactionSet` which handles handle `Address` to `IEnumerable` indices, and the following methods in `IStore`: - `IStore.IterateAddresses()` diff --git a/Libplanet.Tests/Net/SwarmTest.cs b/Libplanet.Tests/Net/SwarmTest.cs index 407c57891af..2485e7111d7 100644 --- a/Libplanet.Tests/Net/SwarmTest.cs +++ b/Libplanet.Tests/Net/SwarmTest.cs @@ -627,12 +627,27 @@ await Task.WhenAll( } [Fact] - public void CanDenyNullParams() + public void ThrowArgumentExceptionInConstructor() { Assert.Throws(() => { new Swarm(null, 1); }); + + // Swarm needs host or iceServers. + Assert.Throws(() => + { + new Swarm(new PrivateKey(), 1); + }); + + // Swarm needs host or iceServers. + Assert.Throws(() => + { + new Swarm( + new PrivateKey(), + 1, + iceServers: new IceServer[] { }); + }); } [Fact] diff --git a/Libplanet/Net/Swarm.cs b/Libplanet/Net/Swarm.cs index 08ed85d68c9..f6cc8031167 100644 --- a/Libplanet/Net/Swarm.cs +++ b/Libplanet/Net/Swarm.cs @@ -47,12 +47,13 @@ public partial class Swarm : ICollection, IDisposable private readonly AsyncLock _receiveMutex; private readonly AsyncLock _blockSyncMutex; private readonly string _host; - private readonly TurnClient _turnClient; + private readonly IList _iceServers; private readonly ILogger _logger; private TaskCompletionSource _runningEvent; private int? _listenPort; + private TurnClient _turnClient; private NetMQQueue _replyQueue; @@ -120,15 +121,13 @@ public Swarm( EndPoint = new DnsEndPoint(_host, listenPort.Value); } - if (iceServers != null) - { - _turnClient = IceServer.CreateTurnClient(iceServers).Result; - } - - if (_host == null && _turnClient == null) + _iceServers = iceServers?.ToList(); + if (_host == null && (_iceServers == null || !_iceServers.Any())) { throw new ArgumentException( - $"Swarm needs {nameof(host)} or {iceServers}."); + $"Swarm requires either {nameof(host)} or " + + $"{nameof(iceServers)}." + ); } string loggerId = _privateKey.PublicKey.ToAddress().ToHex(); @@ -430,6 +429,11 @@ public async Task StartAsync( throw new SwarmException("Swarm is already running."); } + if (_iceServers != null) + { + _turnClient = await IceServer.CreateTurnClient(_iceServers); + } + if (_listenPort == null) { _listenPort = _router.BindRandomPort("tcp://*");