From 41fddc37fafc4a2b80ad5e64d098cb4f9b2fe024 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 8 Aug 2024 15:28:27 +0300 Subject: [PATCH 01/25] Add ITP --- src/libp2p/Libp2p.Core/ChannelFactory.cs | 38 +++++++++++++++---- .../Libp2p.Core/Exceptions/Libp2pException.cs | 5 +++ src/libp2p/Libp2p.Core/IChannelFactory.cs | 2 +- src/libp2p/Libp2p.Core/IProtocol.cs | 27 ++++++++++++- src/libp2p/Libp2p.Core/PeerFactory.cs | 29 +++++++++----- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 24 ++++++------ .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 2 +- .../MultistreamProtocol.cs | 2 +- .../Libp2p.Protocols.Quic/QuicProtocol.cs | 2 +- src/libp2p/Libp2p/Libp2pLocalPeer.cs | 14 +++++++ src/libp2p/Libp2p/Libp2pPeerFactory.cs | 3 +- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 2 +- .../MultiaddressBasedSelectorProtocol.cs | 26 +++++++++---- 13 files changed, 131 insertions(+), 45 deletions(-) create mode 100644 src/libp2p/Libp2p/Libp2pLocalPeer.cs diff --git a/src/libp2p/Libp2p.Core/ChannelFactory.cs b/src/libp2p/Libp2p.Core/ChannelFactory.cs index f63821c8..f4d947e4 100644 --- a/src/libp2p/Libp2p.Core/ChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/ChannelFactory.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Core.Extensions; namespace Nethermind.Libp2p.Core; @@ -11,7 +12,7 @@ public class ChannelFactory : IChannelFactory { private readonly IServiceProvider _serviceProvider; private readonly ILoggerFactory? _loggerFactory; - private IDictionary _factories; + private IDictionary _factories; private readonly ILogger? _logger; public ChannelFactory(IServiceProvider serviceProvider) @@ -21,14 +22,20 @@ public ChannelFactory(IServiceProvider serviceProvider) _logger = _loggerFactory?.CreateLogger(); } - public IEnumerable SubProtocols => _factories.Keys; + public IEnumerable SubProtocols => _factories.Keys; public IChannel SubDial(IPeerContext context, IChannelRequest? req = null) { - IProtocol? subProtocol = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + if (subProtocolId is not IProtocol subProtocol) + { + throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + } + Channel channel = new(); ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + _ = subProtocol.DialAsync(channel.Reverse, channelFactory, context) .ContinueWith(async task => @@ -47,7 +54,12 @@ public IChannel SubDial(IPeerContext context, IChannelRequest? req = null) public IChannel SubListen(IPeerContext context, IChannelRequest? req = null) { - IProtocol? subProtocol = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + if (subProtocolId is not IProtocol subProtocol) + { + throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + } + Channel channel = new(); ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; @@ -70,7 +82,13 @@ public IChannel SubListen(IPeerContext context, IChannelRequest? req = null) public Task SubDialAndBind(IChannel parent, IPeerContext context, IChannelRequest? req = null) { - IProtocol? subProtocol = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + + if (subProtocolId is not IProtocol subProtocol) + { + throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + } + ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; return subProtocol.DialAsync(((Channel)parent), channelFactory, context) @@ -89,7 +107,13 @@ public Task SubDialAndBind(IChannel parent, IPeerContext context, public Task SubListenAndBind(IChannel parent, IPeerContext context, IChannelRequest? req = null) { - IProtocol? subProtocol = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + + if (subProtocolId is not IProtocol subProtocol) + { + throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + } + ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; return subProtocol.ListenAsync(((Channel)parent), channelFactory, context) @@ -100,7 +124,7 @@ public Task SubListenAndBind(IChannel parent, IPeerContext context, }); } - public ChannelFactory Setup(IDictionary factories) + public ChannelFactory Setup(IDictionary factories) { _factories = factories; return this; diff --git a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs index d36b4528..c3d1637f 100644 --- a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs +++ b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs @@ -19,3 +19,8 @@ public class ChannelClosedException : Libp2pException { } + +public class Libp2pSetupException(string? message = null) : Libp2pException(message) +{ + +} diff --git a/src/libp2p/Libp2p.Core/IChannelFactory.cs b/src/libp2p/Libp2p.Core/IChannelFactory.cs index d8ff230e..66870136 100644 --- a/src/libp2p/Libp2p.Core/IChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/IChannelFactory.cs @@ -5,7 +5,7 @@ namespace Nethermind.Libp2p.Core; public interface IChannelFactory { - IEnumerable SubProtocols { get; } + IEnumerable SubProtocols { get; } IChannel SubDial(IPeerContext context, IChannelRequest? request = null); IChannel SubListen(IPeerContext context, IChannelRequest? request = null); diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index a8b207c9..1bc77645 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -3,14 +3,37 @@ namespace Nethermind.Libp2p.Core; -// TODO: Try the synchronous approach -public interface IProtocol +public interface IId { /// /// Id used to during connection establishedment, exchanging information about protocol versions and so on /// string Id { get; } +} + +public interface ITransportProtocol : IId +{ + /// + /// Actively dials a peer + /// + /// A channel to communicate with a bottom layer protocol + /// Factory that spawns new channels used to interact with top layer protocols + /// Holds information about local and remote peers + /// + Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context); + /// + /// Opens a channel to listen to a remote peer + /// + /// A channel to communicate with a bottom layer protocol + /// Factory that spawns new channels used to interact with top layer protocols + /// Holds information about local and remote peers + /// + Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context); +} + +public interface IProtocol : IId +{ /// /// Actively dials a peer /// diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 5e343eb0..692cd0e3 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -3,15 +3,16 @@ using Multiformats.Address; using Multiformats.Address.Protocols; +using Nethermind.Libp2p.Core.Exceptions; using System.Runtime.CompilerServices; namespace Nethermind.Libp2p.Core; -public class PeerFactory : IPeerFactory +public abstract class PeerFactory : IPeerFactory { private readonly IServiceProvider _serviceProvider; - private IProtocol _protocol; + private IId _protocol; private IChannelFactory _upChannelFactory; private static int CtxId = 0; @@ -20,11 +21,7 @@ public PeerFactory(IServiceProvider serviceProvider) _serviceProvider = serviceProvider; } - public virtual ILocalPeer Create(Identity? identity = default, Multiaddress? localAddr = default) - { - identity ??= new Identity(); - return new LocalPeer(this) { Identity = identity ?? new Identity(), Address = localAddr ?? $"/ip4/0.0.0.0/tcp/0/p2p/{identity.PeerId}" }; - } + public abstract ILocalPeer Create(Identity? identity = default, Multiaddress? localAddr = default); /// /// PeerFactory interface ctor @@ -33,7 +30,7 @@ public virtual ILocalPeer Create(Identity? identity = default, Multiaddress? loc /// /// /// - public void Setup(IProtocol protocol, IChannelFactory upChannelFactory) + public void Setup(IId protocol, IChannelFactory upChannelFactory) { _protocol = protocol; _upChannelFactory = upChannelFactory; @@ -84,7 +81,13 @@ void OnListenerReady() ConnectedTo(remotePeer, false) .ContinueWith(t => { result.RaiseOnConnection(remotePeer); }, token); }; - _ = _protocol.ListenAsync(chan, _upChannelFactory, peerContext); + + _ = _protocol switch + { + IProtocol protocol => _ = protocol.ListenAsync(chan, _upChannelFactory, peerContext), + ITransportProtocol protocol => _ = protocol.ListenAsync(chan, _upChannelFactory, peerContext), + _ => throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {_protocol.GetType()}"), + }; await ts.Task; return result; @@ -136,7 +139,12 @@ protected virtual async Task DialAsync(LocalPeer peer, Multiaddress }; context.OnRemotePeerConnection += remotePeerConnected; - _ = _protocol.DialAsync(chan, _upChannelFactory, context); + _ = _protocol switch + { + IProtocol protocol => _ = protocol.DialAsync(chan, _upChannelFactory, context), + ITransportProtocol protocol => _ = protocol.DialAsync(chan, _upChannelFactory, context), + _ => throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {_protocol.GetType()}"), + }; await tcs.Task; return result; @@ -234,3 +242,4 @@ public IPeer Fork() } } } + diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 7aa9d474..70370395 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -7,22 +7,22 @@ namespace Nethermind.Libp2p.Core; public static class PeerFactoryBuilderBase { - private static HashSet protocols = new(); + private static HashSet protocols = new(); - internal static IProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol + internal static TProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IId { if (instance is not null) { protocols.Add(instance); } - IProtocol? existing = instance ?? protocols.OfType().FirstOrDefault(); + IId? existing = instance ?? protocols.OfType().FirstOrDefault(); if (existing is null) { existing = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); protocols.Add(existing); } - return existing; + return (TProtocol)existing; } } @@ -42,7 +42,7 @@ protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) ServiceProvider = serviceProvider ?? new ServiceCollection().BuildServiceProvider(); } - protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol + protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IId { return new ProtocolStack(this, ServiceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance)); } @@ -61,11 +61,11 @@ protected class ProtocolStack public ProtocolStack? Root { get; private set; } public ProtocolStack? Parent { get; private set; } public ProtocolStack? PrevSwitch { get; private set; } - public IProtocol Protocol { get; } + public IId Protocol { get; } public HashSet TopProtocols { get; } = new(); public ChannelFactory UpChannelsFactory { get; } - public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IProtocol protocol) + public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IId protocol) { this.builder = builder; this.serviceProvider = serviceProvider; @@ -79,7 +79,7 @@ public ProtocolStack AddAppLayerProtocol(TProtocol? instance = defaul return this; } - public ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol + public ProtocolStack Over(TProtocol? instance = default) where TProtocol : IId { ProtocolStack nextNode = new(builder, serviceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance)); return Over(nextNode); @@ -91,14 +91,14 @@ public ProtocolStack Or(TProtocol? instance = default) where TProtoco { throw new NotImplementedException(); } - IProtocol protocol = PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance); + IId protocol = PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance); ProtocolStack stack = new(builder, serviceProvider, protocol); return Or(stack); } public ProtocolStack Over(ProtocolStack stack) { - PeerFactoryBuilderBase.ProtocolStack rootProto = stack.Root ?? stack; + ProtocolStack rootProto = stack.Root ?? stack; TopProtocols.Add(rootProto); if (PrevSwitch != null) @@ -149,8 +149,8 @@ public IPeerFactory Build() static void SetupChannelFactories(ProtocolStack root) { - root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols - .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); + root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols + .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); foreach (ProtocolStack topProto in root.TopProtocols) { if (!root.TopProtocols.Any()) diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index c9a7f2bc..a937e00c 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -11,7 +11,7 @@ namespace Nethermind.Libp2p.Protocols; -public class IpTcpProtocol(ILoggerFactory? loggerFactory = null) : IProtocol +public class IpTcpProtocol(ILoggerFactory? loggerFactory = null) : ITransportProtocol { private readonly ILogger? _logger = loggerFactory?.CreateLogger(); diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index fdcf89f7..437cf2d1 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -96,7 +96,7 @@ public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, for (; ; ) { string proto = await channel.ReadLineAsync(); - selected = channelFactory!.SubProtocols.FirstOrDefault(x => x.Id == proto); + selected = channelFactory!.SubProtocols.FirstOrDefault(x => x.Id == proto) as IProtocol; if (selected is not null) { await channel.WriteLineAsync(selected.Id); diff --git a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs index 9bf3a57b..cb21858a 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs @@ -24,7 +24,7 @@ namespace Nethermind.Libp2p.Protocols; /// https://github.com/libp2p/specs/blob/master/quic/README.md /// [RequiresPreviewFeatures] -public class QuicProtocol : IProtocol +public class QuicProtocol : ITransportProtocol { private readonly ILogger? _logger; private readonly ECDsa _sessionKey; diff --git a/src/libp2p/Libp2p/Libp2pLocalPeer.cs b/src/libp2p/Libp2p/Libp2pLocalPeer.cs new file mode 100644 index 00000000..22d98a14 --- /dev/null +++ b/src/libp2p/Libp2p/Libp2pLocalPeer.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Libp2p; +//internal class Libp2pLocalPeer: ILocalPeer +//{ +//} diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 084f6ce7..e055efdf 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -27,6 +27,7 @@ public override ILocalPeer Create(Identity? identity = null, Multiaddress? local { localAddr.Add(identity.PeerId.ToString()); } - return base.Create(identity, localAddr); + + return new LocalPeer(this) { Identity = identity ?? new Identity(), Address = localAddr ?? $"/ip4/0.0.0.0/tcp/0/p2p/{identity.PeerId}" }; } } diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index 6c0a8f19..e2be774e 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -38,7 +38,7 @@ protected override ProtocolStack BuildStack() .Over(); return - Over() + Over() .Over().Or(tcpStack) .Over() .AddAppLayerProtocol() diff --git a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs index 113354f6..5e59cfc3 100644 --- a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs +++ b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs @@ -11,23 +11,33 @@ namespace Nethermind.Libp2p.Protocols; /// /// Select protocol based on multiaddr /// -public class MultiaddressBasedSelectorProtocol(ILoggerFactory? loggerFactory = null) : SymmetricProtocol, IProtocol +public class MultiaddressBasedSelector(ILoggerFactory? loggerFactory = null): ITransportProtocol { - private readonly ILogger? _logger = loggerFactory?.CreateLogger(); + private readonly ILogger? _logger = loggerFactory?.CreateLogger(); public string Id => "multiaddr-select"; - protected override async Task ConnectAsync(IChannel _, IChannelFactory? channelFactory, IPeerContext context, bool isListener) + public Task DialAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) { - IProtocol protocol = null!; + return ConnectAsync(channel, channelFactory, context, false); + } + + public Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) + { + return ConnectAsync(channel, channelFactory, context, true); + } + + protected async Task ConnectAsync(IChannel _, IChannelFactory? channelFactory, IPeerContext context, bool isListener) + { + ITransportProtocol protocol = null!; // TODO: deprecate quic if (context.LocalPeer.Address.Has()) { - protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") ?? throw new ApplicationException("QUICv1 is not supported"); + protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); } else if (context.LocalPeer.Address.Has()) { - protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "ip-tcp") ?? throw new ApplicationException("TCP is not supported"); + protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); } else if (context.LocalPeer.Address.Has()) { @@ -41,7 +51,7 @@ protected override async Task ConnectAsync(IChannel _, IChannelFactory? channelF _logger?.LogPickedProtocol(protocol.Id, isListener ? "listen" : "dial"); await (isListener - ? channelFactory.SubListen(context, protocol) - : channelFactory.SubDial(context, protocol)); + ? protocol.ListenAsync(_, channelFactory, context) + : protocol.DialAsync(_, channelFactory, context)); } } From 034bb7f435c2971317036bbb3831489b70712f88 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Tue, 13 Aug 2024 13:49:12 +0300 Subject: [PATCH 02/25] Refactor peer --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 247 +++++++++++ .../Libp2p.Core.TestsBase/TestLocalPeer.cs | 20 +- src/libp2p/Libp2p.Core/Channel.cs | 2 +- src/libp2p/Libp2p.Core/ChannelFactory.cs | 200 ++++----- src/libp2p/Libp2p.Core/ConnectionContext.cs | 52 +++ src/libp2p/Libp2p.Core/IChannelFactory.cs | 27 +- .../Libp2p.Core/ILibp2pBuilderContext.cs | 12 + src/libp2p/Libp2p.Core/IListener.cs | 8 - src/libp2p/Libp2p.Core/ILocalPeer.cs | 12 - src/libp2p/Libp2p.Core/IPeer.cs | 21 +- src/libp2p/Libp2p.Core/IPeerContext.cs | 47 ++- src/libp2p/Libp2p.Core/IPeerFactory.cs | 2 +- src/libp2p/Libp2p.Core/IProtocol.cs | 42 +- src/libp2p/Libp2p.Core/IReader.cs | 1 - src/libp2p/Libp2p.Core/IRemotePeer.cs | 7 +- src/libp2p/Libp2p.Core/Ids.cs | 8 + src/libp2p/Libp2p.Core/Peer.cs | 382 ++++++++++++++++++ src/libp2p/Libp2p.Core/PeerContext.cs | 69 ++-- src/libp2p/Libp2p.Core/PeerFactory.cs | 239 +---------- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 43 +- src/libp2p/Libp2p.Core/SymetricProtocol.cs | 20 +- src/libp2p/Libp2p.Core/TransportContext.cs | 22 + .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 143 +++---- .../MultistreamProtocolTests.cs | 16 +- .../MultistreamProtocol.cs | 6 +- .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 4 +- .../PlainTextProtocol.cs | 4 +- .../FloodsubProtocolTests.cs | 2 +- .../GossipsubProtocolTests.cs | 2 +- .../PubsubProtocolTests.cs | 72 ++-- .../Libp2p.Protocols.Pubsub/ManagedPeer.cs | 8 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 16 +- .../Libp2p.Protocols.Quic/QuicProtocol.cs | 112 ++--- .../YamuxProtocolTests.cs | 4 +- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 4 +- src/libp2p/Libp2p/Libp2pBuilderContext.cs | 11 + src/libp2p/Libp2p/Libp2pLocalPeer.cs | 2 +- src/libp2p/Libp2p/Libp2pPeerFactory.cs | 24 +- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 3 +- .../MultiaddressBasedSelectorProtocol.cs | 56 ++- .../Libp2p/ServiceProviderExtensions.cs | 8 +- src/samples/chat/Program.cs | 6 +- src/samples/perf-benchmarks/Program.cs | 12 +- src/samples/pubsub-chat/Program.cs | 2 +- src/samples/transport-interop/Program.cs | 6 +- 45 files changed, 1247 insertions(+), 759 deletions(-) create mode 100644 src/libp2p/Libp2p.Core.Tests/ContextTests.cs create mode 100644 src/libp2p/Libp2p.Core/ConnectionContext.cs create mode 100644 src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs delete mode 100644 src/libp2p/Libp2p.Core/ILocalPeer.cs create mode 100644 src/libp2p/Libp2p.Core/Ids.cs create mode 100644 src/libp2p/Libp2p.Core/Peer.cs create mode 100644 src/libp2p/Libp2p.Core/TransportContext.cs create mode 100644 src/libp2p/Libp2p/Libp2pBuilderContext.cs diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs new file mode 100644 index 00000000..96aa7689 --- /dev/null +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -0,0 +1,247 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; +using Multiformats.Address.Protocols; +using Nethermind.Libp2p.Stack; +using Nethermind.Libp2p.Core; +using NUnit.Framework.Constraints; +using System.Net; + +namespace Nethermind.Libp2p.Core.Tests; +public class ContextTests +{ + public static Channel tcp = new Channel(); + + + [Test] + public async Task E2e() + { + ITransportProtocol tProto = new TProto(); + IConnectionProtocol cProto = new CProto(); + ISessionProtocol sProto = new SProto(); + + BuilderContext builderContext = new BuilderContext + { + Protocols = new Dictionary + { + { tProto, [ cProto] }, + { cProto, [sProto] }, + { sProto, [] }, + }, + TopProtocols = [tProto] + }; + + LocalPeer peer1 = new LocalPeer(builderContext, new Identity()); + LocalPeer peer2 = new LocalPeer(builderContext, new Identity()); + + await peer1.StartListenAsync([new Multiaddress()]); + await peer2.StartListenAsync([new Multiaddress()]); + + ISession session = await peer2.DialAsync(new Multiaddress()); + + //await session.DialAsync(); + //await session.DialAsync(); + + //ITransportContext tContext = peer.CreateContext(tProto); + + //ITransportConnectionContext tcContext = tContext.CreateConnection(); + //tcContext.SubDial(); + + //IConnectionContext cContext = peer.CreateContext(cProto); + + //cContext.SubDial(); + //ISession connectionSessionContext = cContext.CreateSession(); + + //ISessionContext sContext = peer.CreateContext(sProto); + + //sContext.SubDial(); + //sContext.DialAsync(); + + //sContext.Disconnect(); + await Task.Delay(1000_0000); + } +} + +class BuilderContext : IBuilderContext +{ + public Dictionary? Protocols { get; set; } = new Dictionary { }; + public IProtocol[]? TopProtocols { get; set; } = []; +} + +class TProto : ITransportProtocol +{ + public string Id => nameof(TProto); + + public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) + { + try + { + context.ListenerReady(Multiaddress.Decode("/ip4/127.0.0.1/tcp/4096")); + using ITransportConnectionContext connectionCtx = context.CreateConnection(); + + + IChannel topChan = connectionCtx.SubListen(); + connectionCtx.Token.Register(() => topChan.CloseAsync()); + + + ReadResult received; + while (true) + { + received = await ContextTests.tcp.ReadAsync(1, ReadBlockingMode.WaitAny); + if(received.Result != IOResult.Ok) + { + break; + } + + var sent = await topChan.WriteAsync(received.Data); + + if (sent!= IOResult.Ok) + { + break; + } + } + await topChan.CloseAsync(); + } + catch + { + + } + } + + public async Task DialAsync(ITransportConnectionContext context, Multiaddress listenAddr, CancellationToken token) + { + IChannel topChan = context.SubDial(); + context.Token.Register(() => topChan.CloseAsync()); + + + ReadResult received; + while (true) + { + received = await topChan.ReadAsync(1, ReadBlockingMode.WaitAny); + if (received.Result != IOResult.Ok) + { + break; + } + + var sent = await ContextTests.tcp.WriteAsync(received.Data); + + if (sent != IOResult.Ok) + { + break; + } + } + await topChan.CloseAsync(); + } +} + +class CProto : IConnectionProtocol +{ + public string Id => throw new NotImplementedException(); + + public async Task DialAsync(IChannel downChannel, IConnectionContext context) + { + try + { + using ISession session = context.CreateSession(new PeerId(new Dto.PublicKey())); + IChannel topChan = context.SubDial(); + + ReadResult received; + while (true) + { + received = await topChan.ReadAsync(1, ReadBlockingMode.WaitAny); + if (received.Result != IOResult.Ok) + { + break; + } + + var sent = await downChannel.WriteAsync(received.Data); + + if (sent != IOResult.Ok) + { + break; + } + } + await topChan.CloseAsync(); + } + catch + { + + } + } + + public async Task ListenAsync(IChannel downChannel, IConnectionContext context) + { + try + { + using ISession session = context.CreateSession(); + IChannel topChan = context.SubListen(); + + ReadResult received; + while (true) + { + received = await downChannel.ReadAsync(1, ReadBlockingMode.WaitAny); + if (received.Result != IOResult.Ok) + { + break; + } + + var sent = await topChan.WriteAsync(received.Data); + + if (sent != IOResult.Ok) + { + break; + } + } + await topChan.CloseAsync(); + } + catch + { + + } + } +} + +class SProto : ISessionProtocol +{ + public string Id => throw new NotImplementedException(); + + public async Task DialAsync(IChannel downChannel, ISessionContext context) + { + try + { + await downChannel.WriteLineAsync("Oh hi there"); + } + catch + { + + } + } + + public async Task ListenAsync(IChannel downChannel, ISessionContext context) + { + try + { + var line = await downChannel.ReadLineAsync(); + } + catch + { + + } + } +} + +class SProto2 : ISessionProtocol +{ + public string Id => throw new NotImplementedException(); + + public Task DialAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } + + public Task ListenAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs index 5165f78e..3cc2077d 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs @@ -2,10 +2,11 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using System.Collections.ObjectModel; namespace Nethermind.Libp2p.Core.TestsBase; -public class TestLocalPeer : ILocalPeer +public class TestLocalPeer : IPeer { public TestLocalPeer() { @@ -16,18 +17,25 @@ public TestLocalPeer() public Identity Identity { get; set; } public Multiaddress Address { get; set; } - public Task DialAsync(Multiaddress addr, CancellationToken token = default) + public ObservableCollection ListenAddresses => throw new NotImplementedException(); + + public Task DialAsync(Multiaddress addr, CancellationToken token = default) { - return Task.FromResult(new TestRemotePeer(addr)); + return Task.FromResult(new TestRemotePeer(addr)); } - public Task ListenAsync(Multiaddress addr, CancellationToken token = default) + //public Task ListenAsync(Multiaddress addr, CancellationToken token = default) + //{ + // return Task.FromResult(null); + //} + + public Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) { - return Task.FromResult(null); + throw new NotImplementedException(); } } -public class TestRemotePeer : IRemotePeer +public class TestRemotePeer : ISession { public TestRemotePeer(Multiaddress addr) { diff --git a/src/libp2p/Libp2p.Core/Channel.cs b/src/libp2p/Libp2p.Core/Channel.cs index 1e3f5707..e19596ef 100644 --- a/src/libp2p/Libp2p.Core/Channel.cs +++ b/src/libp2p/Libp2p.Core/Channel.cs @@ -11,7 +11,7 @@ namespace Nethermind.Libp2p.Core; -internal class Channel : IChannel +public class Channel : IChannel { private IChannel? _reversedChannel; private ReaderWriter _reader; diff --git a/src/libp2p/Libp2p.Core/ChannelFactory.cs b/src/libp2p/Libp2p.Core/ChannelFactory.cs index f4d947e4..ffff70ba 100644 --- a/src/libp2p/Libp2p.Core/ChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/ChannelFactory.cs @@ -12,7 +12,7 @@ public class ChannelFactory : IChannelFactory { private readonly IServiceProvider _serviceProvider; private readonly ILoggerFactory? _loggerFactory; - private IDictionary _factories; + private IDictionary _factories; private readonly ILogger? _logger; public ChannelFactory(IServiceProvider serviceProvider) @@ -22,111 +22,131 @@ public ChannelFactory(IServiceProvider serviceProvider) _logger = _loggerFactory?.CreateLogger(); } - public IEnumerable SubProtocols => _factories.Keys; + public IEnumerable SubProtocols => _factories.Keys; - public IChannel SubDial(IPeerContext context, IChannelRequest? req = null) - { - IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - if (subProtocolId is not IProtocol subProtocol) - { - throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - } + //public IChannel SubDial(IPeerContext context, IChannelRequest? req = null) + //{ + // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + // if (subProtocolId is not IProtocol subProtocol) + // { + // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + // } - Channel channel = new(); - ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + // Channel channel = new(); + // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - _ = subProtocol.DialAsync(channel.Reverse, channelFactory, context) - .ContinueWith(async task => - { - if (!task.IsCompletedSuccessfully) - { - _logger?.DialFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); - } - await channel.CloseAsync(); - - req?.CompletionSource?.SetResult(); - }); - - return channel; + // _ = subProtocol.DialAsync(channel.Reverse, channelFactory, context) + // .ContinueWith(async task => + // { + // if (!task.IsCompletedSuccessfully) + // { + // _logger?.DialFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); + // } + // await channel.CloseAsync(); + + // req?.CompletionSource?.SetResult(); + // }); + + // return channel; + //} + + //public IChannel SubListen(IPeerContext context, IChannelRequest? req = null) + //{ + // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + // if (subProtocolId is not IProtocol subProtocol) + // { + // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + // } + + // Channel channel = new(); + // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + + + // _ = subProtocol.ListenAsync(channel.Reverse, channelFactory, context) + // .ContinueWith(async task => + // { + // if (!task.IsCompletedSuccessfully) + // { + // _logger?.ListenFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); + // } + // await channel.CloseAsync(); + + // req?.CompletionSource?.SetResult(); + // }); + + // return channel; + //} + + //public Task SubDialAndBind(IChannel parent, IPeerContext context, + // IChannelRequest? req = null) + //{ + // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + + // if (subProtocolId is not IProtocol subProtocol) + // { + // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + // } + + // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + + // return subProtocol.DialAsync(((Channel)parent), channelFactory, context) + // .ContinueWith(async task => + // { + // if (!task.IsCompletedSuccessfully) + // { + // _logger?.DialAndBindFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); + // } + // await parent.CloseAsync(); + + // req?.CompletionSource?.SetResult(); + // }); + //} + + //public Task SubListenAndBind(IChannel parent, IPeerContext context, + // IChannelRequest? req = null) + //{ + // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + + // if (subProtocolId is not IProtocol subProtocol) + // { + // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); + // } + + // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + + // return subProtocol.ListenAsync(((Channel)parent), channelFactory, context) + // .ContinueWith(async task => + // { + // await parent.CloseAsync(); + // req?.CompletionSource?.SetResult(); + // }); + //} + + public ChannelFactory Setup(IDictionary factories) + { + _factories = factories; + return this; } - public IChannel SubListen(IPeerContext context, IChannelRequest? req = null) + public IChannel SubDial(IChannelRequest? request = null) { - IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - if (subProtocolId is not IProtocol subProtocol) - { - throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - } - - Channel channel = new(); - ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - - _ = subProtocol.ListenAsync(channel.Reverse, channelFactory, context) - .ContinueWith(async task => - { - if (!task.IsCompletedSuccessfully) - { - _logger?.ListenFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); - } - await channel.CloseAsync(); - - req?.CompletionSource?.SetResult(); - }); - - return channel; + throw new NotImplementedException(); } - public Task SubDialAndBind(IChannel parent, IPeerContext context, - IChannelRequest? req = null) + public IChannel SubListen(IChannelRequest? request = null) { - IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - - if (subProtocolId is not IProtocol subProtocol) - { - throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - } - - ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - return subProtocol.DialAsync(((Channel)parent), channelFactory, context) - .ContinueWith(async task => - { - if (!task.IsCompletedSuccessfully) - { - _logger?.DialAndBindFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); - } - await parent.CloseAsync(); - - req?.CompletionSource?.SetResult(); - }); + throw new NotImplementedException(); } - public Task SubListenAndBind(IChannel parent, IPeerContext context, - IChannelRequest? req = null) + public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) { - IId? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - - if (subProtocolId is not IProtocol subProtocol) - { - throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - } - - ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - return subProtocol.ListenAsync(((Channel)parent), channelFactory, context) - .ContinueWith(async task => - { - await parent.CloseAsync(); - req?.CompletionSource?.SetResult(); - }); + throw new NotImplementedException(); } - public ChannelFactory Setup(IDictionary factories) + public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) { - _factories = factories; - return this; + throw new NotImplementedException(); } } diff --git a/src/libp2p/Libp2p.Core/ConnectionContext.cs b/src/libp2p/Libp2p.Core/ConnectionContext.cs new file mode 100644 index 00000000..26385c63 --- /dev/null +++ b/src/libp2p/Libp2p.Core/ConnectionContext.cs @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Libp2p.Core; + +//public class ConnectionContext(LocalPeer localPeer, ITransportProtocol transportProtocol) : ITransportConnectionContext +//{ + +// public CancellationToken Token => throw new NotImplementedException(); + +// public string Id => throw new NotImplementedException(); + +// public IEnumerable SubProtocols => throw new NotImplementedException(); + +// public Identity Identity => throw new NotImplementedException(); + +// public ISessionContext CreateSession() +// { +// throw new NotImplementedException(); +// } + +// public void Dispose() +// { +// throw new NotImplementedException(); +// } + +// public IChannel SubDial(IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } + +// public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } + +// public IChannel SubListen(IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } + +// public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } +//} diff --git a/src/libp2p/Libp2p.Core/IChannelFactory.cs b/src/libp2p/Libp2p.Core/IChannelFactory.cs index 66870136..5f4f8db7 100644 --- a/src/libp2p/Libp2p.Core/IChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/IChannelFactory.cs @@ -5,34 +5,33 @@ namespace Nethermind.Libp2p.Core; public interface IChannelFactory { - IEnumerable SubProtocols { get; } - IChannel SubDial(IPeerContext context, IChannelRequest? request = null); + IEnumerable SubProtocols { get; } + IChannel SubDial(IChannelRequest? request = null); - IChannel SubListen(IPeerContext context, IChannelRequest? request = null); + IChannel SubListen(IChannelRequest? request = null); - Task SubDialAndBind(IChannel parentChannel, IPeerContext context, IChannelRequest? request = null); + Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null); - Task SubListenAndBind(IChannel parentChannel, IPeerContext context, IChannelRequest? request = null); + Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null); - - IChannel SubDial(IPeerContext context, IProtocol protocol) + IChannel SubDial(IProtocol protocol) { - return SubDial(context, new ChannelRequest { SubProtocol = protocol }); + return SubDial(new ChannelRequest { SubProtocol = protocol }); } - IChannel SubListen(IPeerContext context, IProtocol protocol) + IChannel SubListen(IProtocol protocol) { - return SubListen(context, new ChannelRequest { SubProtocol = protocol }); + return SubListen(new ChannelRequest { SubProtocol = protocol }); } - Task SubDialAndBind(IChannel parentChannel, IPeerContext context, IProtocol protocol) + Task SubDialAndBind(IChannel parentChannel, IProtocol protocol) { - return SubDialAndBind(parentChannel, context, new ChannelRequest { SubProtocol = protocol }); + return SubDialAndBind(parentChannel, new ChannelRequest { SubProtocol = protocol }); } - Task SubListenAndBind(IChannel parentChannel, IPeerContext context, IProtocol protocol) + Task SubListenAndBind(IChannel parentChannel, IProtocol protocol) { - return SubListenAndBind(parentChannel, context, new ChannelRequest { SubProtocol = protocol }); + return SubListenAndBind(parentChannel, new ChannelRequest { SubProtocol = protocol }); } } diff --git a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs new file mode 100644 index 00000000..397bde8d --- /dev/null +++ b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; + +namespace Nethermind.Libp2p.Stack; + +public interface IBuilderContext +{ + Dictionary? Protocols { get; set; } + IProtocol[]? TopProtocols { get; set; } +} diff --git a/src/libp2p/Libp2p.Core/IListener.cs b/src/libp2p/Libp2p.Core/IListener.cs index 1e19c470..2c17e0a9 100644 --- a/src/libp2p/Libp2p.Core/IListener.cs +++ b/src/libp2p/Libp2p.Core/IListener.cs @@ -6,13 +6,5 @@ namespace Nethermind.Libp2p.Core; -public interface IListener -{ - Multiaddress Address { get; } - event OnConnection OnConnection; - Task DisconnectAsync(); - TaskAwaiter GetAwaiter(); -} -public delegate Task OnConnection(IRemotePeer peer); diff --git a/src/libp2p/Libp2p.Core/ILocalPeer.cs b/src/libp2p/Libp2p.Core/ILocalPeer.cs deleted file mode 100644 index 64c7e1eb..00000000 --- a/src/libp2p/Libp2p.Core/ILocalPeer.cs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Multiformats.Address; - -namespace Nethermind.Libp2p.Core; - -public interface ILocalPeer : IPeer -{ - Task DialAsync(Multiaddress addr, CancellationToken token = default); - Task ListenAsync(Multiaddress addr, CancellationToken token = default); -} diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index 148e9885..86d46d28 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -2,11 +2,28 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using System.Collections.ObjectModel; namespace Nethermind.Libp2p.Core; public interface IPeer { - Identity Identity { get; set; } - Multiaddress Address { get; set; } + Identity Identity { get; } + + Task DialAsync(Multiaddress addr, CancellationToken token = default); + + Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default); + + //Task DisconnectAsync(); + + ObservableCollection ListenAddresses { get; } + + //event OnConnection OnConnection; +} + +public delegate Task OnConnection(ISession peer); + +public interface ISession +{ + } diff --git a/src/libp2p/Libp2p.Core/IPeerContext.cs b/src/libp2p/Libp2p.Core/IPeerContext.cs index e9fe0425..cacd722e 100644 --- a/src/libp2p/Libp2p.Core/IPeerContext.cs +++ b/src/libp2p/Libp2p.Core/IPeerContext.cs @@ -2,34 +2,41 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; -using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core; -public interface IPeerContext +public interface IContext { string Id { get; } - IPeer LocalPeer { get; } - IPeer RemotePeer { get; } - - Multiaddress RemoteEndpoint { get; set; } - Multiaddress LocalEndpoint { get; set; } - - // TODO: Get rid of this: - IPeerContext Fork(); + IPeer Peer { get; } +} - #region Allows muxer to manage session and channels for the app protocols - BlockingCollection SubDialRequests { get; } +public interface ITransportContext : IContext +{ + void ListenerReady(Multiaddress addr); + ITransportConnectionContext CreateConnection(); +} - IChannelRequest? SpecificProtocolRequest { get; set; } +public interface ITransportConnectionContext : IDisposable, IChannelFactory, IContext +{ + CancellationToken Token { get; } + IConnectionSessionContext CreateSession(PeerId peerId); +} - event RemotePeerConnected OnRemotePeerConnection; - event ListenerReady OnListenerReady; +public interface IConnectionContext : IChannelFactory, IContext +{ + Task DisconnectAsync(); + IConnectionSessionContext CreateSession(PeerId peerId); +} - void Connected(IPeer peer); - void ListenerReady(); - #endregion +public interface IConnectionSessionContext : IDisposable +{ + string Id { get; } + IEnumerable DialRequests { get; } } -public delegate void RemotePeerConnected(IRemotePeer peer); -public delegate void ListenerReady(); +public interface ISessionContext : IChannelFactory, IContext +{ + Task DialAsync() where TProtocol: ISessionProtocol; + Task DisconnectAsync(); +} diff --git a/src/libp2p/Libp2p.Core/IPeerFactory.cs b/src/libp2p/Libp2p.Core/IPeerFactory.cs index c48bbba1..bed6ba98 100644 --- a/src/libp2p/Libp2p.Core/IPeerFactory.cs +++ b/src/libp2p/Libp2p.Core/IPeerFactory.cs @@ -7,5 +7,5 @@ namespace Nethermind.Libp2p.Core; public interface IPeerFactory { - ILocalPeer Create(Identity? identity = default, Multiaddress? localAddr = default); + IPeer Create(Identity? identity = default); } diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index 1bc77645..47de4d9d 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -1,9 +1,11 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Multiformats.Address; + namespace Nethermind.Libp2p.Core; -public interface IId +public interface IProtocol { /// /// Id used to during connection establishedment, exchanging information about protocol versions and so on @@ -11,29 +13,38 @@ public interface IId string Id { get; } } -public interface ITransportProtocol : IId +public interface ITransportProtocol : IProtocol { /// - /// Actively dials a peer + /// Opens a channel to listen to a remote peer /// /// A channel to communicate with a bottom layer protocol /// Factory that spawns new channels used to interact with top layer protocols /// Holds information about local and remote peers /// - Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context); + Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token); /// - /// Opens a channel to listen to a remote peer + /// Actively dials a peer /// /// A channel to communicate with a bottom layer protocol /// Factory that spawns new channels used to interact with top layer protocols /// Holds information about local and remote peers /// - Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context); + Task DialAsync(ITransportConnectionContext context, Multiaddress listenAddr, CancellationToken token); } -public interface IProtocol : IId +public interface IConnectionProtocol : IProtocol { + /// + /// Opens a channel to listen to a remote peer + /// + /// A channel to communicate with a bottom layer protocol + /// Factory that spawns new channels used to interact with top layer protocols + /// Holds information about local and remote peers + /// + Task ListenAsync(IChannel downChannel, IConnectionContext context); + /// /// Actively dials a peer /// @@ -41,7 +52,11 @@ public interface IProtocol : IId /// Factory that spawns new channels used to interact with top layer protocols /// Holds information about local and remote peers /// - Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context); + Task DialAsync(IChannel downChannel, IConnectionContext context); +} + +public interface ISessionProtocol : IProtocol +{ /// /// Opens a channel to listen to a remote peer @@ -50,5 +65,14 @@ public interface IProtocol : IId /// Factory that spawns new channels used to interact with top layer protocols /// Holds information about local and remote peers /// - Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context); + Task ListenAsync(IChannel downChannel, ISessionContext context); + + /// + /// Actively dials a peer + /// + /// A channel to communicate with a bottom layer protocol + /// Factory that spawns new channels used to interact with top layer protocols + /// Holds information about local and remote peers + /// + Task DialAsync(IChannel downChannel, ISessionContext context); } diff --git a/src/libp2p/Libp2p.Core/IReader.cs b/src/libp2p/Libp2p.Core/IReader.cs index fc167e7d..d8105178 100644 --- a/src/libp2p/Libp2p.Core/IReader.cs +++ b/src/libp2p/Libp2p.Core/IReader.cs @@ -12,7 +12,6 @@ public interface IReader { ValueTask ReadAsync(int length, ReadBlockingMode blockingMode = ReadBlockingMode.WaitAll, CancellationToken token = default); - #region Read helpers async IAsyncEnumerable> ReadAllAsync( [EnumeratorCancellation] CancellationToken token = default) diff --git a/src/libp2p/Libp2p.Core/IRemotePeer.cs b/src/libp2p/Libp2p.Core/IRemotePeer.cs index be9cf928..40a4bee9 100644 --- a/src/libp2p/Libp2p.Core/IRemotePeer.cs +++ b/src/libp2p/Libp2p.Core/IRemotePeer.cs @@ -1,10 +1,7 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Multiformats.Address; + namespace Nethermind.Libp2p.Core; -public interface IRemotePeer : IPeer -{ - Task DialAsync(CancellationToken token = default) where TProtocol : IProtocol; - Task DisconnectAsync(); -} diff --git a/src/libp2p/Libp2p.Core/Ids.cs b/src/libp2p/Libp2p.Core/Ids.cs new file mode 100644 index 00000000..0e0ad8fa --- /dev/null +++ b/src/libp2p/Libp2p.Core/Ids.cs @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Core; +public static class Ids +{ + public static int IdCounter = 0; +} diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs new file mode 100644 index 00000000..4a52f36d --- /dev/null +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -0,0 +1,382 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; +using Nethermind.Libp2p.Core.Exceptions; +using Nethermind.Libp2p.Stack; +using Org.BouncyCastle.Tls; +using System.Collections.Concurrent; +using System.Collections.ObjectModel; + +namespace Nethermind.Libp2p.Core; + +public class LocalPeer(IBuilderContext builderContext, Identity identity) : IPeer +{ + public Identity Identity { get; } = identity; + + public ObservableCollection ListenAddresses { get; } = new(); + public ObservableCollection Sessions { get; } = new(); + + public class Session(LocalPeer localPeer) : ISession + { + public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + + + + public Task DialAsync(CancellationToken token = default) where TProtocol : IProtocol + { + TaskCompletionSource tcs = new(); + SubDialRequests.Add(new ChannelRequest() { CompletionSource = tcs, SubProtocol = localPeer.GetProtocolInstance() }); + return tcs.Task; + } + + public Task DialAsync(IProtocol[] protocols, CancellationToken token = default) + { + TaskCompletionSource tcs = new(); + SubDialRequests.Add(new ChannelRequest() { CompletionSource = tcs, SubProtocol = protocols[0] }); + return tcs.Task; + } + + private CancellationTokenSource connectionTokenSource = new(); + + public CancellationToken ConnectionToken => connectionTokenSource.Token; + + + private TaskCompletionSource ConnectedTcs = new(); + + public Task Connected => ConnectedTcs.Task; + + public PeerId PeerId { get; set; } + + private BlockingCollection SubDialRequests = new(); + + internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); + + internal Task DisconnectAsync() + { + return Task.CompletedTask; + } + } + + protected virtual IProtocol SelectProtocol(Multiaddress addr) + { + if (builderContext.TopProtocols is null) + { + throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + } + + if (builderContext.TopProtocols.Length is not 1) + { + throw new Libp2pSetupException("Top protocol should be single one by default"); + + } + + return builderContext.TopProtocols.Single(); + } + + Dictionary> listenerReadyTcs = new(); + + public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) + { + List listenTasks = new(addrs.Length); + + foreach (Multiaddress addr in addrs) + { + IProtocol listenerProtocol = SelectProtocol(addr); + + if (listenerProtocol is not ITransportProtocol transportProtocol) + { + throw new Libp2pSetupException($"{nameof(ITransportProtocol)} should be implemented by {listenerProtocol.GetType()}"); + } + + ITransportContext ctx = new TransportContext(this, transportProtocol); + TaskCompletionSource tcs = new(); + listenerReadyTcs[ctx] = tcs; + + _ = transportProtocol.ListenAsync(ctx, addr, token).ContinueWith(t => ListenAddresses.Remove(tcs.Task.Result)); + + listenTasks.Add(tcs.Task.WaitAsync(TimeSpan.FromMilliseconds(5000))); + ListenAddresses.Add(tcs.Task.Result); + } + + await Task.WhenAll(listenTasks); + } + + public ITransportContext CreateContext(ITransportProtocol tProto) + { + return new TransportContext(this, tProto); + } + + public void ListenerReady(ITransportContext sender, Multiaddress addr) + { + if (listenerReadyTcs.Remove(sender, out TaskCompletionSource? tcs)) + { + tcs.SetResult(addr); + } + } + + public ITransportConnectionContext CreateConnection(ITransportProtocol proto) + { + Session session = new(this); + Sessions.Add(session); + return new TransportConnectionContext(this, session, proto); + } + + public IConnectionSessionContext CreateSession(Session session, PeerId peerId) + { + lock (Sessions) + { + if(Sessions.Any(s => !object.ReferenceEquals(session, s) && s.PeerId == peerId)) + { + throw new Libp2pException("Session is already established"); + } + + session.PeerId = peerId; + return new ConnectionSessionContext(this, session); + } + } + + internal IEnumerable GetProtocolsFor(IProtocol protocol) + { + if (builderContext.Protocols is null) + { + throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + } + + if (!builderContext.Protocols.ContainsKey(protocol)) + { + throw new Libp2pSetupException($"{protocol} is noty added"); + } + + return builderContext.Protocols[protocol]; + } + + internal IProtocol? GetProtocolInstance() + { + return builderContext.Protocols?.Keys.FirstOrDefault(p => p.GetType() == typeof(TProtocol)); + } + + public async Task DialAsync(Multiaddress addr, CancellationToken token = default) + { + IProtocol dialerProtocol = SelectProtocol(addr); + + if (dialerProtocol is not ITransportProtocol transportProtocol) + { + throw new Libp2pSetupException($"{nameof(ITransportProtocol)} should be implemented by {dialerProtocol.GetType()}"); + } + + Session session = new(this); + ITransportConnectionContext ctx = new TransportConnectionContext(this, session, transportProtocol); + + _ = transportProtocol.DialAsync(ctx, addr, token); + + await session.Connected; + return session; + } + + internal IChannel SubDial(LocalPeer localPeer, Session session, IProtocol protocol, IChannelRequest? request) + { + if (builderContext.Protocols is null) + { + throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + } + + if (!builderContext.Protocols.ContainsKey(protocol)) + { + throw new Libp2pSetupException($"{protocol} is noty added"); + } + + IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + + Channel res = new Channel(); + if (top is IConnectionProtocol tProto) + { + var ctx = new ConnectionContext(this, session, top); + _ = tProto.DialAsync(res.Reverse, ctx); + } + if (top is ISessionProtocol sProto) + { + var ctx = new SessionContext(this, session, top); + _ = sProto.DialAsync(res.Reverse, ctx); + } + return res; + } + + internal IChannel SubListen(LocalPeer localPeer, Session session, IProtocol protocol, IChannelRequest? request) + { + if (builderContext.Protocols is null) + { + throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + } + + if (!builderContext.Protocols.ContainsKey(protocol)) + { + throw new Libp2pSetupException($"{protocol} is noty added"); + } + + IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + + Channel res = new Channel(); + if (top is IConnectionProtocol tProto) + { + var ctx = new ConnectionContext(this, session, top); + _ = tProto.ListenAsync(res.Reverse, ctx); + } + if (top is ISessionProtocol sProto) + { + var ctx = new SessionContext(this, session, top); + _ = sProto.ListenAsync(res.Reverse, ctx); + } + return res; + } + + internal async Task SubDialAndBind(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IChannelRequest? request) + { + if (builderContext.Protocols is null) + { + throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + } + + if (!builderContext.Protocols.ContainsKey(protocol)) + { + throw new Libp2pSetupException($"{protocol} is noty added"); + } + + IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + + if (top is IConnectionProtocol tProto) + { + var ctx = new ConnectionContext(this, session, top); + await tProto.DialAsync(parentChannel, ctx); + } + if (top is ISessionProtocol sProto) + { + var ctx = new SessionContext(this, session, top); + await sProto.DialAsync(parentChannel, ctx); + } + } + + + internal async Task SubListenAndBind(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IChannelRequest? request) + { + if (builderContext.Protocols is null) + { + throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + } + + if (!builderContext.Protocols.ContainsKey(protocol)) + { + throw new Libp2pSetupException($"{protocol} is noty added"); + } + + IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + + if (top is IConnectionProtocol tProto) + { + var ctx = new ConnectionContext(this, session, top); + await tProto.ListenAsync(parentChannel, ctx); + } + if (top is ISessionProtocol sProto) + { + var ctx = new SessionContext(this, session, top); + await sProto.ListenAsync(parentChannel, ctx); + } + } + + internal void DisposeConnection(TransportConnectionContext transportConnectionContext, Session session) + { + Sessions.Remove(session); + } + + internal void DisposeSession(Session session) + { + + } +} + +public class ConnectionSessionContext(LocalPeer localPeer, LocalPeer.Session session) : IConnectionSessionContext +{ + public IEnumerable DialRequests => session.GetRequestQueue(); + + public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + + public void Dispose() + { + localPeer.DisposeSession(session); + } +} + +public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), ISessionContext +{ + public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol + { + return session.DialAsync(token); + } + + public Task DisconnectAsync() + { + return session.DisconnectAsync(); + } +} + +public class TransportConnectionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), ITransportConnectionContext +{ + public CancellationToken Token => session.ConnectionToken; + + public IConnectionSessionContext CreateSession(PeerId peerId) + { + return localPeer.CreateSession(session, peerId); + } + + public void Dispose() + { + localPeer.DisposeConnection(this, session); + } +} + +public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), IConnectionContext +{ + public IConnectionSessionContext CreateSession(PeerId peerId) + { + return localPeer.CreateSession(session, peerId); + } + + public Task DisconnectAsync() + { + return session.DisconnectAsync(); + } +} + + +public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) +{ + public IPeer Peer => localPeer; + + public IEnumerable SubProtocols => localPeer.GetProtocolsFor(protocol); + + public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + + protected LocalPeer localPeer = localPeer; + protected LocalPeer.Session session = session; + protected IProtocol protocol = protocol; + + public IChannel SubDial(IChannelRequest? request = null) + { + return localPeer.SubDial(localPeer, session, protocol, request); + } + + public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) + { + return localPeer.SubDialAndBind(localPeer, session, parentChannel, protocol, request); + } + + public IChannel SubListen(IChannelRequest? request = null) + { + return localPeer.SubListen(localPeer, session, protocol, request); + } + + public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) + { + return localPeer.SubListenAndBind(localPeer, session, parentChannel, protocol, request); + } +} diff --git a/src/libp2p/Libp2p.Core/PeerContext.cs b/src/libp2p/Libp2p.Core/PeerContext.cs index 19abb63c..9c51f356 100644 --- a/src/libp2p/Libp2p.Core/PeerContext.cs +++ b/src/libp2p/Libp2p.Core/PeerContext.cs @@ -6,34 +6,41 @@ namespace Nethermind.Libp2p.Core; -public class PeerContext : IPeerContext -{ - public string Id { get; set; } - public IPeer LocalPeer { get; set; } - public IPeer RemotePeer { get; set; } - public Multiaddress RemoteEndpoint { get; set; } - public Multiaddress LocalEndpoint { get; set; } - public BlockingCollection SubDialRequests { get; set; } = new(); - public IChannelRequest? SpecificProtocolRequest { get; set; } - - public IPeerContext Fork() - { - PeerContext result = (PeerContext)MemberwiseClone(); - result.RemotePeer = ((PeerFactory.RemotePeer)RemotePeer).Fork(); - return result; - } - - - - public event RemotePeerConnected? OnRemotePeerConnection; - public void Connected(IPeer peer) - { - OnRemotePeerConnection?.Invoke((IRemotePeer)peer); - } - - public event ListenerReady? OnListenerReady; - public void ListenerReady() - { - OnListenerReady?.Invoke(); - } -} +//public class PeerContext : IPeerContext +//{ + + +// public string Id { get; set; } +// public IPeerInfo LocalPeer { get; set; } +// public IPeerInfo RemotePeer { get; set; } +// public Multiaddress RemoteEndpoint { get; set; } +// public Multiaddress LocalEndpoint { get; set; } +// public BlockingCollection SubDialRequests { get; set; } = new(); +// public IChannelRequest? SpecificProtocolRequest { get; set; } + +// public IPeerContext Fork() +// { +// PeerContext result = (PeerContext)MemberwiseClone(); +// result.RemotePeer = ((PeerFactory.RemotePeer)RemotePeer).Fork(); +// return result; +// } + + + +// public event RemotePeerConnected? OnRemotePeerConnection; +// public void Connected(IPeerInfo peer) +// { +// OnRemotePeerConnection?.Invoke((ISession)peer); +// } + +// public event ListenerReady? OnListenerReady; +// public void ListenerReady() +// { +// OnListenerReady?.Invoke(); +// } + +// public void Dispose() +// { +// throw new NotImplementedException(); +// } +//} diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 692cd0e3..139f297e 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -1,245 +1,14 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; -using Multiformats.Address.Protocols; -using Nethermind.Libp2p.Core.Exceptions; -using System.Runtime.CompilerServices; +using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; -public abstract class PeerFactory : IPeerFactory +public class PeerFactory(IBuilderContext builderContext) : IPeerFactory { - private readonly IServiceProvider _serviceProvider; - - private IId _protocol; - private IChannelFactory _upChannelFactory; - private static int CtxId = 0; - - public PeerFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public abstract ILocalPeer Create(Identity? identity = default, Multiaddress? localAddr = default); - - /// - /// PeerFactory interface ctor - /// - /// - /// - /// - /// - public void Setup(IId protocol, IChannelFactory upChannelFactory) - { - _protocol = protocol; - _upChannelFactory = upChannelFactory; - } - - private async Task ListenAsync(LocalPeer peer, Multiaddress addr, CancellationToken token) - { - peer.Address = addr; - if (!peer.Address.Has()) - { - peer.Address = peer.Address.Add(peer.Identity.PeerId.ToString()); - } - - Channel chan = new(); - if (token != default) - { - token.Register(() => chan.CloseAsync()); - } - - TaskCompletionSource ts = new(); - - - PeerContext peerContext = new() - { - Id = $"ctx-{++CtxId}", - LocalPeer = peer, - }; - - peerContext.OnListenerReady += OnListenerReady; - - void OnListenerReady() - { - ts.SetResult(); - peerContext.OnListenerReady -= OnListenerReady; - } - - RemotePeer remotePeer = new(this, peer, peerContext); - peerContext.RemotePeer = remotePeer; - - PeerListener result = new(chan, peer); - peerContext.OnRemotePeerConnection += remotePeer => - { - if (((RemotePeer)remotePeer).LocalPeer != peer) - { - return; - } - - ConnectedTo(remotePeer, false) - .ContinueWith(t => { result.RaiseOnConnection(remotePeer); }, token); - }; - - _ = _protocol switch - { - IProtocol protocol => _ = protocol.ListenAsync(chan, _upChannelFactory, peerContext), - ITransportProtocol protocol => _ = protocol.ListenAsync(chan, _upChannelFactory, peerContext), - _ => throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {_protocol.GetType()}"), - }; - - await ts.Task; - return result; - } - - protected virtual Task ConnectedTo(IRemotePeer peer, bool isDialer) - { - return Task.CompletedTask; - } - - private Task DialAsync(IPeerContext peerContext, CancellationToken token) where TProtocol : IProtocol - { - TaskCompletionSource cts = new(token); - peerContext.SubDialRequests.Add(new ChannelRequest - { - SubProtocol = PeerFactoryBuilderBase.CreateProtocolInstance(_serviceProvider), - CompletionSource = cts - }); - return cts.Task; - } - - protected virtual async Task DialAsync(LocalPeer peer, Multiaddress addr, CancellationToken token) - { - try - { - Channel chan = new(); - token.Register(() => _ = chan.CloseAsync()); - - PeerContext context = new() - { - Id = $"ctx-{++CtxId}", - LocalPeer = peer, - }; - RemotePeer result = new(this, peer, context) { Address = addr, Channel = chan }; - context.RemotePeer = result; - - TaskCompletionSource tcs = new(); - RemotePeerConnected remotePeerConnected = null!; - - remotePeerConnected = remotePeer => - { - if (((RemotePeer)remotePeer).LocalPeer != peer) - { - return; - } - - ConnectedTo(remotePeer, true).ContinueWith((t) => { tcs.TrySetResult(true); }); - context.OnRemotePeerConnection -= remotePeerConnected; - }; - context.OnRemotePeerConnection += remotePeerConnected; - - _ = _protocol switch - { - IProtocol protocol => _ = protocol.DialAsync(chan, _upChannelFactory, context), - ITransportProtocol protocol => _ = protocol.DialAsync(chan, _upChannelFactory, context), - _ => throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {_protocol.GetType()}"), - }; - - await tcs.Task; - return result; - } - catch - { - throw; - } - } - - private class PeerListener : IListener - { - private readonly Channel _chan; - private readonly LocalPeer _localPeer; - - public PeerListener(Channel chan, LocalPeer localPeer) - { - _chan = chan; - _localPeer = localPeer; - } - - public event OnConnection? OnConnection; - public Multiaddress Address => _localPeer.Address; - - public Task DisconnectAsync() - { - return _chan.CloseAsync().AsTask(); - } - - public TaskAwaiter GetAwaiter() - { - return _chan.GetAwaiter(); - } - - internal void RaiseOnConnection(IRemotePeer peer) - { - OnConnection?.Invoke(peer); - } - } - - protected class LocalPeer : ILocalPeer + public virtual IPeer Create(Identity? identity = default) { - private readonly PeerFactory _factory; - - public LocalPeer(PeerFactory factory) - { - _factory = factory; - } - - public Identity? Identity { get; set; } - public Multiaddress Address { get; set; } - - public Task DialAsync(Multiaddress addr, CancellationToken token = default) - { - return _factory.DialAsync(this, addr, token); - } - - public Task ListenAsync(Multiaddress addr, CancellationToken token = default) - { - return _factory.ListenAsync(this, addr, token); - } - } - - internal class RemotePeer : IRemotePeer - { - private readonly PeerFactory _factory; - private readonly IPeerContext peerContext; - - public RemotePeer(PeerFactory factory, ILocalPeer localPeer, IPeerContext peerContext) - { - _factory = factory; - LocalPeer = localPeer; - this.peerContext = peerContext; - } - - public Channel Channel { get; set; } - - public Identity Identity { get; set; } - public Multiaddress Address { get; set; } - internal ILocalPeer LocalPeer { get; } - - public Task DialAsync(CancellationToken token = default) where TProtocol : IProtocol - { - return _factory.DialAsync(peerContext, token); - } - - public Task DisconnectAsync() - { - return Channel.CloseAsync().AsTask(); - } - - public IPeer Fork() - { - return (IPeer)MemberwiseClone(); - } + return new LocalPeer(builderContext, identity ?? new Identity()); } } - diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 70370395..658cca78 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -2,21 +2,22 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.DependencyInjection; +using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; public static class PeerFactoryBuilderBase { - private static HashSet protocols = new(); + private static HashSet protocols = new(); - internal static TProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IId + internal static TProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol { if (instance is not null) { protocols.Add(instance); } - IId? existing = instance ?? protocols.OfType().FirstOrDefault(); + IProtocol? existing = instance ?? protocols.OfType().FirstOrDefault(); if (existing is null) { existing = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); @@ -28,7 +29,7 @@ internal static TProtocol CreateProtocolInstance(IServiceProvider ser public abstract class PeerFactoryBuilderBase : IPeerFactoryBuilder where TBuilder : PeerFactoryBuilderBase, IPeerFactoryBuilder - where TPeerFactory : PeerFactory + where TPeerFactory : IPeerFactory { private readonly List _appLayerProtocols = new(); public IEnumerable AppLayerProtocols { get => _appLayerProtocols; } @@ -42,7 +43,7 @@ protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) ServiceProvider = serviceProvider ?? new ServiceCollection().BuildServiceProvider(); } - protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IId + protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol { return new ProtocolStack(this, ServiceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance)); } @@ -59,13 +60,13 @@ protected class ProtocolStack private readonly IServiceProvider serviceProvider; public ProtocolStack? Root { get; private set; } - public ProtocolStack? Parent { get; private set; } + public ProtocolStack Parent { get; private set; } public ProtocolStack? PrevSwitch { get; private set; } - public IId Protocol { get; } + public IProtocol Protocol { get; } public HashSet TopProtocols { get; } = new(); public ChannelFactory UpChannelsFactory { get; } - public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IId protocol) + public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IProtocol protocol) { this.builder = builder; this.serviceProvider = serviceProvider; @@ -79,7 +80,7 @@ public ProtocolStack AddAppLayerProtocol(TProtocol? instance = defaul return this; } - public ProtocolStack Over(TProtocol? instance = default) where TProtocol : IId + public ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol { ProtocolStack nextNode = new(builder, serviceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance)); return Over(nextNode); @@ -89,9 +90,10 @@ public ProtocolStack Or(TProtocol? instance = default) where TProtoco { if (Parent is null) { - throw new NotImplementedException(); + Parent = new ProtocolStack(builder, serviceProvider, new RootStub()); + Parent.Over(this); } - IId protocol = PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance); + IProtocol protocol = PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance); ProtocolStack stack = new(builder, serviceProvider, protocol); return Or(stack); } @@ -116,7 +118,8 @@ public ProtocolStack Or(ProtocolStack stack) { if (Parent is null) { - throw new NotImplementedException(); + Parent = new ProtocolStack(builder, serviceProvider, new RootStub()); + Parent.Over(this); } stack.PrevSwitch = this; return Parent.Over(stack); @@ -147,10 +150,12 @@ public IPeerFactory Build() throw new ApplicationException("Root protocol is not properly defined"); } + Dictionary protocols = new(); + static void SetupChannelFactories(ProtocolStack root) { - root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols - .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); + root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols + .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); foreach (ProtocolStack topProto in root.TopProtocols) { if (!root.TopProtocols.Any()) @@ -163,8 +168,11 @@ static void SetupChannelFactories(ProtocolStack root) SetupChannelFactories(root); + IBuilderContext builderContext = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); + builderContext.Protocols = protocols;//root.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol] + TPeerFactory result = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); - result.Setup(root?.Protocol!, root!.UpChannelsFactory); + return result; } @@ -178,4 +186,9 @@ public override string ToString() return (IsSelector ? "(selector)" : "") + string.Join(",", Protocols.Select(p => p.Id)); } } + + class RootStub : IProtocol + { + public string Id => "protocol hierachy root"; + } } diff --git a/src/libp2p/Libp2p.Core/SymetricProtocol.cs b/src/libp2p/Libp2p.Core/SymetricProtocol.cs index 3ca90875..d583b0a7 100644 --- a/src/libp2p/Libp2p.Core/SymetricProtocol.cs +++ b/src/libp2p/Libp2p.Core/SymetricProtocol.cs @@ -5,16 +5,16 @@ namespace Nethermind.Libp2p.Core; public abstract class SymmetricProtocol { - public Task DialAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) - { - return ConnectAsync(channel, channelFactory, context, false); - } + //public Task DialAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) + //{ + // return ConnectAsync(channel, channelFactory, context, false); + //} - public Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) - { - return ConnectAsync(channel, channelFactory, context, true); - } + //public Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) + //{ + // return ConnectAsync(channel, channelFactory, context, true); + //} - protected abstract Task ConnectAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context, bool isListener); + //protected abstract Task ConnectAsync(IChannel channel, IChannelFactory? channelFactory, + // IPeerContext context, bool isListener); } diff --git a/src/libp2p/Libp2p.Core/TransportContext.cs b/src/libp2p/Libp2p.Core/TransportContext.cs new file mode 100644 index 00000000..c39e3893 --- /dev/null +++ b/src/libp2p/Libp2p.Core/TransportContext.cs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; + +namespace Nethermind.Libp2p.Core; + +public class TransportContext(LocalPeer peer, ITransportProtocol proto) : ITransportContext +{ + public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + public Identity Identity => peer.Identity; + + public void ListenerReady(Multiaddress addr) + { + peer.ListenerReady(this, addr); + } + + public ITransportConnectionContext CreateConnection() + { + return peer.CreateConnection(proto); + } +} diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index a937e00c..6b5ef4d3 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -17,61 +17,39 @@ public class IpTcpProtocol(ILoggerFactory? loggerFactory = null) : ITransportPro public string Id => "ip-tcp"; - public async Task ListenAsync(IChannel signalingChannel, IChannelFactory? channelFactory, IPeerContext context) + public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) { - if (channelFactory is null) - { - throw new Exception("Protocol is not properly instantiated"); - } + Socket listener = new(SocketType.Stream, ProtocolType.Tcp); - Multiaddress addr = context.LocalPeer.Address; - bool isIP4 = addr.Has(); - MultiaddressProtocol ipProtocol = isIP4 ? addr.Get() : addr.Get(); - IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); - int tcpPort = int.Parse(addr.Get().ToString()); + IPEndPoint endpoint = ToEndPoint(listenAddr); - Socket srv = new(SocketType.Stream, ProtocolType.Tcp); - srv.Bind(new IPEndPoint(ipAddress, tcpPort)); - srv.Listen(tcpPort); - signalingChannel.GetAwaiter().OnCompleted(() => - { - srv.Close(); - }); + listener.Bind(endpoint); + listener.Listen(); - IPEndPoint localIpEndpoint = (IPEndPoint)srv.LocalEndPoint!; - - - Multiaddress localMultiaddress = new(); - localMultiaddress = isIP4 ? localMultiaddress.Add(localIpEndpoint.Address.MapToIPv4()) : localMultiaddress.Add(localIpEndpoint.Address.MapToIPv6()); - localMultiaddress = localMultiaddress.Add(localIpEndpoint.Port); - context.LocalEndpoint = localMultiaddress; - - if (tcpPort == 0) + if (endpoint.Port is 0) { - context.LocalPeer.Address = context.LocalPeer.Address - .ReplaceOrAdd(localIpEndpoint.Port); + IPEndPoint localIpEndpoint = (IPEndPoint)listener.LocalEndPoint!; + listenAddr.Add(localIpEndpoint.Port); } + token.Register(listener.Close); + + _logger?.LogDebug("Ready to handle connections"); - context.ListenerReady(); + context.ListenerReady(listenAddr); await Task.Run(async () => { for (; ; ) { - Socket client = await srv.AcceptAsync(); - IPeerContext clientContext = context.Fork(); - IPEndPoint remoteIpEndpoint = (IPEndPoint)client.RemoteEndPoint!; + Socket client = await listener.AcceptAsync(); - Multiaddress remoteMultiaddress = new(); - remoteMultiaddress = isIP4 ? remoteMultiaddress.Add(remoteIpEndpoint.Address.MapToIPv4()) : remoteMultiaddress.Add(remoteIpEndpoint.Address.MapToIPv6()); - remoteMultiaddress = remoteMultiaddress.Add(remoteIpEndpoint.Port); + ITransportConnectionContext connectionCtx = context.CreateConnection(client.Close); + connectionCtx.Token.Register(client.Close); - clientContext.RemoteEndpoint = clientContext.RemotePeer.Address = remoteMultiaddress; + IChannel upChannel = connectionCtx.SubListen(); - IChannel upChannel = channelFactory.SubListen(clientContext); - - _ = Task.Run(async () => + Task readTask = Task.Run(async () => { try { @@ -84,14 +62,8 @@ await Task.Run(async () => byte[] buf = new byte[client.ReceiveBufferSize]; int length = await client.ReceiveAsync(buf, SocketFlags.None); - if (length != 0) - { - if ((await upChannel.WriteAsync(new ReadOnlySequence(buf.AsMemory()[..length]))) != IOResult.Ok) - { - break; - } - } - else + + if (length is 0 || await upChannel.WriteAsync(new ReadOnlySequence(buf.AsMemory()[..length])) != IOResult.Ok) { break; } @@ -102,7 +74,8 @@ await Task.Run(async () => await upChannel.CloseAsync(); } }); - _ = Task.Run(async () => + + Task writeTask = Task.Run(async () => { try { @@ -111,6 +84,7 @@ await Task.Run(async () => int sent = await client.SendAsync(data.ToArray(), SocketFlags.None); if (sent is 0 || !client.Connected) { + await upChannel.CloseAsync(); break; } } @@ -121,61 +95,36 @@ await Task.Run(async () => await upChannel.CloseAsync(); } }); + + _ = Task.WhenAll(readTask, writeTask).ContinueWith(_ => connectionCtx.Dispose()); } }); } - public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelFactory, IPeerContext context) + public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) { - if (channelFactory is null) - { - throw new ProtocolViolationException(); - } - Socket client = new(SocketType.Stream, ProtocolType.Tcp); - Multiaddress addr = context.RemotePeer.Address; - MultiaddressProtocol ipProtocol = addr.Has() ? addr.Get() : addr.Get(); - IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); - int tcpPort = addr.Get().Port; - _logger?.LogDebug("Dialing {0}:{1}", ipAddress, tcpPort); + IPEndPoint remoteEndpoint = ToEndPoint(remoteAddr); + _logger?.LogDebug("Dialing {0}:{1}", remoteEndpoint.Address, remoteEndpoint.Port); try { - await client.ConnectAsync(new IPEndPoint(ipAddress, tcpPort), signalingChannel.CancellationToken); + await client.ConnectAsync(remoteEndpoint, token); } catch (SocketException e) { - _logger?.LogDebug($"Failed({context.Id}) to connect {addr}"); + _logger?.LogDebug($"Failed({context.Id}) to connect {remoteAddr}"); _logger?.LogTrace($"Failed with {e.GetType()}: {e.Message}"); - _ = signalingChannel.CloseAsync(); return; } - signalingChannel.GetAwaiter().OnCompleted(() => - { - client.Close(); - }); - - IPEndPoint localEndpoint = (IPEndPoint)client.LocalEndPoint!; - IPEndPoint remoteEndpoint = (IPEndPoint)client.RemoteEndPoint!; - - var isIP4 = addr.Has(); - - var remoteMultiaddress = new Multiaddress(); - var remoteIpAddress = isIP4 ? remoteEndpoint.Address.MapToIPv4() : remoteEndpoint.Address.MapToIPv6(); - remoteMultiaddress = isIP4 ? remoteMultiaddress.Add(remoteIpAddress) : remoteMultiaddress.Add(remoteIpAddress); - context.RemoteEndpoint = remoteMultiaddress.Add(remoteEndpoint.Port); + using ITransportConnectionContext connectionCtx = context.CreateConnection(client.Close); + connectionCtx.Token.Register(client.Close); + token.Register(client.Close); - var localMultiaddress = new Multiaddress(); - var localIpAddress = isIP4 ? localEndpoint.Address.MapToIPv4() : localEndpoint.Address.MapToIPv6(); - localMultiaddress = isIP4 ? localMultiaddress.Add(localIpAddress) : localMultiaddress.Add(localIpAddress); - context.LocalEndpoint = localMultiaddress.Add(localEndpoint.Port); - - context.LocalPeer.Address = context.LocalEndpoint.Add(context.LocalPeer.Identity.PeerId.ToString()); - - IChannel upChannel = channelFactory.SubDial(context); + IChannel upChannel = connectionCtx.SubDial(); Task receiveTask = Task.Run(async () => { @@ -185,20 +134,13 @@ public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelF for (; client.Connected;) { int dataLength = await client.ReceiveAsync(buf, SocketFlags.None); - if (dataLength != 0) - { - _logger?.LogDebug("Receive {0} data, len={1}", context.Id, dataLength); - if ((await upChannel.WriteAsync(new ReadOnlySequence(buf[..dataLength]))) != IOResult.Ok) - { - break; - } - } - else + _logger?.LogDebug("Receive {0} data, len={1}", connectionCtx.Id, dataLength); + + if (dataLength == 0 || (await upChannel.WriteAsync(new ReadOnlySequence(buf[..dataLength]))) != IOResult.Ok) { break; } } - } catch (SocketException) { @@ -212,7 +154,7 @@ public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelF { await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) { - _logger?.LogDebug("Send {0} data, len={1}", context.Id, data.Length); + _logger?.LogDebug("Send {0} data, len={1}", connectionCtx.Id, data.Length); int sent = await client.SendAsync(data.ToArray(), SocketFlags.None); if (sent is 0 || !client.Connected) { @@ -226,7 +168,16 @@ public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelF } }); - await Task.WhenAll(receiveTask, sendTask); + await Task.WhenAll(receiveTask, sendTask).ContinueWith(t => connectionCtx.Dispose()); + _ = upChannel.CloseAsync(); } + + private static IPEndPoint ToEndPoint(Multiaddress addr) + { + MultiaddressProtocol ipProtocol = addr.Has() ? addr.Get() : addr.Get(); + IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); + int tcpPort = int.Parse(addr.Get().ToString()); + return new IPEndPoint(ipAddress, tcpPort); + } } diff --git a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs index cc682465..0dcaf85a 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs @@ -22,7 +22,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() proto1.Id.Returns("proto1"); channelFactory.SubProtocols.Returns(new[] { proto1 }); IChannel upChannel = new TestChannel(); - channelFactory.SubDialAndBind(Arg.Any(), Arg.Any(), Arg.Any()) + channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) .Returns(Task.CompletedTask); MultistreamProtocol proto = new(); @@ -38,7 +38,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() await dialTask; - _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, peerContext, proto1); + _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); await downChannel.CloseAsync(); } @@ -57,7 +57,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake_With_SpecificRequest channelRequest.SubProtocol.Returns(proto1); IChannel upChannel = new TestChannel(); - channelFactory.SubDialAndBind(Arg.Any(), Arg.Any(), Arg.Any()) + channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) .Returns(Task.CompletedTask); MultistreamProtocol proto = new(); @@ -73,7 +73,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake_With_SpecificRequest await dialTask; - _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, peerContext, proto1); + _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); await downChannel.CloseAsync(); } @@ -104,7 +104,7 @@ public async Task Test_ConnectionClosed_ForUnknownProtocol() await dialTask; - _ = channelFactory.DidNotReceive().SubDialAndBind(downChannelFromProtocolPov, peerContext, proto1); + _ = channelFactory.DidNotReceive().SubDialAndBind(downChannelFromProtocolPov, proto1); } [Test] @@ -122,7 +122,7 @@ public async Task Test_ConnectionEstablished_ForAnyOfProtocols() proto2.Id.Returns("proto2"); channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); IChannel upChannel = new TestChannel(); - channelFactory.SubDialAndBind(Arg.Any(), Arg.Any(), Arg.Any()) + channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) .Returns(Task.CompletedTask); MultistreamProtocol proto = new(); @@ -140,7 +140,7 @@ public async Task Test_ConnectionEstablished_ForAnyOfProtocols() await dialTask; - _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, peerContext, proto2); + _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto2); await upChannel.CloseAsync(); } @@ -173,6 +173,6 @@ public async Task Test_ConnectionClosed_ForBadProtocol() await dialTask; - _ = channelFactory.DidNotReceiveWithAnyArgs().SubDialAndBind(null!, null!, (IProtocol)null!); + _ = channelFactory.DidNotReceiveWithAnyArgs().SubDialAndBind(null!, (IProtocol)null!); } } diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index 437cf2d1..f5beff14 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -80,7 +80,8 @@ public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, return; } _logger?.LogDebug($"Protocol selected during dialing: {selected}"); - await channelFactory.SubDialAndBind(channel, context, selected); + //await channelFactory.SubDialAndBind(channel, context, selected); + throw new NotImplementedException(); } public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, @@ -115,7 +116,8 @@ public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, } _logger?.LogDebug($"Protocol selected during listening: {selected}"); - await channelFactory.SubListenAndBind(channel, context, selected); + // await channelFactory.SubListenAndBind(channel, context, selected); + throw new NotImplementedException(); } private async Task SendHello(IChannel channel) diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index d4bf27ed..82c32ef8 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -93,7 +93,7 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFact _logger?.LogDebug("Established connection to {peer}", context.RemotePeer.Address); - IChannel upChannel = upChannelFactory.SubDial(context); + IChannel upChannel = upChannelFactory.SubDial(); await ExchangeData(transport, downChannel, upChannel); @@ -153,7 +153,7 @@ public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFa _logger?.LogDebug("Established connection to {peer}", context.RemotePeer.Address); - IChannel upChannel = upChannelFactory.SubListen(context); + IChannel upChannel = upChannelFactory.SubListen(); await ExchangeData(transport, downChannel, upChannel); diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs index 6dc12e34..af957bea 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs @@ -36,7 +36,7 @@ protected override async Task ConnectAsync(IChannel channel, IChannelFactory? ch Exchange? dest = Exchange.Parser.ParseFrom(buf); await (isListener - ? channelFactory.SubListenAndBind(channel, context) - : channelFactory.SubDialAndBind(channel, context)); + ? channelFactory.SubListenAndBind(channel) + : channelFactory.SubDialAndBind(channel)); } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index fda59a2c..ef4d8d06 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -20,7 +20,7 @@ public async Task Test_Peer_is_in_fpeers() Multiaddress localPeerAddr = TestPeers.Multiaddr(2); const string commonTopic = "topic1"; - ILocalPeer peer = Substitute.For(); + IPeer peer = Substitute.For(); peer.Address.Returns(localPeerAddr); peer.DialAsync(discoveredPeer, Arg.Any()).Returns(new TestRemotePeer(discoveredPeer)); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs index cb0e293a..4dafc27c 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs @@ -19,7 +19,7 @@ public async Task Test_New_messages_are_sent_to_mesh_only() int peerCount = Settings.Default.Degree * 2; const string commonTopic = "topic1"; - ILocalPeer peer = new TestLocalPeer(); + IPeer peer = new TestLocalPeer(); TestDiscoveryProtocol discovery = new(); CancellationToken token = default; List sentRpcs = new(); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs index 2b1d99a3..dda26e43 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs @@ -1,36 +1,36 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Multiformats.Address; - -namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; - -[TestFixture] -public class PubsubProtocolTests -{ - [Test] - public async Task Test_Peer_is_dialed_when_added_by_discovery() - { - PubsubRouter router = new(); - IRoutingStateContainer state = router; - Multiaddress discoveredPeer = TestPeers.Multiaddr(1); - PeerId peerId = TestPeers.PeerId(1); - Multiaddress localPeer = TestPeers.Multiaddr(2); - - ILocalPeer peer = Substitute.For(); - peer.Address.Returns(localPeer); - peer.DialAsync(discoveredPeer, Arg.Any()).Returns(new TestRemotePeer(discoveredPeer)); - - TestDiscoveryProtocol discovery = new(); - CancellationToken token = default; - - _ = router.RunAsync(peer, discovery, token: token); - discovery.OnAddPeer!([discoveredPeer]); - - await Task.Delay(100); - _ = peer.Received().DialAsync(discoveredPeer, Arg.Any()); - - router.OutboundConnection(discoveredPeer, PubsubRouter.FloodsubProtocolVersion, Task.CompletedTask, (rpc) => { }); - Assert.That(state.ConnectedPeers, Has.Member(peerId)); - } -} +//// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +//// SPDX-License-Identifier: MIT + +//using Multiformats.Address; + +//namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; + +//[TestFixture] +//public class PubsubProtocolTests +//{ +// [Test] +// public async Task Test_Peer_is_dialed_when_added_by_discovery() +// { +// PubsubRouter router = new(); +// IRoutingStateContainer state = router; +// Multiaddress discoveredPeer = TestPeers.Multiaddr(1); +// PeerId peerId = TestPeers.PeerId(1); +// Multiaddress localPeer = TestPeers.Multiaddr(2); + +// IPeer peer = Substitute.For(); +// peer.Address.Returns(localPeer); +// peer.DialAsync(discoveredPeer, Arg.Any()).Returns(new TestRemotePeer(discoveredPeer)); + +// TestDiscoveryProtocol discovery = new(); +// CancellationToken token = default; + +// _ = router.RunAsync(peer, discovery, token: token); +// discovery.OnAddPeer!([discoveredPeer]); + +// await Task.Delay(100); +// _ = peer.Received().DialAsync(discoveredPeer, Arg.Any()); + +// router.OutboundConnection(discoveredPeer, PubsubRouter.FloodsubProtocolVersion, Task.CompletedTask, (rpc) => { }); +// Assert.That(state.ConnectedPeers, Has.Member(peerId)); +// } +//} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs b/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs index 6caa80f1..f8c98f72 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs @@ -4,9 +4,9 @@ using Nethermind.Libp2p.Core; namespace Nethermind.Libp2p.Protocols.Pubsub; -internal class ManagedPeer(ILocalPeer peer) +internal class ManagedPeer(IPeer peer) { - internal async Task DialAsync(Multiaddress[] addrs, CancellationToken token) + internal async Task DialAsync(Multiaddress[] addrs, CancellationToken token) { Dictionary cancellations = new(); foreach (Multiaddress addr in addrs) @@ -15,7 +15,7 @@ internal async Task DialAsync(Multiaddress[] addrs, CancellationTok } Task timoutTask = Task.Delay(15_000, token); - Task> firstConnectedTask = Task.WhenAny(addrs + Task> firstConnectedTask = Task.WhenAny(addrs .Select(addr => peer.DialAsync(addr, cancellations[addr].Token))); Task wait = await Task.WhenAny(firstConnectedTask, timoutTask); @@ -25,7 +25,7 @@ internal async Task DialAsync(Multiaddress[] addrs, CancellationTok throw new TimeoutException(); } - IRemotePeer firstConnected = firstConnectedTask.Result.Result; + ISession firstConnected = firstConnectedTask.Result.Result; foreach (KeyValuePair c in cancellations) { diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 0748613b..20486456 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -118,7 +118,7 @@ public Action? SendRpc private TtlCache limboMessageCache; private TtlCache<(PeerId, MessageId)> dontWantMessages; - private ILocalPeer? localPeer; + private IPeer? localPeer; private ManagedPeer peer; private ILogger? logger = loggerFactory?.CreateLogger(); @@ -150,7 +150,7 @@ static PubsubRouter() Canceled = cts.Token; } - public async Task RunAsync(ILocalPeer localPeer, IDiscoveryProtocol discoveryProtocol, Settings? settings = null, CancellationToken token = default) + public async Task RunAsync(IPeer localPeer, IDiscoveryProtocol discoveryProtocol, Settings? settings = null, CancellationToken token = default) { if (this.localPeer is not null) { @@ -195,14 +195,14 @@ private async Task StartDiscoveryAsync(IDiscoveryProtocol discoveryProtocol, Can { try { - IRemotePeer remotePeer = await peer.DialAsync(addrs, token); + ISession session = await peer.DialAsync(addrs, token); - if (!peerState.ContainsKey(remotePeer.Address.Get().ToString())) + if (!peerState.ContainsKey(session.Address.Get().ToString())) { - await remotePeer.DialAsync(token); - if (peerState.TryGetValue(remotePeer.Address.GetPeerId()!, out PubsubPeer? state) && state.InititatedBy == ConnectionInitiation.Remote) + await session.DialAsync(token); + if (peerState.TryGetValue(session.Address.GetPeerId()!, out PubsubPeer? state) && state.InititatedBy == ConnectionInitiation.Remote) { - _ = remotePeer.DisconnectAsync(); + _ = session.DisconnectAsync(); } } } @@ -232,7 +232,7 @@ private async Task Reconnect(CancellationToken token) { try { - IRemotePeer remotePeer = await peer.DialAsync(rec.Addresses, token); + ISession remotePeer = await peer.DialAsync(rec.Addresses, token); await remotePeer.DialAsync(token); } catch diff --git a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs index cb21858a..d08cb91d 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs @@ -43,22 +43,16 @@ public QuicProtocol(ILoggerFactory? loggerFactory = null) public string Id => "quic-v1"; - public async Task ListenAsync(IChannel signalingChannel, IChannelFactory? channelFactory, IPeerContext context) + public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, CancellationToken token) { - if (channelFactory is null) - { - throw new ArgumentException($"The protocol requires {nameof(channelFactory)}"); - } - if (!QuicListener.IsSupported) { throw new NotSupportedException("QUIC is not supported, check for presence of libmsquic and support of TLS 1.3."); } - Multiaddress addr = context.LocalPeer.Address; - MultiaddressProtocol ipProtocol = addr.Has() ? addr.Get() : addr.Get(); + MultiaddressProtocol ipProtocol = localAddr.Has() ? localAddr.Get() : localAddr.Get(); IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); - int udpPort = int.Parse(addr.Get().ToString()); + int udpPort = int.Parse(localAddr.Get().ToString()); IPEndPoint localEndpoint = new(ipAddress, udpPort); @@ -70,8 +64,8 @@ public async Task ListenAsync(IChannel signalingChannel, IChannelFactory? channe ServerAuthenticationOptions = new SslServerAuthenticationOptions { ApplicationProtocols = protocols, - RemoteCertificateValidationCallback = (_, c, _, _) => VerifyRemoteCertificate(context.RemotePeer, c), - ServerCertificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.LocalPeer.Identity) + RemoteCertificateValidationCallback = (_, c, _, _) => true, + ServerCertificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.Identity) }, }; @@ -82,39 +76,25 @@ public async Task ListenAsync(IChannel signalingChannel, IChannelFactory? channe ConnectionOptionsCallback = (_, _, _) => ValueTask.FromResult(serverConnectionOptions) }); - var localEndPoint = new Multiaddress(); - // IP (4 or 6 is based on source address). - var strLocalEndpoint = listener.LocalEndPoint.Address.ToString(); - localEndPoint = addr.Has() ? localEndPoint.Add(strLocalEndpoint) : localEndPoint.Add(strLocalEndpoint); - - // UDP - localEndPoint = localEndPoint.Add(listener.LocalEndPoint.Port); - - // Set on context - context.LocalEndpoint = localEndPoint; - if (udpPort == 0) { - context.LocalPeer.Address = context.LocalPeer.Address - .ReplaceOrAdd(listener.LocalEndPoint.Port); + localAddr = localAddr.ReplaceOrAdd(listener.LocalEndPoint.Port); } + context.ListenerReady(localAddr); _logger?.ReadyToHandleConnections(); - context.ListenerReady(); - TaskAwaiter signalingWawaiter = signalingChannel.GetAwaiter(); - signalingWawaiter.OnCompleted(() => - { - listener.DisposeAsync(); - }); + token.Register(() => _ = listener.DisposeAsync()); - while (!signalingWawaiter.IsCompleted) + while (!token.IsCancellationRequested) { try { QuicConnection connection = await listener.AcceptConnectionAsync(); - _ = ProcessStreams(connection, context.Fork(), channelFactory); + ITransportConnectionContext clientContext = context.CreateConnection(() => _ = connection.CloseAsync(0)); + + _ = ProcessStreams(clientContext, connection, token).ContinueWith(t => clientContext.Dispose()); } catch (Exception ex) { @@ -124,37 +104,24 @@ public async Task ListenAsync(IChannel signalingChannel, IChannelFactory? channe } } - public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelFactory, IPeerContext context) + public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) { - if (channelFactory is null) - { - throw new ArgumentException($"The protocol requires {nameof(channelFactory)}"); - } - if (!QuicConnection.IsSupported) { throw new NotSupportedException("QUIC is not supported, check for presence of libmsquic and support of TLS 1.3."); } - Multiaddress addr = context.LocalPeer.Address; + Multiaddress addr = remoteAddr; bool isIp4 = addr.Has(); MultiaddressProtocol protocol = isIp4 ? addr.Get() : addr.Get(); + IPAddress ipAddress = IPAddress.Parse(protocol.ToString()); int udpPort = int.Parse(addr.Get().ToString()); - IPEndPoint localEndpoint = new(ipAddress, udpPort); - - addr = context.RemotePeer.Address; - isIp4 = addr.Has(); - protocol = isIp4 ? addr.Get() : addr.Get(); - ipAddress = IPAddress.Parse(protocol.ToString()!); - udpPort = int.Parse(addr.Get().ToString()!); - IPEndPoint remoteEndpoint = new(ipAddress, udpPort); QuicClientConnectionOptions clientConnectionOptions = new() { - LocalEndPoint = localEndpoint, DefaultStreamErrorCode = 0, // Protocol-dependent error code. DefaultCloseErrorCode = 1, // Protocol-dependent error code. MaxInboundUnidirectionalStreams = 256, @@ -163,8 +130,8 @@ public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelF { TargetHost = null, ApplicationProtocols = protocols, - RemoteCertificateValidationCallback = (_, c, _, _) => VerifyRemoteCertificate(context.RemotePeer, c), - ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.LocalPeer.Identity) }, + RemoteCertificateValidationCallback = (_, c, _, _) => VerifyRemoteCertificate(remoteAddr, c), + ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.Identity) }, }, RemoteEndPoint = remoteEndpoint, }; @@ -173,52 +140,27 @@ public async Task DialAsync(IChannel signalingChannel, IChannelFactory? channelF _logger?.Connected(connection.LocalEndPoint, connection.RemoteEndPoint); - signalingChannel.GetAwaiter().OnCompleted(() => - { - connection.CloseAsync(0); - }); + token.Register(() => _ = connection.CloseAsync(0)); + using ITransportConnectionContext clientContext = context.CreateConnection(() => _ = connection.CloseAsync(0)); - await ProcessStreams(connection, context, channelFactory); + await ProcessStreams(clientContext, connection, token); } - private static bool VerifyRemoteCertificate(IPeer? remotePeer, X509Certificate certificate) => - CertificateHelper.ValidateCertificate(certificate as X509Certificate2, remotePeer?.Address.Get().ToString()); + private static bool VerifyRemoteCertificate(Multiaddress remoteAddr, X509Certificate certificate) => + CertificateHelper.ValidateCertificate(certificate as X509Certificate2, remoteAddr.Get().ToString()); - private async Task ProcessStreams(QuicConnection connection, IPeerContext context, IChannelFactory channelFactory, CancellationToken token = default) + private async Task ProcessStreams(ITransportConnectionContext context, QuicConnection connection, CancellationToken token = default) { _logger?.LogDebug("New connection to {remote}", connection.RemoteEndPoint); - bool isIP4 = connection.LocalEndPoint.AddressFamily == AddressFamily.InterNetwork; - - Multiaddress localEndPointMultiaddress = new(); - string strLocalEndpointAddress = connection.LocalEndPoint.Address.ToString(); - localEndPointMultiaddress = isIP4 ? localEndPointMultiaddress.Add(strLocalEndpointAddress) : localEndPointMultiaddress.Add(strLocalEndpointAddress); - localEndPointMultiaddress = localEndPointMultiaddress.Add(connection.LocalEndPoint.Port); - - context.LocalEndpoint = localEndPointMultiaddress; - - context.LocalPeer.Address = isIP4 ? context.LocalPeer.Address.ReplaceOrAdd(strLocalEndpointAddress) : context.LocalPeer.Address.ReplaceOrAdd(strLocalEndpointAddress); - - IPEndPoint remoteIpEndpoint = connection.RemoteEndPoint!; - isIP4 = remoteIpEndpoint.AddressFamily == AddressFamily.InterNetwork; - - Multiaddress remoteEndPointMultiaddress = new(); - string strRemoteEndpointAddress = remoteIpEndpoint.Address.ToString(); - remoteEndPointMultiaddress = isIP4 ? remoteEndPointMultiaddress.Add(strRemoteEndpointAddress) : remoteEndPointMultiaddress.Add(strRemoteEndpointAddress); - remoteEndPointMultiaddress = remoteEndPointMultiaddress.Add(remoteIpEndpoint.Port); - - context.RemoteEndpoint = remoteEndPointMultiaddress; - - context.Connected(context.RemotePeer); + using ISessionContext session = context.CreateSession(); _ = Task.Run(async () => { - foreach (IChannelRequest request in context.SubDialRequests.GetConsumingEnumerable()) + foreach (IChannelRequest request in session.SubDialRequests.GetConsumingEnumerable()) { QuicStream stream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); - IPeerContext dialContext = context.Fork(); - dialContext.SpecificProtocolRequest = request; - IChannel upChannel = channelFactory.SubDial(dialContext); + IChannel upChannel = session.SubDial(request); ExchangeData(stream, upChannel, request.CompletionSource); } }, token); @@ -226,7 +168,7 @@ private async Task ProcessStreams(QuicConnection connection, IPeerContext contex while (!token.IsCancellationRequested) { QuicStream inboundStream = await connection.AcceptInboundStreamAsync(token); - IChannel upChannel = channelFactory.SubListen(context); + IChannel upChannel = session.SubListen(); ExchangeData(inboundStream, upChannel, null); } } diff --git a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs index 673c7fdc..1a088ad9 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs @@ -34,7 +34,7 @@ public async Task Test_Protocol_Communication() IChannelFactory dialerUpchannelFactory = Substitute.For(); dialerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); TestChannel dialerUpChannel = new TestChannel(); - dialerUpchannelFactory.SubDial(Arg.Any(), Arg.Any()) + dialerUpchannelFactory.SubDial(Arg.Any()) .Returns(dialerUpChannel); @@ -47,7 +47,7 @@ public async Task Test_Protocol_Communication() listenerPeerContext.SubDialRequests.Returns(listenerRequests); listenerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); TestChannel listenerUpChannel = new TestChannel(); - listenerUpchannelFactory.SubListen(Arg.Any(), Arg.Any()) + listenerUpchannelFactory.SubListen(Arg.Any()) .Returns(listenerUpChannel); YamuxProtocol proto = new(loggerFactory: new DebugLoggerFactory()); diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 805193ad..762d4389 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -221,13 +221,13 @@ ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, ICha if (isListenerChannel) { - upChannel = channelFactory.SubListen(context); + upChannel = channelFactory.SubListen(); } else { IPeerContext dialContext = context.Fork(); dialContext.SpecificProtocolRequest = channelRequest; - upChannel = channelFactory.SubDial(dialContext); + upChannel = channelFactory.SubDial(); } ChannelState state = new(upChannel, channelRequest); diff --git a/src/libp2p/Libp2p/Libp2pBuilderContext.cs b/src/libp2p/Libp2p/Libp2pBuilderContext.cs new file mode 100644 index 00000000..fd4269a5 --- /dev/null +++ b/src/libp2p/Libp2p/Libp2pBuilderContext.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; + +namespace Nethermind.Libp2p.Stack; + +internal class Libp2pBuilderContext : IBuilderContext +{ + public IProtocol[]? TopProtocols { get; set; } +} diff --git a/src/libp2p/Libp2p/Libp2pLocalPeer.cs b/src/libp2p/Libp2p/Libp2pLocalPeer.cs index 22d98a14..5ae2723e 100644 --- a/src/libp2p/Libp2p/Libp2pLocalPeer.cs +++ b/src/libp2p/Libp2p/Libp2pLocalPeer.cs @@ -9,6 +9,6 @@ using System.Threading.Tasks; namespace Nethermind.Libp2p; -//internal class Libp2pLocalPeer: ILocalPeer +//internal class Libp2pLocalPeer: IPeer //{ //} diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index e055efdf..53cb729d 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -14,12 +14,32 @@ public Libp2pPeerFactory(IServiceProvider serviceProvider) : base(serviceProvide { } - protected override async Task ConnectedTo(IRemotePeer peer, bool isDialer) + protected override async Task ConnectedTo(ISession peer, bool isDialer) { await peer.DialAsync(); } - public override ILocalPeer Create(Identity? identity = null, Multiaddress? localAddr = null) + protected override IProtocol SelectProtocol(Multiaddress addr) + { + ITransportProtocol protocol = null!; + + if (addr.Has()) + { + protocol = TopProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); + } + else if (addr.Has()) + { + protocol = TopProtocols!.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); + } + else + { + throw new NotImplementedException($"No transport protocol found for the given address: {addr}"); + } + + return protocol; + } + + public override IPeer Create(Identity? identity = null) { identity ??= new Identity(); localAddr ??= $"/ip4/0.0.0.0/tcp/0/p2p/{identity.PeerId}"; diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index e2be774e..39ef94d2 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -38,8 +38,7 @@ protected override ProtocolStack BuildStack() .Over(); return - Over() - .Over().Or(tcpStack) + Over().Or(tcpStack) .Over() .AddAppLayerProtocol() //.AddAppLayerProtocol() diff --git a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs index 5e59cfc3..70a2e987 100644 --- a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs +++ b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.Logging; +using Multiformats.Address; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Stack; @@ -17,41 +18,38 @@ public class MultiaddressBasedSelector(ILoggerFactory? loggerFactory = null): IT public string Id => "multiaddr-select"; - public Task DialAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) + public Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) { - return ConnectAsync(channel, channelFactory, context, false); + return ConnectAsync(context, listenAddr, token, false); } - public Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) + public Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) { - return ConnectAsync(channel, channelFactory, context, true); + return ConnectAsync(context, listenAddr, token, true); } - protected async Task ConnectAsync(IChannel _, IChannelFactory? channelFactory, IPeerContext context, bool isListener) + protected async Task ConnectAsync(ITransportContext context, Multiaddress addr, CancellationToken token, bool isListener) { - ITransportProtocol protocol = null!; - // TODO: deprecate quic - if (context.LocalPeer.Address.Has()) - { - protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); - } - else if (context.LocalPeer.Address.Has()) - { - protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); - } - else if (context.LocalPeer.Address.Has()) - { - throw new ApplicationException("QUIC is not supported. Use QUICv1 instead."); - } - else - { - throw new NotImplementedException($"No transport protocol found for the given address: {context.LocalPeer.Address}"); - } - - _logger?.LogPickedProtocol(protocol.Id, isListener ? "listen" : "dial"); - - await (isListener - ? protocol.ListenAsync(_, channelFactory, context) - : protocol.DialAsync(_, channelFactory, context)); + throw new NotImplementedException(); + //ITransportProtocol protocol = null!; + + //if (addr.Has()) + //{ + // protocol = context!.SubProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); + //} + //else if (addr.Has()) + //{ + // protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); + //} + //else + //{ + // throw new NotImplementedException($"No transport protocol found for the given address: {addr}"); + //} + + //_logger?.LogPickedProtocol(protocol.Id, isListener ? "listen" : "dial"); + + //await (isListener + // ? protocol.ListenAsync(context, addr, token) + // : protocol.DialAsync(context, addr, token)); } } diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index f9b56cab..0dd52997 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -11,11 +11,13 @@ namespace Nethermind.Libp2p.Stack; public static class ServiceProviderExtensions { [RequiresPreviewFeatures] - public static IServiceCollection AddLibp2p(this IServiceCollection services, Func factorySetup) + public static IServiceCollection AddLibp2p(this IServiceCollection services, Func? factorySetup = null) { return services - .AddScoped(sp => factorySetup(new Libp2pPeerFactoryBuilder(sp))) - .AddScoped(sp => (ILibp2pPeerFactoryBuilder)factorySetup(new Libp2pPeerFactoryBuilder(sp))) + .AddSingleton(sp => new Libp2pPeerFactoryBuilder(sp)) + .AddSingleton() + .AddSingleton(sp => factorySetup is null ? sp.GetRequiredService() : factorySetup(sp.GetRequiredService())) + .AddSingleton(sp => (ILibp2pPeerFactoryBuilder)sp.GetRequiredService()) .AddScoped(sp => sp.GetService()!.Build()) .AddScoped() .AddScoped() diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index 8b0f9129..cb547422 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -32,10 +32,10 @@ "/ip4/0.0.0.0/udp/0/quic-v1" : "/ip4/0.0.0.0/tcp/0"; - ILocalPeer localPeer = peerFactory.Create(localAddr: addrTemplate); + IPeer localPeer = peerFactory.Create(localAddr: addrTemplate); logger.LogInformation("Dialing {remote}", remoteAddr); - IRemotePeer remotePeer = await localPeer.DialAsync(remoteAddr, ts.Token); + ISession remotePeer = await localPeer.DialAsync(remoteAddr, ts.Token); await remotePeer.DialAsync(ts.Token); await remotePeer.DisconnectAsync(); @@ -43,7 +43,7 @@ else { Identity optionalFixedIdentity = new(Enumerable.Repeat((byte)42, 32).ToArray()); - ILocalPeer peer = peerFactory.Create(optionalFixedIdentity); + IPeer peer = peerFactory.Create(optionalFixedIdentity); string addrTemplate = args.Contains("-quic") ? "/ip4/0.0.0.0/udp/{0}/quic-v1" : diff --git a/src/samples/perf-benchmarks/Program.cs b/src/samples/perf-benchmarks/Program.cs index 5ff5cc51..9e27d432 100644 --- a/src/samples/perf-benchmarks/Program.cs +++ b/src/samples/perf-benchmarks/Program.cs @@ -18,13 +18,13 @@ IPeerFactory peerFactory = serviceProvider.GetService()!; Identity optionalFixedIdentity = new(Enumerable.Repeat((byte)42, 32).ToArray()); - ILocalPeer peer = peerFactory.Create(optionalFixedIdentity); + IPeer peer = peerFactory.Create(optionalFixedIdentity); IListener listener = await peer.ListenAsync($"/ip4/0.0.0.0/tcp/0/p2p/{peer.Identity.PeerId}"); Multiaddress remoteAddr = listener.Address; - ILocalPeer localPeer = peerFactory.Create(); - IRemotePeer remotePeer = await localPeer.DialAsync(remoteAddr); + IPeer localPeer = peerFactory.Create(); + ISession remotePeer = await localPeer.DialAsync(remoteAddr); Stopwatch timeSpent = Stopwatch.StartNew(); await remotePeer.DialAsync(); @@ -41,12 +41,12 @@ .AddAppLayerProtocol() .Build(); - ILocalPeer peer = peerFactory.Create(); + IPeer peer = peerFactory.Create(); IListener listener = await peer.ListenAsync($"/ip4/0.0.0.0/tcp/0"); Multiaddress remoteAddr = listener.Address; - ILocalPeer localPeer = peerFactory.Create(); - IRemotePeer remotePeer = await localPeer.DialAsync(remoteAddr); + IPeer localPeer = peerFactory.Create(); + ISession remotePeer = await localPeer.DialAsync(remoteAddr); Stopwatch timeSpent = Stopwatch.StartNew(); await remotePeer.DialAsync(); diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 135216d4..765528d2 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -30,7 +30,7 @@ Identity localPeerIdentity = new(); string addr = $"/ip4/0.0.0.0/tcp/0/p2p/{localPeerIdentity.PeerId}"; -ILocalPeer peer = peerFactory.Create(localPeerIdentity, Multiaddress.Decode(addr)); +IPeer peer = peerFactory.Create(localPeerIdentity, Multiaddress.Decode(addr)); PubsubRouter router = serviceProvider.GetService()!; ITopic topic = router.Subscribe("chat-room:awesome-chat-room"); diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index a477a74d..090c1f1e 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -33,7 +33,7 @@ if (isDialer) { - ILocalPeer localPeer = peerFactory.Create(localAddr: builder.MakeAddress()); + IPeer localPeer = peerFactory.Create(localAddr: builder.MakeAddress()); Log($"Picking an address to dial..."); @@ -46,7 +46,7 @@ Log($"Dialing {listenerAddr}..."); Stopwatch handshakeStartInstant = Stopwatch.StartNew(); - IRemotePeer remotePeer = await localPeer.DialAsync(listenerAddr); + ISession remotePeer = await localPeer.DialAsync(listenerAddr); Stopwatch pingIstant = Stopwatch.StartNew(); await remotePeer.DialAsync(); @@ -81,7 +81,7 @@ ip = addresses.First().Address.ToString()!; } Log("Starting to listen..."); - ILocalPeer localPeer = peerFactory.Create(localAddr: builder.MakeAddress(ip)); + IPeer localPeer = peerFactory.Create(localAddr: builder.MakeAddress(ip)); IListener listener = await localPeer.ListenAsync(localPeer.Address); listener.OnConnection += (peer) => { Log($"Connected {peer.Address}"); return Task.CompletedTask; }; Log($"Listening on {listener.Address}"); From e4a98f73265f494207d13b89234b405a657f4be0 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Tue, 13 Aug 2024 18:06:18 +0300 Subject: [PATCH 03/25] Protocols --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 12 +- .../Libp2p.Core.TestsBase/TestLocalPeer.cs | 4 +- .../Libp2p.Core/ILibp2pBuilderContext.cs | 2 +- src/libp2p/Libp2p.Core/IPeer.cs | 8 +- src/libp2p/Libp2p.Core/IPeerContext.cs | 16 + src/libp2p/Libp2p.Core/Peer.cs | 87 +++-- src/libp2p/Libp2p.Core/PeerFactory.cs | 6 +- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 4 +- src/libp2p/Libp2p.Core/SymetricProtocol.cs | 33 +- src/libp2p/Libp2p.Core/TransportContext.cs | 2 + .../IdentifyProtocol.cs | 14 +- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 16 +- .../MultistreamProtocolTests.cs | 332 +++++++++--------- .../MultistreamProtocol.cs | 16 +- .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 23 +- .../Libp2p.Protocols.Ping/PingProtocol.cs | 8 +- .../PlainTextProtocol.cs | 11 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 10 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 2 +- .../Libp2p.Protocols.Quic/QuicProtocol.cs | 20 +- .../YamuxProtocolTests.cs | 56 +-- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 20 +- src/libp2p/Libp2p/Libp2pBuilderContext.cs | 3 +- src/libp2p/Libp2p/Libp2pPeerFactory.cs | 32 +- .../MultiaddressBasedSelectorProtocol.cs | 82 ++--- .../Libp2p/ServiceProviderExtensions.cs | 2 +- src/samples/chat/ChatProtocol.cs | 5 +- src/samples/chat/Program.cs | 16 +- src/samples/perf-benchmarks/PerfProtocol.cs | 6 +- src/samples/perf-benchmarks/Program.cs | 8 +- src/samples/pubsub-chat/Program.cs | 4 +- src/samples/transport-interop/Program.cs | 16 +- 32 files changed, 452 insertions(+), 424 deletions(-) diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index 96aa7689..f343868c 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -21,7 +21,7 @@ public async Task E2e() IConnectionProtocol cProto = new CProto(); ISessionProtocol sProto = new SProto(); - BuilderContext builderContext = new BuilderContext + BuilderContext protocolStackSettings = new BuilderContext { Protocols = new Dictionary { @@ -32,8 +32,8 @@ public async Task E2e() TopProtocols = [tProto] }; - LocalPeer peer1 = new LocalPeer(builderContext, new Identity()); - LocalPeer peer2 = new LocalPeer(builderContext, new Identity()); + LocalPeer peer1 = new LocalPeer(protocolStackSettings, new Identity()); + LocalPeer peer2 = new LocalPeer(protocolStackSettings, new Identity()); await peer1.StartListenAsync([new Multiaddress()]); await peer2.StartListenAsync([new Multiaddress()]); @@ -63,7 +63,7 @@ public async Task E2e() } } -class BuilderContext : IBuilderContext +class BuilderContext : IProtocolStackSettings { public Dictionary? Protocols { get; set; } = new Dictionary { }; public IProtocol[]? TopProtocols { get; set; } = []; @@ -143,7 +143,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) { try { - using ISession session = context.CreateSession(new PeerId(new Dto.PublicKey())); + using IConnectionSessionContext session = context.CreateSession(new PeerId(new Dto.PublicKey())); IChannel topChan = context.SubDial(); ReadResult received; @@ -174,7 +174,7 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { try { - using ISession session = context.CreateSession(); + using IConnectionSessionContext session = context.CreateSession(new PeerId(new Dto.PublicKey())); IChannel topChan = context.SubListen(); ReadResult received; diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs index 3cc2077d..db6def17 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs @@ -19,6 +19,8 @@ public TestLocalPeer() public ObservableCollection ListenAddresses => throw new NotImplementedException(); + public event OnConnection OnConnection; + public Task DialAsync(Multiaddress addr, CancellationToken token = default) { return Task.FromResult(new TestRemotePeer(addr)); @@ -46,7 +48,7 @@ public TestRemotePeer(Multiaddress addr) public Identity Identity { get; set; } public Multiaddress Address { get; set; } - public Task DialAsync(CancellationToken token = default) where TProtocol : IProtocol + public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { return Task.CompletedTask; } diff --git a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs index 397bde8d..739a704f 100644 --- a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs +++ b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs @@ -5,7 +5,7 @@ namespace Nethermind.Libp2p.Stack; -public interface IBuilderContext +public interface IProtocolStackSettings { Dictionary? Protocols { get; set; } IProtocol[]? TopProtocols { get; set; } diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index 86d46d28..4ed3ece5 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -9,6 +9,7 @@ namespace Nethermind.Libp2p.Core; public interface IPeer { Identity Identity { get; } + Multiaddress Address { get; } Task DialAsync(Multiaddress addr, CancellationToken token = default); @@ -18,12 +19,15 @@ public interface IPeer ObservableCollection ListenAddresses { get; } - //event OnConnection OnConnection; + event OnConnection OnConnection; } -public delegate Task OnConnection(ISession peer); +public delegate Task OnConnection(ISession newSession); public interface ISession { + Multiaddress Address { get; } + Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol; + Task DisconnectAsync(); } diff --git a/src/libp2p/Libp2p.Core/IPeerContext.cs b/src/libp2p/Libp2p.Core/IPeerContext.cs index cacd722e..2aadaafd 100644 --- a/src/libp2p/Libp2p.Core/IPeerContext.cs +++ b/src/libp2p/Libp2p.Core/IPeerContext.cs @@ -11,6 +11,21 @@ public interface IContext IPeer Peer { get; } } +public interface IConnectionContext +{ + string Id { get; } + IPeer Peer { get; } + IRemotePeer RemotePeer { get; } +} + +public interface IRemotePeer +{ + Identity Identity { get; set; } + Multiaddress Address { get; set; } +} + + + public interface ITransportContext : IContext { void ListenerReady(Multiaddress addr); @@ -38,5 +53,6 @@ public interface IConnectionSessionContext : IDisposable public interface ISessionContext : IChannelFactory, IContext { Task DialAsync() where TProtocol: ISessionProtocol; + Task DialAsync(ISessionProtocol[] protocols); Task DisconnectAsync(); } diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 4a52f36d..e7fbf4bd 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -10,27 +10,32 @@ namespace Nethermind.Libp2p.Core; -public class LocalPeer(IBuilderContext builderContext, Identity identity) : IPeer +public class LocalPeer(IProtocolStackSettings protocolStackSettings, Identity identity) : IPeer { + + protected IProtocolStackSettings protocolStackSettings = protocolStackSettings; + public Identity Identity { get; } = identity; public ObservableCollection ListenAddresses { get; } = new(); public ObservableCollection Sessions { get; } = new(); + public Multiaddress Address => throw new NotImplementedException(); - public class Session(LocalPeer localPeer) : ISession + protected virtual Task ConnectedTo(ISession peer, bool isDialer) => Task.CompletedTask; + + public class Session(LocalPeer localPeer) : ISession, IRemotePeer { + public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); - - - public Task DialAsync(CancellationToken token = default) where TProtocol : IProtocol + public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { TaskCompletionSource tcs = new(); SubDialRequests.Add(new ChannelRequest() { CompletionSource = tcs, SubProtocol = localPeer.GetProtocolInstance() }); return tcs.Task; } - public Task DialAsync(IProtocol[] protocols, CancellationToken token = default) + public Task DialAsync(ISessionProtocol[] protocols, CancellationToken token = default) { TaskCompletionSource tcs = new(); SubDialRequests.Add(new ChannelRequest() { CompletionSource = tcs, SubProtocol = protocols[0] }); @@ -48,11 +53,15 @@ public Task DialAsync(IProtocol[] protocols, CancellationToken token = default) public PeerId PeerId { get; set; } + public Identity Identity { get; set; } + + public Multiaddress Address { get; set; } + private BlockingCollection SubDialRequests = new(); internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); - internal Task DisconnectAsync() + public Task DisconnectAsync() { return Task.CompletedTask; } @@ -60,22 +69,24 @@ internal Task DisconnectAsync() protected virtual IProtocol SelectProtocol(Multiaddress addr) { - if (builderContext.TopProtocols is null) + if (protocolStackSettings.TopProtocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (builderContext.TopProtocols.Length is not 1) + if (protocolStackSettings.TopProtocols.Length is not 1) { throw new Libp2pSetupException("Top protocol should be single one by default"); } - return builderContext.TopProtocols.Single(); + return protocolStackSettings.TopProtocols.Single(); } Dictionary> listenerReadyTcs = new(); + public event OnConnection OnConnection; + public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) { List listenTasks = new(addrs.Length); @@ -138,22 +149,22 @@ public IConnectionSessionContext CreateSession(Session session, PeerId peerId) internal IEnumerable GetProtocolsFor(IProtocol protocol) { - if (builderContext.Protocols is null) + if (protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (!builderContext.Protocols.ContainsKey(protocol)) + if (!protocolStackSettings.Protocols.ContainsKey(protocol)) { throw new Libp2pSetupException($"{protocol} is noty added"); } - return builderContext.Protocols[protocol]; + return protocolStackSettings.Protocols[protocol]; } internal IProtocol? GetProtocolInstance() { - return builderContext.Protocols?.Keys.FirstOrDefault(p => p.GetType() == typeof(TProtocol)); + return protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.GetType() == typeof(TProtocol)); } public async Task DialAsync(Multiaddress addr, CancellationToken token = default) @@ -176,17 +187,17 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token internal IChannel SubDial(LocalPeer localPeer, Session session, IProtocol protocol, IChannelRequest? request) { - if (builderContext.Protocols is null) + if (protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (!builderContext.Protocols.ContainsKey(protocol)) + if (!protocolStackSettings.Protocols.ContainsKey(protocol)) { throw new Libp2pSetupException($"{protocol} is noty added"); } - IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); Channel res = new Channel(); if (top is IConnectionProtocol tProto) @@ -204,17 +215,17 @@ internal IChannel SubDial(LocalPeer localPeer, Session session, IProtocol protoc internal IChannel SubListen(LocalPeer localPeer, Session session, IProtocol protocol, IChannelRequest? request) { - if (builderContext.Protocols is null) + if (protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (!builderContext.Protocols.ContainsKey(protocol)) + if (!protocolStackSettings.Protocols.ContainsKey(protocol)) { throw new Libp2pSetupException($"{protocol} is noty added"); } - IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); Channel res = new Channel(); if (top is IConnectionProtocol tProto) @@ -232,17 +243,17 @@ internal IChannel SubListen(LocalPeer localPeer, Session session, IProtocol prot internal async Task SubDialAndBind(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IChannelRequest? request) { - if (builderContext.Protocols is null) + if (protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (!builderContext.Protocols.ContainsKey(protocol)) + if (!protocolStackSettings.Protocols.ContainsKey(protocol)) { throw new Libp2pSetupException($"{protocol} is noty added"); } - IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); if (top is IConnectionProtocol tProto) { @@ -259,17 +270,17 @@ internal async Task SubDialAndBind(LocalPeer localPeer, Session session, IChanne internal async Task SubListenAndBind(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IChannelRequest? request) { - if (builderContext.Protocols is null) + if (protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(builderContext)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (!builderContext.Protocols.ContainsKey(protocol)) + if (!protocolStackSettings.Protocols.ContainsKey(protocol)) { throw new Libp2pSetupException($"{protocol} is noty added"); } - IProtocol top = request?.SubProtocol ?? builderContext.Protocols[protocol].First(); + IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); if (top is IConnectionProtocol tProto) { @@ -308,9 +319,14 @@ public void Dispose() public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), ISessionContext { - public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol + public Task DialAsync() where TProtocol : ISessionProtocol + { + return session.DialAsync(); + } + + public Task DialAsync(ISessionProtocol[] protocols) { - return session.DialAsync(token); + return session.DialAsync(protocols); } public Task DisconnectAsync() @@ -347,10 +363,11 @@ public Task DisconnectAsync() } } - public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) { + public IChannelRequest SpecificProtocolRequest { get; set; } public IPeer Peer => localPeer; + public IRemotePeer RemotePeer => session; public IEnumerable SubProtocols => localPeer.GetProtocolsFor(protocol); diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 139f297e..3fa12d00 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -5,10 +5,12 @@ namespace Nethermind.Libp2p.Core; -public class PeerFactory(IBuilderContext builderContext) : IPeerFactory +public class PeerFactory(IProtocolStackSettings protocolStackSettings) : IPeerFactory { + protected IProtocolStackSettings protocolStackSettings = protocolStackSettings; + public virtual IPeer Create(Identity? identity = default) { - return new LocalPeer(builderContext, identity ?? new Identity()); + return new LocalPeer(protocolStackSettings, identity ?? new Identity()); } } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 658cca78..dd7a1347 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -168,8 +168,8 @@ static void SetupChannelFactories(ProtocolStack root) SetupChannelFactories(root); - IBuilderContext builderContext = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); - builderContext.Protocols = protocols;//root.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol] + IProtocolStackSettings protocolStackSettings = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); + protocolStackSettings.Protocols = protocols;//root.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol] TPeerFactory result = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); diff --git a/src/libp2p/Libp2p.Core/SymetricProtocol.cs b/src/libp2p/Libp2p.Core/SymetricProtocol.cs index d583b0a7..a5f63583 100644 --- a/src/libp2p/Libp2p.Core/SymetricProtocol.cs +++ b/src/libp2p/Libp2p.Core/SymetricProtocol.cs @@ -5,16 +5,29 @@ namespace Nethermind.Libp2p.Core; public abstract class SymmetricProtocol { - //public Task DialAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) - //{ - // return ConnectAsync(channel, channelFactory, context, false); - //} + public Task DialAsync(IChannel channel, IConnectionContext context) + { + return ConnectAsync(channel, context, false); + } - //public Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, IPeerContext context) - //{ - // return ConnectAsync(channel, channelFactory, context, true); - //} + public Task ListenAsync(IChannel channel, IConnectionContext context) + { + return ConnectAsync(channel, context, true); + } - //protected abstract Task ConnectAsync(IChannel channel, IChannelFactory? channelFactory, - // IPeerContext context, bool isListener); + protected abstract Task ConnectAsync(IChannel channel, IConnectionContext context, bool isListener); +} +public abstract class SymmetricSessionProtocol +{ + public Task DialAsync(IChannel channel, ISessionContext context) + { + return ConnectAsync(channel, context, false); + } + + public Task ListenAsync(IChannel channel, ISessionContext context) + { + return ConnectAsync(channel, context, true); + } + + protected abstract Task ConnectAsync(IChannel channel, ISessionContext context, bool isListener); } diff --git a/src/libp2p/Libp2p.Core/TransportContext.cs b/src/libp2p/Libp2p.Core/TransportContext.cs index c39e3893..3307b7fe 100644 --- a/src/libp2p/Libp2p.Core/TransportContext.cs +++ b/src/libp2p/Libp2p.Core/TransportContext.cs @@ -9,6 +9,8 @@ public class TransportContext(LocalPeer peer, ITransportProtocol proto) : ITrans { public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); public Identity Identity => peer.Identity; + public IPeer Peer => peer; + public IRemotePeer RemotePeer => throw new NotImplementedException(); public void ListenerReady(Multiaddress addr) { diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 577ebf17..7eb101a4 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -12,7 +12,7 @@ namespace Nethermind.Libp2p.Protocols; /// /// https://github.com/libp2p/specs/tree/master/identify /// -public class IdentifyProtocol : IProtocol +public class IdentifyProtocol : ISessionProtocol { private readonly string _agentVersion; private readonly string _protocolVersion; @@ -31,8 +31,7 @@ public IdentifyProtocol(IPeerFactoryBuilder peerFactoryBuilder, IdentifyProtocol public string Id => "/ipfs/id/1.0.0"; - public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task DialAsync(IChannel channel, ISessionContext context) { _logger?.LogInformation("Dial"); @@ -47,8 +46,7 @@ public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, } } - public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task ListenAsync(IChannel channel, ISessionContext context) { _logger?.LogInformation("Listen"); @@ -56,9 +54,9 @@ public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, { ProtocolVersion = _protocolVersion, AgentVersion = _agentVersion, - PublicKey = context.LocalPeer.Identity.PublicKey.ToByteString(), - ListenAddrs = { ByteString.CopyFrom(context.LocalEndpoint.Get().ToBytes()) }, - ObservedAddr = ByteString.CopyFrom(context.RemoteEndpoint.Get().ToBytes()), + PublicKey = context.Peer.Identity.PublicKey.ToByteString(), + ListenAddrs = { ByteString.CopyFrom(context.Peer.Address.Get().ToBytes()) }, + ObservedAddr = ByteString.CopyFrom(context.RemotePeer.Address.Get().ToBytes()), Protocols = { _peerFactoryBuilder.AppLayerProtocols.Select(p => p.Id) } }; byte[] ar = new byte[identify.CalculateSize()]; diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index 6b5ef4d3..cb7b432d 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -44,7 +44,7 @@ await Task.Run(async () => { Socket client = await listener.AcceptAsync(); - ITransportConnectionContext connectionCtx = context.CreateConnection(client.Close); + ITransportConnectionContext connectionCtx = context.CreateConnection(); connectionCtx.Token.Register(client.Close); IChannel upChannel = connectionCtx.SubListen(); @@ -101,7 +101,7 @@ await Task.Run(async () => }); } - public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) + public async Task DialAsync(ITransportConnectionContext context, Multiaddress remoteAddr, CancellationToken token) { Socket client = new(SocketType.Stream, ProtocolType.Tcp); @@ -119,12 +119,10 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, return; } - using ITransportConnectionContext connectionCtx = context.CreateConnection(client.Close); - - connectionCtx.Token.Register(client.Close); + context.Token.Register(client.Close); token.Register(client.Close); - IChannel upChannel = connectionCtx.SubDial(); + IChannel upChannel = context.SubDial(); Task receiveTask = Task.Run(async () => { @@ -134,7 +132,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, for (; client.Connected;) { int dataLength = await client.ReceiveAsync(buf, SocketFlags.None); - _logger?.LogDebug("Receive {0} data, len={1}", connectionCtx.Id, dataLength); + _logger?.LogDebug("Receive {0} data, len={1}", context.Id, dataLength); if (dataLength == 0 || (await upChannel.WriteAsync(new ReadOnlySequence(buf[..dataLength]))) != IOResult.Ok) { @@ -154,7 +152,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, { await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) { - _logger?.LogDebug("Send {0} data, len={1}", connectionCtx.Id, data.Length); + _logger?.LogDebug("Send {0} data, len={1}", context.Id, data.Length); int sent = await client.SendAsync(data.ToArray(), SocketFlags.None); if (sent is 0 || !client.Connected) { @@ -168,7 +166,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, } }); - await Task.WhenAll(receiveTask, sendTask).ContinueWith(t => connectionCtx.Dispose()); + await Task.WhenAll(receiveTask, sendTask).ContinueWith(t => context.Dispose()); _ = upChannel.CloseAsync(); } diff --git a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs index 0dcaf85a..fe2c3bb1 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs @@ -9,170 +9,170 @@ namespace Libp2p.Protocols.Multistream.Tests; [Parallelizable(scope: ParallelScope.All)] public class MultistreamProtocolTests { - [Test] - public async Task Test_ConnectionEstablished_AfterHandshake() - { - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); - channelFactory.SubProtocols.Returns(new[] { proto1 }); - IChannel upChannel = new TestChannel(); - channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) - .Returns(Task.CompletedTask); - - MultistreamProtocol proto = new(); - Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - _ = Task.Run(async () => - { - await downChannel.WriteLineAsync(proto.Id); - await downChannel.WriteLineAsync("proto1"); - }); - - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); - - await dialTask; - - _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); - await downChannel.CloseAsync(); - } - - [Test] - public async Task Test_ConnectionEstablished_AfterHandshake_With_SpecificRequest() - { - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - IChannelRequest channelRequest = Substitute.For(); - peerContext.SpecificProtocolRequest.Returns(channelRequest); - - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); - channelRequest.SubProtocol.Returns(proto1); - IChannel upChannel = new TestChannel(); - - channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) - .Returns(Task.CompletedTask); - - MultistreamProtocol proto = new(); - Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - _ = Task.Run(async () => - { - await downChannel.WriteLineAsync(proto.Id); - await downChannel.WriteLineAsync("proto1"); - }); - - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); - - await dialTask; - - _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); - await downChannel.CloseAsync(); - } - - [Test] - public async Task Test_ConnectionClosed_ForUnknownProtocol() - { - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); - channelFactory.SubProtocols.Returns(new[] { proto1 }); - - MultistreamProtocol proto = new(); - _ = Task.Run(async () => - { - await downChannel.WriteLineAsync(proto.Id); - await downChannel.WriteLineAsync("proto2"); - }); - - Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); - - await dialTask; - - _ = channelFactory.DidNotReceive().SubDialAndBind(downChannelFromProtocolPov, proto1); - } - - [Test] - public async Task Test_ConnectionEstablished_ForAnyOfProtocols() - { - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); - IProtocol? proto2 = Substitute.For(); - proto2.Id.Returns("proto2"); - channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); - IChannel upChannel = new TestChannel(); - channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) - .Returns(Task.CompletedTask); - - MultistreamProtocol proto = new(); - Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - _ = Task.Run(async () => - { - await downChannel.WriteLineAsync(proto.Id); - await downChannel.WriteLineAsync("na"); - await downChannel.WriteLineAsync("proto2"); - }); - - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto2.Id)); - - await dialTask; - - _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto2); - await upChannel.CloseAsync(); - } - - [Test] - public async Task Test_ConnectionClosed_ForBadProtocol() - { - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); - IProtocol? proto2 = Substitute.For(); - proto1.Id.Returns("proto2"); - channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); - - MultistreamProtocol proto = new(); - Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - _ = Task.Run(async () => - { - await downChannel.WriteLineAsync(proto.Id); - await downChannel.WriteLineAsync("na1"); - await downChannel.WriteLineAsync("proto2"); - }); - - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); - - await dialTask; - - _ = channelFactory.DidNotReceiveWithAnyArgs().SubDialAndBind(null!, (IProtocol)null!); - } + //[Test] + //public async Task Test_ConnectionEstablished_AfterHandshake() + //{ + // IChannel downChannel = new TestChannel(); + // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + // IChannelFactory channelFactory = Substitute.For(); + // IPeerContext peerContext = Substitute.For(); + // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); + + // IProtocol? proto1 = Substitute.For(); + // proto1.Id.Returns("proto1"); + // channelFactory.SubProtocols.Returns(new[] { proto1 }); + // IChannel upChannel = new TestChannel(); + // channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) + // .Returns(Task.CompletedTask); + + // MultistreamProtocol proto = new(); + // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + // _ = Task.Run(async () => + // { + // await downChannel.WriteLineAsync(proto.Id); + // await downChannel.WriteLineAsync("proto1"); + // }); + + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); + + // await dialTask; + + // _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); + // await downChannel.CloseAsync(); + //} + + //[Test] + //public async Task Test_ConnectionEstablished_AfterHandshake_With_SpecificRequest() + //{ + // IChannel downChannel = new TestChannel(); + // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + // IChannelFactory channelFactory = Substitute.For(); + // IPeerContext peerContext = Substitute.For(); + // IChannelRequest channelRequest = Substitute.For(); + // peerContext.SpecificProtocolRequest.Returns(channelRequest); + + // IProtocol? proto1 = Substitute.For(); + // proto1.Id.Returns("proto1"); + // channelRequest.SubProtocol.Returns(proto1); + // IChannel upChannel = new TestChannel(); + + // channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) + // .Returns(Task.CompletedTask); + + // MultistreamProtocol proto = new(); + // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + // _ = Task.Run(async () => + // { + // await downChannel.WriteLineAsync(proto.Id); + // await downChannel.WriteLineAsync("proto1"); + // }); + + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); + + // await dialTask; + + // _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); + // await downChannel.CloseAsync(); + //} + + //[Test] + //public async Task Test_ConnectionClosed_ForUnknownProtocol() + //{ + // IChannel downChannel = new TestChannel(); + // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + // IChannelFactory channelFactory = Substitute.For(); + // IPeerContext peerContext = Substitute.For(); + // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); + + // IProtocol? proto1 = Substitute.For(); + // proto1.Id.Returns("proto1"); + // channelFactory.SubProtocols.Returns(new[] { proto1 }); + + // MultistreamProtocol proto = new(); + // _ = Task.Run(async () => + // { + // await downChannel.WriteLineAsync(proto.Id); + // await downChannel.WriteLineAsync("proto2"); + // }); + + // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); + + // await dialTask; + + // _ = channelFactory.DidNotReceive().SubDialAndBind(downChannelFromProtocolPov, proto1); + //} + + //[Test] + //public async Task Test_ConnectionEstablished_ForAnyOfProtocols() + //{ + // IChannel downChannel = new TestChannel(); + // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + // IChannelFactory channelFactory = Substitute.For(); + // IPeerContext peerContext = Substitute.For(); + // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); + + // IProtocol? proto1 = Substitute.For(); + // proto1.Id.Returns("proto1"); + // IProtocol? proto2 = Substitute.For(); + // proto2.Id.Returns("proto2"); + // channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); + // IChannel upChannel = new TestChannel(); + // channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) + // .Returns(Task.CompletedTask); + + // MultistreamProtocol proto = new(); + // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + // _ = Task.Run(async () => + // { + // await downChannel.WriteLineAsync(proto.Id); + // await downChannel.WriteLineAsync("na"); + // await downChannel.WriteLineAsync("proto2"); + // }); + + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto2.Id)); + + // await dialTask; + + // _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto2); + // await upChannel.CloseAsync(); + //} + + //[Test] + //public async Task Test_ConnectionClosed_ForBadProtocol() + //{ + // IChannel downChannel = new TestChannel(); + // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + // IChannelFactory channelFactory = Substitute.For(); + // IPeerContext peerContext = Substitute.For(); + // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); + + // IProtocol? proto1 = Substitute.For(); + // proto1.Id.Returns("proto1"); + // IProtocol? proto2 = Substitute.For(); + // proto1.Id.Returns("proto2"); + // channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); + + // MultistreamProtocol proto = new(); + // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + // _ = Task.Run(async () => + // { + // await downChannel.WriteLineAsync(proto.Id); + // await downChannel.WriteLineAsync("na1"); + // await downChannel.WriteLineAsync("proto2"); + // }); + + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); + + // await dialTask; + + // _ = channelFactory.DidNotReceiveWithAnyArgs().SubDialAndBind(null!, (IProtocol)null!); + //} } diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index f5beff14..aaf9dcd4 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -19,8 +19,7 @@ public MultistreamProtocol(ILoggerFactory? loggerFactory = null) { _logger = loggerFactory?.CreateLogger(); } - public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task DialAsync(IChannel channel, IConnectionContext context) { if (!await SendHello(channel)) { @@ -59,7 +58,7 @@ public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, } else { - foreach (IProtocol selector in channelFactory!.SubProtocols) + foreach (IProtocol selector in context!.SubProtocols) { bool? dialResult = await DialProtocol(selector); if (dialResult == true) @@ -80,12 +79,10 @@ public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, return; } _logger?.LogDebug($"Protocol selected during dialing: {selected}"); - //await channelFactory.SubDialAndBind(channel, context, selected); - throw new NotImplementedException(); + await context.SubDialAndBind(channel, selected); } - public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task ListenAsync(IChannel channel, IConnectionContext context) { if (!await SendHello(channel)) { @@ -97,7 +94,7 @@ public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, for (; ; ) { string proto = await channel.ReadLineAsync(); - selected = channelFactory!.SubProtocols.FirstOrDefault(x => x.Id == proto) as IProtocol; + selected = context.SubProtocols.FirstOrDefault(x => x.Id == proto) as IProtocol; if (selected is not null) { await channel.WriteLineAsync(selected.Id); @@ -116,8 +113,7 @@ public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, } _logger?.LogDebug($"Protocol selected during listening: {selected}"); - // await channelFactory.SubListenAndBind(channel, context, selected); - throw new NotImplementedException(); + await context.SubListenAndBind(channel, selected); } private async Task SendHello(IChannel channel) diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index 82c32ef8..c1c7ef6d 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -25,7 +25,7 @@ public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILog HashFunction.Sha256 ); private readonly ILogger? _logger = loggerFactory?.CreateLogger(); - private readonly NoiseExtensions _extensions = new NoiseExtensions() + private readonly NoiseExtensions _extensions = new() { StreamMuxers = { @@ -34,12 +34,11 @@ public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILog }; public string Id => "/noise"; + private const string PayloadSigPrefix = "noise-libp2p-static-key:"; - public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + public async Task DialAsync(IChannel downChannel, IConnectionContext context) { - ArgumentNullException.ThrowIfNull(upChannelFactory); - KeyPair? clientStatic = KeyPair.Generate(); using HandshakeState? handshakeState = _protocol.Create(true, s: clientStatic.PrivateKey); byte[] buffer = new byte[Protocol.MaxMessageLength]; @@ -69,10 +68,10 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFact byte[] msg = [.. Encoding.UTF8.GetBytes(PayloadSigPrefix), .. ByteString.CopyFrom(clientStatic.PublicKey)]; byte[] sig = new byte[64]; - Ed25519.Sign([.. context.LocalPeer.Identity.PrivateKey!.Data], 0, msg, 0, msg.Length, sig, 0); + Ed25519.Sign([.. context.Peer.Identity.PrivateKey!.Data], 0, msg, 0, msg.Length, sig, 0); NoiseHandshakePayload payload = new() { - IdentityKey = context.LocalPeer.Identity.PublicKey.ToByteString(), + IdentityKey = context.Peer.Identity.PublicKey.ToByteString(), IdentitySig = ByteString.CopyFrom(sig), Extensions = _extensions }; @@ -93,7 +92,7 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFact _logger?.LogDebug("Established connection to {peer}", context.RemotePeer.Address); - IChannel upChannel = upChannelFactory.SubDial(); + IChannel upChannel = context.SubDial(); await ExchangeData(transport, downChannel, upChannel); @@ -101,10 +100,8 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFact _logger?.LogDebug("Closed"); } - public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { - ArgumentNullException.ThrowIfNull(upChannelFactory); - KeyPair? serverStatic = KeyPair.Generate(); using HandshakeState? handshakeState = _protocol.Create(false, @@ -120,10 +117,10 @@ public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFa .Concat(ByteString.CopyFrom(serverStatic.PublicKey)) .ToArray(); byte[] sig = new byte[64]; - Ed25519.Sign(context.LocalPeer.Identity.PrivateKey!.Data.ToArray(), 0, msg, 0, msg.Length, sig, 0); + Ed25519.Sign(context.Peer.Identity.PrivateKey!.Data.ToArray(), 0, msg, 0, msg.Length, sig, 0); NoiseHandshakePayload payload = new() { - IdentityKey = context.LocalPeer.Identity.PublicKey.ToByteString(), + IdentityKey = context.Peer.Identity.PublicKey.ToByteString(), IdentitySig = ByteString.CopyFrom(sig), Extensions = _extensions }; @@ -153,7 +150,7 @@ public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFa _logger?.LogDebug("Established connection to {peer}", context.RemotePeer.Address); - IChannel upChannel = upChannelFactory.SubListen(); + IChannel upChannel = context.SubListen(); await ExchangeData(transport, downChannel, upChannel); diff --git a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs index ce49759d..5c0632d0 100644 --- a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs @@ -11,7 +11,7 @@ namespace Nethermind.Libp2p.Protocols; /// /// https://github.com/libp2p/specs/blob/master/ping/ping.md /// -public class PingProtocol : IProtocol +public class PingProtocol : ISessionProtocol { private const int PayloadLength = 32; @@ -24,8 +24,7 @@ public PingProtocol(ILoggerFactory? loggerFactory = null) _logger = loggerFactory?.CreateLogger(); } - public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task DialAsync(IChannel channel, ISessionContext context) { byte[] ping = new byte[PayloadLength]; _random.NextBytes(ping.AsSpan(0, PayloadLength)); @@ -49,8 +48,7 @@ public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, _logger?.LogPinged(context.RemotePeer.Address); } - public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task ListenAsync(IChannel channel, ISessionContext context) { _logger?.PingListenStarted(context.RemotePeer.Address); diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs index af957bea..16c8cc1c 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs @@ -14,13 +14,12 @@ public class PlainTextProtocol : SymmetricProtocol, IProtocol { public string Id => "/plaintext/2.0.0"; - protected override async Task ConnectAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context, bool isListener) + protected override async Task ConnectAsync(IChannel channel, IConnectionContext context, bool isListener) { Exchange src = new() { - Id = ByteString.CopyFrom(context.LocalPeer.Identity.PeerId.Bytes), - Pubkey = context.LocalPeer.Identity.PublicKey.ToByteString() + Id = ByteString.CopyFrom(context.Peer.Identity.PeerId.Bytes), + Pubkey = context.Peer.Identity.PublicKey.ToByteString() }; int size = src.CalculateSize(); int sizeOfSize = VarInt.GetSizeInBytes(size); @@ -36,7 +35,7 @@ protected override async Task ConnectAsync(IChannel channel, IChannelFactory? ch Exchange? dest = Exchange.Parser.ParseFrom(buf); await (isListener - ? channelFactory.SubListenAndBind(channel) - : channelFactory.SubDialAndBind(channel)); + ? context.SubListenAndBind(channel) + : context.SubDialAndBind(channel)); } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 83638670..3a7ceb76 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -11,7 +11,7 @@ namespace Nethermind.Libp2p.Protocols.Pubsub; /// /// https://github.com/libp2p/specs/tree/master/pubsub /// -public abstract class PubsubProtocol : IProtocol +public abstract class PubsubProtocol : ISessionProtocol { private readonly ILogger? _logger; private readonly PubsubRouter router; @@ -25,8 +25,7 @@ public PubsubProtocol(string protocolId, PubsubRouter router, ILoggerFactory? lo this.router = router; } - public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task DialAsync(IChannel channel, ISessionContext context) { string peerId = context.RemotePeer.Address.Get().ToString()!; _logger?.LogDebug($"Dialed({context.Id}) {context.RemotePeer.Address}"); @@ -52,8 +51,7 @@ public async Task DialAsync(IChannel channel, IChannelFactory? channelFactory, } - public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context) + public async Task ListenAsync(IChannel channel, ISessionContext context) { string peerId = context.RemotePeer.Address.Get().ToString()!; @@ -64,7 +62,7 @@ public async Task ListenAsync(IChannel channel, IChannelFactory? channelFactory, CancellationToken token = router.InboundConnection(context.RemotePeer.Address, Id, listTcs.Task, dialTcs.Task, () => { - context.SubDialRequests.Add(new ChannelRequest { SubProtocol = this }); + _ = context.DialAsync([this]); return dialTcs.Task; }); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 20486456..7b7c2d82 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -165,7 +165,7 @@ public async Task RunAsync(IPeer localPeer, IDiscoveryProtocol discoveryProtocol LocalPeerId = new PeerId(localPeer.Address.Get().ToString()!); - _ = localPeer.ListenAsync(localPeer.Address, token); + _ = localPeer.StartListenAsync([localPeer.Address], token); _ = StartDiscoveryAsync(discoveryProtocol, token); logger?.LogInformation("Started"); diff --git a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs index d08cb91d..294200fa 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs @@ -65,7 +65,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, { ApplicationProtocols = protocols, RemoteCertificateValidationCallback = (_, c, _, _) => true, - ServerCertificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.Identity) + ServerCertificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity) }, }; @@ -92,7 +92,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, try { QuicConnection connection = await listener.AcceptConnectionAsync(); - ITransportConnectionContext clientContext = context.CreateConnection(() => _ = connection.CloseAsync(0)); + ITransportConnectionContext clientContext = context.CreateConnection(); _ = ProcessStreams(clientContext, connection, token).ContinueWith(t => clientContext.Dispose()); } @@ -104,7 +104,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, } } - public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) + public async Task DialAsync(ITransportConnectionContext context, Multiaddress remoteAddr, CancellationToken token) { if (!QuicConnection.IsSupported) { @@ -131,7 +131,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, TargetHost = null, ApplicationProtocols = protocols, RemoteCertificateValidationCallback = (_, c, _, _) => VerifyRemoteCertificate(remoteAddr, c), - ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.Identity) }, + ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity) }, }, RemoteEndPoint = remoteEndpoint, }; @@ -141,9 +141,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, _logger?.Connected(connection.LocalEndPoint, connection.RemoteEndPoint); token.Register(() => _ = connection.CloseAsync(0)); - using ITransportConnectionContext clientContext = context.CreateConnection(() => _ = connection.CloseAsync(0)); - - await ProcessStreams(clientContext, connection, token); + await ProcessStreams(context, connection, token); } private static bool VerifyRemoteCertificate(Multiaddress remoteAddr, X509Certificate certificate) => @@ -153,14 +151,14 @@ private async Task ProcessStreams(ITransportConnectionContext context, QuicConne { _logger?.LogDebug("New connection to {remote}", connection.RemoteEndPoint); - using ISessionContext session = context.CreateSession(); + using IConnectionSessionContext session = context.CreateSession(null!); _ = Task.Run(async () => { - foreach (IChannelRequest request in session.SubDialRequests.GetConsumingEnumerable()) + foreach (IChannelRequest request in session.DialRequests) { QuicStream stream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); - IChannel upChannel = session.SubDial(request); + IChannel upChannel = context.SubDial(request); ExchangeData(stream, upChannel, request.CompletionSource); } }, token); @@ -168,7 +166,7 @@ private async Task ProcessStreams(ITransportConnectionContext context, QuicConne while (!token.IsCancellationRequested) { QuicStream inboundStream = await connection.AcceptInboundStreamAsync(token); - IChannel upChannel = session.SubListen(); + IChannel upChannel = context.SubListen(); ExchangeData(inboundStream, upChannel, null); } } diff --git a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs index 1a088ad9..5727428e 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs @@ -24,44 +24,44 @@ public class YamuxProtocolTests [Test] public async Task Test_Protocol_Communication() { - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); - IPeerContext dialerPeerContext = Substitute.For(); - var dialerRequests = new BlockingCollection() { new ChannelRequest() { SubProtocol = proto1 } }; - dialerPeerContext.SubDialRequests.Returns(dialerRequests); + //IProtocol? proto1 = Substitute.For(); + //proto1.Id.Returns("proto1"); + //IPeerContext dialerPeerContext = Substitute.For(); + //var dialerRequests = new BlockingCollection() { new ChannelRequest() { SubProtocol = proto1 } }; + //dialerPeerContext.SubDialRequests.Returns(dialerRequests); - TestChannel dialerDownChannel = new TestChannel(); - IChannelFactory dialerUpchannelFactory = Substitute.For(); - dialerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); - TestChannel dialerUpChannel = new TestChannel(); - dialerUpchannelFactory.SubDial(Arg.Any()) - .Returns(dialerUpChannel); + //TestChannel dialerDownChannel = new TestChannel(); + //IChannelFactory dialerUpchannelFactory = Substitute.For(); + //dialerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); + //TestChannel dialerUpChannel = new TestChannel(); + //dialerUpchannelFactory.SubDial(Arg.Any()) + // .Returns(dialerUpChannel); - _ = dialerUpChannel.Reverse().WriteLineAsync("hello").AsTask().ContinueWith((e) => dialerUpChannel.CloseAsync()); + //_ = dialerUpChannel.Reverse().WriteLineAsync("hello").AsTask().ContinueWith((e) => dialerUpChannel.CloseAsync()); - IPeerContext listenerPeerContext = Substitute.For(); - IChannel listenerDownChannel = dialerDownChannel.Reverse(); - IChannelFactory listenerUpchannelFactory = Substitute.For(); - var listenerRequests = new BlockingCollection(); - listenerPeerContext.SubDialRequests.Returns(listenerRequests); - listenerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); - TestChannel listenerUpChannel = new TestChannel(); - listenerUpchannelFactory.SubListen(Arg.Any()) - .Returns(listenerUpChannel); + //IPeerContext listenerPeerContext = Substitute.For(); + //IChannel listenerDownChannel = dialerDownChannel.Reverse(); + //IChannelFactory listenerUpchannelFactory = Substitute.For(); + //var listenerRequests = new BlockingCollection(); + //listenerPeerContext.SubDialRequests.Returns(listenerRequests); + //listenerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); + //TestChannel listenerUpChannel = new TestChannel(); + //listenerUpchannelFactory.SubListen(Arg.Any()) + // .Returns(listenerUpChannel); - YamuxProtocol proto = new(loggerFactory: new DebugLoggerFactory()); + //YamuxProtocol proto = new(loggerFactory: new DebugLoggerFactory()); - _ = proto.ListenAsync(listenerDownChannel, listenerUpchannelFactory, listenerPeerContext); + //_ = proto.ListenAsync(listenerDownChannel, listenerUpchannelFactory, listenerPeerContext); - _ = proto.DialAsync(dialerDownChannel, dialerUpchannelFactory, dialerPeerContext); + //_ = proto.DialAsync(dialerDownChannel, dialerUpchannelFactory, dialerPeerContext); - var res = await listenerUpChannel.Reverse().ReadLineAsync(); - await listenerUpChannel.CloseAsync(); + //var res = await listenerUpChannel.Reverse().ReadLineAsync(); + //await listenerUpChannel.CloseAsync(); - Assert.That(res, Is.EqualTo("hello")); + //Assert.That(res, Is.EqualTo("hello")); - await Task.Delay(1000); + //await Task.Delay(1000); } } diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 762d4389..16aac3f8 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Exceptions; +using Org.BouncyCastle.Asn1.X509; using System.Buffers; using System.Runtime.CompilerServices; @@ -26,14 +27,8 @@ public YamuxProtocol(MultiplexerSettings? multiplexerSettings = null, ILoggerFac public string Id => "/yamux/1.0.0"; - protected override async Task ConnectAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context, bool isListener) + protected override async Task ConnectAsync(IChannel channel, IConnectionContext context, bool isListener) { - if (channelFactory is null) - { - throw new ArgumentException("ChannelFactory should be available for a muxer", nameof(channelFactory)); - } - _logger?.LogInformation(isListener ? "Listen" : "Dial"); TaskAwaiter downChannelAwaiter = channel.GetAwaiter(); @@ -42,7 +37,7 @@ protected override async Task ConnectAsync(IChannel channel, IChannelFactory? ch try { int streamIdCounter = isListener ? 2 : 1; - context.Connected(context.RemotePeer); + IConnectionSessionContext session = context.CreateSession(context.RemotePeer.Address.GetPeerId()!); int pingCounter = 0; using Timer timer = new((s) => @@ -52,7 +47,7 @@ protected override async Task ConnectAsync(IChannel channel, IChannelFactory? ch _ = Task.Run(() => { - foreach (IChannelRequest request in context.SubDialRequests.GetConsumingEnumerable()) + foreach (IChannelRequest request in session.DialRequests) { int streamId = streamIdCounter; Interlocked.Add(ref streamIdCounter, 2); @@ -221,13 +216,12 @@ ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, ICha if (isListenerChannel) { - upChannel = channelFactory.SubListen(); + upChannel = context.SubListen(); } else { - IPeerContext dialContext = context.Fork(); - dialContext.SpecificProtocolRequest = channelRequest; - upChannel = channelFactory.SubDial(); + context.SpecificProtocolRequest = channelRequest; + upChannel = context.SubDial(); } ChannelState state = new(upChannel, channelRequest); diff --git a/src/libp2p/Libp2p/Libp2pBuilderContext.cs b/src/libp2p/Libp2p/Libp2pBuilderContext.cs index fd4269a5..d6b14555 100644 --- a/src/libp2p/Libp2p/Libp2pBuilderContext.cs +++ b/src/libp2p/Libp2p/Libp2pBuilderContext.cs @@ -5,7 +5,8 @@ namespace Nethermind.Libp2p.Stack; -internal class Libp2pBuilderContext : IBuilderContext +internal class Libp2pBuilderContext : IProtocolStackSettings { public IProtocol[]? TopProtocols { get; set; } + public Dictionary? Protocols { get; set; } } diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 53cb729d..d624843b 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.DependencyInjection; using Multiformats.Address; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; @@ -8,28 +9,31 @@ namespace Nethermind.Libp2p.Stack; -public class Libp2pPeerFactory : PeerFactory +public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings) : PeerFactory(protocolStackSettings) { - public Libp2pPeerFactory(IServiceProvider serviceProvider) : base(serviceProvider) - { - } + public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, identity ?? new Identity()); +} - protected override async Task ConnectedTo(ISession peer, bool isDialer) +class Libp2pPeer(IProtocolStackSettings protocolStackSettings, Identity identity) : LocalPeer(protocolStackSettings, identity) +{ + protected override async Task ConnectedTo(ISession session, bool isDialer) { - await peer.DialAsync(); + await session.DialAsync(); } protected override IProtocol SelectProtocol(Multiaddress addr) { + ArgumentNullException.ThrowIfNull(protocolStackSettings.TopProtocols); + ITransportProtocol protocol = null!; if (addr.Has()) { - protocol = TopProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); + protocol = protocolStackSettings.TopProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); } else if (addr.Has()) { - protocol = TopProtocols!.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); + protocol = protocolStackSettings.TopProtocols!.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); } else { @@ -38,16 +42,4 @@ protected override IProtocol SelectProtocol(Multiaddress addr) return protocol; } - - public override IPeer Create(Identity? identity = null) - { - identity ??= new Identity(); - localAddr ??= $"/ip4/0.0.0.0/tcp/0/p2p/{identity.PeerId}"; - if (localAddr.Get() is null) - { - localAddr.Add(identity.PeerId.ToString()); - } - - return new LocalPeer(this) { Identity = identity ?? new Identity(), Address = localAddr ?? $"/ip4/0.0.0.0/tcp/0/p2p/{identity.PeerId}" }; - } } diff --git a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs index 70a2e987..b43f0a52 100644 --- a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs +++ b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs @@ -12,44 +12,44 @@ namespace Nethermind.Libp2p.Protocols; /// /// Select protocol based on multiaddr /// -public class MultiaddressBasedSelector(ILoggerFactory? loggerFactory = null): ITransportProtocol -{ - private readonly ILogger? _logger = loggerFactory?.CreateLogger(); - - public string Id => "multiaddr-select"; - - public Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) - { - return ConnectAsync(context, listenAddr, token, false); - } - - public Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) - { - return ConnectAsync(context, listenAddr, token, true); - } - - protected async Task ConnectAsync(ITransportContext context, Multiaddress addr, CancellationToken token, bool isListener) - { - throw new NotImplementedException(); - //ITransportProtocol protocol = null!; - - //if (addr.Has()) - //{ - // protocol = context!.SubProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); - //} - //else if (addr.Has()) - //{ - // protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); - //} - //else - //{ - // throw new NotImplementedException($"No transport protocol found for the given address: {addr}"); - //} - - //_logger?.LogPickedProtocol(protocol.Id, isListener ? "listen" : "dial"); - - //await (isListener - // ? protocol.ListenAsync(context, addr, token) - // : protocol.DialAsync(context, addr, token)); - } -} +//public class MultiaddressBasedSelector(ILoggerFactory? loggerFactory = null): ITransportProtocol +//{ +// private readonly ILogger? _logger = loggerFactory?.CreateLogger(); + +// public string Id => "multiaddr-select"; + +// public Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) +// { +// return ConnectAsync(context, listenAddr, token, false); +// } + +// public Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) +// { +// return ConnectAsync(context, listenAddr, token, true); +// } + +// protected async Task ConnectAsync(ITransportContext context, Multiaddress addr, CancellationToken token, bool isListener) +// { +// throw new NotImplementedException(); +// //ITransportProtocol protocol = null!; + +// //if (addr.Has()) +// //{ +// // protocol = context!.SubProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); +// //} +// //else if (addr.Has()) +// //{ +// // protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); +// //} +// //else +// //{ +// // throw new NotImplementedException($"No transport protocol found for the given address: {addr}"); +// //} + +// //_logger?.LogPickedProtocol(protocol.Id, isListener ? "listen" : "dial"); + +// //await (isListener +// // ? protocol.ListenAsync(context, addr, token) +// // : protocol.DialAsync(context, addr, token)); +// } +//} diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index 0dd52997..517c510f 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -15,7 +15,7 @@ public static IServiceCollection AddLibp2p(this IServiceCollection services, Fun { return services .AddSingleton(sp => new Libp2pPeerFactoryBuilder(sp)) - .AddSingleton() + .AddSingleton() .AddSingleton(sp => factorySetup is null ? sp.GetRequiredService() : factorySetup(sp.GetRequiredService())) .AddSingleton(sp => (ILibp2pPeerFactoryBuilder)sp.GetRequiredService()) .AddScoped(sp => sp.GetService()!.Build()) diff --git a/src/samples/chat/ChatProtocol.cs b/src/samples/chat/ChatProtocol.cs index 25195249..85088352 100644 --- a/src/samples/chat/ChatProtocol.cs +++ b/src/samples/chat/ChatProtocol.cs @@ -5,13 +5,12 @@ using System.Text; using Nethermind.Libp2p.Core; -internal class ChatProtocol : SymmetricProtocol, IProtocol +internal class ChatProtocol : SymmetricSessionProtocol, ISessionProtocol { private static readonly ConsoleReader Reader = new(); public string Id => "/chat/1.0.0"; - protected override async Task ConnectAsync(IChannel channel, IChannelFactory? channelFactory, - IPeerContext context, bool isListener) + protected override async Task ConnectAsync(IChannel channel, ISessionContext context, bool isListener) { Console.Write("> "); _ = Task.Run(async () => diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index cb547422..afe58273 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -32,7 +32,7 @@ "/ip4/0.0.0.0/udp/0/quic-v1" : "/ip4/0.0.0.0/tcp/0"; - IPeer localPeer = peerFactory.Create(localAddr: addrTemplate); + IPeer localPeer = peerFactory.Create(); logger.LogInformation("Dialing {remote}", remoteAddr); ISession remotePeer = await localPeer.DialAsync(remoteAddr, ts.Token); @@ -49,12 +49,14 @@ "/ip4/0.0.0.0/udp/{0}/quic-v1" : "/ip4/0.0.0.0/tcp/{0}"; - IListener listener = await peer.ListenAsync( - string.Format(addrTemplate, args.Length > 0 && args[0] == "-sp" ? args[1] : "0"), + peer.OnConnection += async newSession => logger.LogInformation("A peer connected {remote}", newSession.Address); + + await peer.StartListenAsync( + [string.Format(addrTemplate, args.Length > 0 && args[0] == "-sp" ? args[1] : "0")], ts.Token); - logger.LogInformation("Listener started at {address}", listener.Address); - listener.OnConnection += async remotePeer => logger.LogInformation("A peer connected {remote}", remotePeer.Address); - Console.CancelKeyPress += delegate { listener.DisconnectAsync(); }; + logger.LogInformation("Listener started at {address}", peer.Address); + + Console.CancelKeyPress += delegate { ts.Cancel(); }; - await listener; + await Task.FromCanceled(ts.Token); } diff --git a/src/samples/perf-benchmarks/PerfProtocol.cs b/src/samples/perf-benchmarks/PerfProtocol.cs index 6dc94841..a0d91493 100644 --- a/src/samples/perf-benchmarks/PerfProtocol.cs +++ b/src/samples/perf-benchmarks/PerfProtocol.cs @@ -8,7 +8,7 @@ namespace DataTransferBenchmark; // TODO: Align with perf protocol -public class PerfProtocol : IProtocol +public class PerfProtocol : ISessionProtocol { private readonly ILogger? _logger; public string Id => "/perf/1.0.0"; @@ -21,7 +21,7 @@ public PerfProtocol(ILoggerFactory? loggerFactory = null) public const long TotalLoad = 1024L * 1024 * 100; private Random rand = new(); - public async Task DialAsync(IChannel downChannel, IChannelFactory upChannelFactory, IPeerContext context) + public async Task DialAsync(IChannel downChannel, ISessionContext context) { await downChannel.WriteVarintAsync(TotalLoad); @@ -60,7 +60,7 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory upChannelFacto } } - public async Task ListenAsync(IChannel downChannel, IChannelFactory upChannelFactory, IPeerContext context) + public async Task ListenAsync(IChannel downChannel, ISessionContext context) { ulong total = await downChannel.ReadVarintUlongAsync(); ulong bytesRead = 0; diff --git a/src/samples/perf-benchmarks/Program.cs b/src/samples/perf-benchmarks/Program.cs index 9e27d432..0d779128 100644 --- a/src/samples/perf-benchmarks/Program.cs +++ b/src/samples/perf-benchmarks/Program.cs @@ -20,9 +20,9 @@ Identity optionalFixedIdentity = new(Enumerable.Repeat((byte)42, 32).ToArray()); IPeer peer = peerFactory.Create(optionalFixedIdentity); - IListener listener = await peer.ListenAsync($"/ip4/0.0.0.0/tcp/0/p2p/{peer.Identity.PeerId}"); + await peer.StartListenAsync([$"/ip4/0.0.0.0/tcp/0/p2p/{peer.Identity.PeerId}"]); - Multiaddress remoteAddr = listener.Address; + Multiaddress remoteAddr = peer.ListenAddresses.First(); IPeer localPeer = peerFactory.Create(); ISession remotePeer = await localPeer.DialAsync(remoteAddr); @@ -42,9 +42,9 @@ .Build(); IPeer peer = peerFactory.Create(); - IListener listener = await peer.ListenAsync($"/ip4/0.0.0.0/tcp/0"); + await peer.StartListenAsync([$"/ip4/0.0.0.0/tcp/0"]); - Multiaddress remoteAddr = listener.Address; + Multiaddress remoteAddr = peer.ListenAddresses.First(); IPeer localPeer = peerFactory.Create(); ISession remotePeer = await localPeer.DialAsync(remoteAddr); diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 765528d2..72e5d46d 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -30,7 +30,7 @@ Identity localPeerIdentity = new(); string addr = $"/ip4/0.0.0.0/tcp/0/p2p/{localPeerIdentity.PeerId}"; -IPeer peer = peerFactory.Create(localPeerIdentity, Multiaddress.Decode(addr)); +IPeer peer = peerFactory.Create(localPeerIdentity); PubsubRouter router = serviceProvider.GetService()!; ITopic topic = router.Subscribe("chat-room:awesome-chat-room"); @@ -55,7 +55,7 @@ -string peerId = peer.Address.Get().ToString(); +string peerId = peer.Identity.PeerId.ToString(); string nickName = "libp2p-dotnet"; diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index 090c1f1e..a65f4b9d 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -33,7 +33,7 @@ if (isDialer) { - IPeer localPeer = peerFactory.Create(localAddr: builder.MakeAddress()); + IPeer localPeer = peerFactory.Create(); Log($"Picking an address to dial..."); @@ -81,13 +81,15 @@ ip = addresses.First().Address.ToString()!; } Log("Starting to listen..."); - IPeer localPeer = peerFactory.Create(localAddr: builder.MakeAddress(ip)); - IListener listener = await localPeer.ListenAsync(localPeer.Address); - listener.OnConnection += (peer) => { Log($"Connected {peer.Address}"); return Task.CompletedTask; }; - Log($"Listening on {listener.Address}"); - db.ListRightPush(new RedisKey("listenerAddr"), new RedisValue(listener.Address.ToString())); + IPeer localPeer = peerFactory.Create(); + + CancellationTokenSource listennTcs = new(); + await localPeer.StartListenAsync([builder.MakeAddress(ip)], listennTcs.Token); + localPeer.OnConnection += (peer) => { Log($"Connected {peer.Address}"); return Task.CompletedTask; }; + Log($"Listening on {localPeer.Address}"); + db.ListRightPush(new RedisKey("listenerAddr"), new RedisValue(localPeer.Address.ToString())); await Task.Delay(testTimeoutSeconds * 1000); - await listener.DisconnectAsync(); + await listennTcs.CancelAsync(); return -1; } } From 45568c43ea1f474e5e2c008e8d9f802bdc226856 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 14 Aug 2024 13:40:54 +0300 Subject: [PATCH 04/25] Before protocol refs --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 30 +- .../Libp2p.Core.TestsBase/TestLocalPeer.cs | 2 + src/libp2p/Libp2p.Core/ChannelFactory.cs | 304 +++++++++--------- src/libp2p/Libp2p.Core/ChannelRequest.cs | 2 +- .../Extensions/ChannelFactoryExtensions.cs | 10 +- src/libp2p/Libp2p.Core/IChannelFactory.cs | 33 +- src/libp2p/Libp2p.Core/IChannelRequest.cs | 10 +- src/libp2p/Libp2p.Core/IPeer.cs | 2 +- src/libp2p/Libp2p.Core/IPeerContext.cs | 29 +- src/libp2p/Libp2p.Core/Peer.cs | 167 ++++------ src/libp2p/Libp2p.Core/PeerContext.cs | 2 +- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 19 +- src/libp2p/Libp2p.Core/PeerId.cs | 4 +- src/libp2p/Libp2p.Core/TransportContext.cs | 6 +- .../IdentifyProtocol.cs | 6 +- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 4 +- .../MultistreamProtocol.cs | 13 +- .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 16 +- .../Libp2p.Protocols.Ping/PingProtocol.cs | 18 +- .../PlainTextProtocol.cs | 4 +- .../Libp2p.Protocols.Pubsub/ManagedPeer.cs | 2 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 16 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 4 +- .../Libp2p.Protocols.Quic/QuicProtocol.cs | 8 +- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 15 +- src/samples/chat/Program.cs | 2 +- src/samples/transport-interop/Program.cs | 2 +- 27 files changed, 330 insertions(+), 400 deletions(-) diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index f343868c..e5a7a246 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -21,14 +21,14 @@ public async Task E2e() IConnectionProtocol cProto = new CProto(); ISessionProtocol sProto = new SProto(); - BuilderContext protocolStackSettings = new BuilderContext + ProtocolStackSettings protocolStackSettings = new() { Protocols = new Dictionary - { - { tProto, [ cProto] }, - { cProto, [sProto] }, - { sProto, [] }, - }, + { + { tProto, [ cProto] }, + { cProto, [sProto] }, + { sProto, [] }, + }, TopProtocols = [tProto] }; @@ -63,7 +63,7 @@ public async Task E2e() } } -class BuilderContext : IProtocolStackSettings +class ProtocolStackSettings : IProtocolStackSettings { public Dictionary? Protocols { get; set; } = new Dictionary { }; public IProtocol[]? TopProtocols { get; set; } = []; @@ -81,7 +81,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr using ITransportConnectionContext connectionCtx = context.CreateConnection(); - IChannel topChan = connectionCtx.SubListen(); + IChannel topChan = connectionCtx.Upgrade(); connectionCtx.Token.Register(() => topChan.CloseAsync()); @@ -89,14 +89,14 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr while (true) { received = await ContextTests.tcp.ReadAsync(1, ReadBlockingMode.WaitAny); - if(received.Result != IOResult.Ok) + if (received.Result != IOResult.Ok) { break; } var sent = await topChan.WriteAsync(received.Data); - if (sent!= IOResult.Ok) + if (sent != IOResult.Ok) { break; } @@ -111,7 +111,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr public async Task DialAsync(ITransportConnectionContext context, Multiaddress listenAddr, CancellationToken token) { - IChannel topChan = context.SubDial(); + IChannel topChan = context.Upgrade(); context.Token.Register(() => topChan.CloseAsync()); @@ -143,8 +143,8 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) { try { - using IConnectionSessionContext session = context.CreateSession(new PeerId(new Dto.PublicKey())); - IChannel topChan = context.SubDial(); + using IConnectionSessionContext session = context.CreateSession(); + IChannel topChan = context.Upgrade(); ReadResult received; while (true) @@ -174,8 +174,8 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { try { - using IConnectionSessionContext session = context.CreateSession(new PeerId(new Dto.PublicKey())); - IChannel topChan = context.SubListen(); + using IConnectionSessionContext session = context.CreateSession(); + IChannel topChan = context.Upgrade(); ReadResult received; while (true) diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs index db6def17..e5eb3233 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs @@ -48,6 +48,8 @@ public TestRemotePeer(Multiaddress addr) public Identity Identity { get; set; } public Multiaddress Address { get; set; } + public Multiaddress RemoteAddress => new Multiaddress(); + public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { return Task.CompletedTask; diff --git a/src/libp2p/Libp2p.Core/ChannelFactory.cs b/src/libp2p/Libp2p.Core/ChannelFactory.cs index ffff70ba..91a02a28 100644 --- a/src/libp2p/Libp2p.Core/ChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/ChannelFactory.cs @@ -1,152 +1,152 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Nethermind.Libp2p.Core.Exceptions; -using Nethermind.Libp2p.Core.Extensions; - -namespace Nethermind.Libp2p.Core; - -public class ChannelFactory : IChannelFactory -{ - private readonly IServiceProvider _serviceProvider; - private readonly ILoggerFactory? _loggerFactory; - private IDictionary _factories; - private readonly ILogger? _logger; - - public ChannelFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - _loggerFactory = _serviceProvider.GetService(); - _logger = _loggerFactory?.CreateLogger(); - } - - public IEnumerable SubProtocols => _factories.Keys; - - //public IChannel SubDial(IPeerContext context, IChannelRequest? req = null) - //{ - // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - // if (subProtocolId is not IProtocol subProtocol) - // { - // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - // } - - // Channel channel = new(); - // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - - - // _ = subProtocol.DialAsync(channel.Reverse, channelFactory, context) - // .ContinueWith(async task => - // { - // if (!task.IsCompletedSuccessfully) - // { - // _logger?.DialFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); - // } - // await channel.CloseAsync(); - - // req?.CompletionSource?.SetResult(); - // }); - - // return channel; - //} - - //public IChannel SubListen(IPeerContext context, IChannelRequest? req = null) - //{ - // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - // if (subProtocolId is not IProtocol subProtocol) - // { - // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - // } - - // Channel channel = new(); - // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - - // _ = subProtocol.ListenAsync(channel.Reverse, channelFactory, context) - // .ContinueWith(async task => - // { - // if (!task.IsCompletedSuccessfully) - // { - // _logger?.ListenFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); - // } - // await channel.CloseAsync(); - - // req?.CompletionSource?.SetResult(); - // }); - - // return channel; - //} - - //public Task SubDialAndBind(IChannel parent, IPeerContext context, - // IChannelRequest? req = null) - //{ - // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - - // if (subProtocolId is not IProtocol subProtocol) - // { - // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - // } - - // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - // return subProtocol.DialAsync(((Channel)parent), channelFactory, context) - // .ContinueWith(async task => - // { - // if (!task.IsCompletedSuccessfully) - // { - // _logger?.DialAndBindFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); - // } - // await parent.CloseAsync(); - - // req?.CompletionSource?.SetResult(); - // }); - //} - - //public Task SubListenAndBind(IChannel parent, IPeerContext context, - // IChannelRequest? req = null) - //{ - // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - - // if (subProtocolId is not IProtocol subProtocol) - // { - // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); - // } - - // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - // return subProtocol.ListenAsync(((Channel)parent), channelFactory, context) - // .ContinueWith(async task => - // { - // await parent.CloseAsync(); - // req?.CompletionSource?.SetResult(); - // }); - //} - - public ChannelFactory Setup(IDictionary factories) - { - _factories = factories; - return this; - } - - public IChannel SubDial(IChannelRequest? request = null) - { - throw new NotImplementedException(); - } - - public IChannel SubListen(IChannelRequest? request = null) - { - throw new NotImplementedException(); - } - - public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) - { - throw new NotImplementedException(); - } - - public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) - { - throw new NotImplementedException(); - } -} +//// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +//// SPDX-License-Identifier: MIT + +//using Microsoft.Extensions.DependencyInjection; +//using Microsoft.Extensions.Logging; +//using Nethermind.Libp2p.Core.Exceptions; +//using Nethermind.Libp2p.Core.Extensions; + +//namespace Nethermind.Libp2p.Core; + +//public class ChannelFactory : IChannelFactory +//{ +// private readonly IServiceProvider _serviceProvider; +// private readonly ILoggerFactory? _loggerFactory; +// private IDictionary _factories; +// private readonly ILogger? _logger; + +// public ChannelFactory(IServiceProvider serviceProvider) +// { +// _serviceProvider = serviceProvider; +// _loggerFactory = _serviceProvider.GetService(); +// _logger = _loggerFactory?.CreateLogger(); +// } + +// public IEnumerable SubProtocols => _factories.Keys; + +// //public IChannel SubDial(IPeerContext context, IChannelRequest? req = null) +// //{ +// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); +// // if (subProtocolId is not IProtocol subProtocol) +// // { +// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); +// // } + +// // Channel channel = new(); +// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + + + +// // _ = subProtocol.DialAsync(channel.Reverse, channelFactory, context) +// // .ContinueWith(async task => +// // { +// // if (!task.IsCompletedSuccessfully) +// // { +// // _logger?.DialFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); +// // } +// // await channel.CloseAsync(); + +// // req?.CompletionSource?.SetResult(); +// // }); + +// // return channel; +// //} + +// //public IChannel SubListen(IPeerContext context, IChannelRequest? req = null) +// //{ +// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); +// // if (subProtocolId is not IProtocol subProtocol) +// // { +// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); +// // } + +// // Channel channel = new(); +// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + + +// // _ = subProtocol.ListenAsync(channel.Reverse, channelFactory, context) +// // .ContinueWith(async task => +// // { +// // if (!task.IsCompletedSuccessfully) +// // { +// // _logger?.ListenFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); +// // } +// // await channel.CloseAsync(); + +// // req?.CompletionSource?.SetResult(); +// // }); + +// // return channel; +// //} + +// //public Task SubDialAndBind(IChannel parent, IPeerContext context, +// // IChannelRequest? req = null) +// //{ +// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + +// // if (subProtocolId is not IProtocol subProtocol) +// // { +// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); +// // } + +// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + +// // return subProtocol.DialAsync(((Channel)parent), channelFactory, context) +// // .ContinueWith(async task => +// // { +// // if (!task.IsCompletedSuccessfully) +// // { +// // _logger?.DialAndBindFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); +// // } +// // await parent.CloseAsync(); + +// // req?.CompletionSource?.SetResult(); +// // }); +// //} + +// //public Task SubListenAndBind(IChannel parent, IPeerContext context, +// // IChannelRequest? req = null) +// //{ +// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); + +// // if (subProtocolId is not IProtocol subProtocol) +// // { +// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); +// // } + +// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; + +// // return subProtocol.ListenAsync(((Channel)parent), channelFactory, context) +// // .ContinueWith(async task => +// // { +// // await parent.CloseAsync(); +// // req?.CompletionSource?.SetResult(); +// // }); +// //} + +// public ChannelFactory Setup(IDictionary factories) +// { +// _factories = factories; +// return this; +// } + +// public IChannel SubDial(IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } + +// public IChannel SubListen(IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } + +// public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } + +// public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) +// { +// throw new NotImplementedException(); +// } +//} diff --git a/src/libp2p/Libp2p.Core/ChannelRequest.cs b/src/libp2p/Libp2p.Core/ChannelRequest.cs index 3f909665..97ca93b8 100644 --- a/src/libp2p/Libp2p.Core/ChannelRequest.cs +++ b/src/libp2p/Libp2p.Core/ChannelRequest.cs @@ -3,7 +3,7 @@ namespace Nethermind.Libp2p.Core; -public class ChannelRequest : IChannelRequest +public class ChannelRequest { public IProtocol? SubProtocol { get; init; } public TaskCompletionSource? CompletionSource { get; init; } diff --git a/src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs b/src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs index 3c5c70ed..22eeed56 100644 --- a/src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs +++ b/src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs @@ -3,8 +3,8 @@ namespace Nethermind.Libp2p.Core.Extensions; -internal static class ChannelFactoryExtensions -{ - public static IEnumerable GetSubProtocols(this ChannelFactory? channelFactory) - => channelFactory?.SubProtocols.Select(protocol => protocol.Id) ?? Enumerable.Empty(); -} +//internal static class ChannelFactoryExtensions +//{ +// public static IEnumerable GetSubProtocols(this ChannelFactory? channelFactory) +// => channelFactory?.SubProtocols.Select(protocol => protocol.Id) ?? Enumerable.Empty(); +//} diff --git a/src/libp2p/Libp2p.Core/IChannelFactory.cs b/src/libp2p/Libp2p.Core/IChannelFactory.cs index 5f4f8db7..d745f59a 100644 --- a/src/libp2p/Libp2p.Core/IChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/IChannelFactory.cs @@ -6,32 +6,15 @@ namespace Nethermind.Libp2p.Core; public interface IChannelFactory { IEnumerable SubProtocols { get; } - IChannel SubDial(IChannelRequest? request = null); - IChannel SubListen(IChannelRequest? request = null); + IChannel Upgrade(UpgradeOptions? options = null); + IChannel Upgrade(IProtocol specificProtocol, UpgradeOptions? options = null); - Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null); - - Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null); - - - IChannel SubDial(IProtocol protocol) - { - return SubDial(new ChannelRequest { SubProtocol = protocol }); - } - - IChannel SubListen(IProtocol protocol) - { - return SubListen(new ChannelRequest { SubProtocol = protocol }); - } - - Task SubDialAndBind(IChannel parentChannel, IProtocol protocol) - { - return SubDialAndBind(parentChannel, new ChannelRequest { SubProtocol = protocol }); - } + Task Upgrade(IChannel parentChannel, UpgradeOptions? options = null); + Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeOptions? options = null); +} - Task SubListenAndBind(IChannel parentChannel, IProtocol protocol) - { - return SubListenAndBind(parentChannel, new ChannelRequest { SubProtocol = protocol }); - } +public class UpgradeOptions +{ + public IProtocol? SelectedProtocol { get; init; } } diff --git a/src/libp2p/Libp2p.Core/IChannelRequest.cs b/src/libp2p/Libp2p.Core/IChannelRequest.cs index 999b486e..251a56bd 100644 --- a/src/libp2p/Libp2p.Core/IChannelRequest.cs +++ b/src/libp2p/Libp2p.Core/IChannelRequest.cs @@ -3,8 +3,8 @@ namespace Nethermind.Libp2p.Core; -public interface IChannelRequest -{ - IProtocol? SubProtocol { get; } - public TaskCompletionSource? CompletionSource { get; } -} +//public interface IChannelRequest +//{ +// IProtocol? SubProtocol { get; } +// public TaskCompletionSource? CompletionSource { get; } +//} diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index 4ed3ece5..ecfe1227 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -26,7 +26,7 @@ public interface IPeer public interface ISession { - Multiaddress Address { get; } + Multiaddress RemoteAddress { get; } Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol; Task DisconnectAsync(); diff --git a/src/libp2p/Libp2p.Core/IPeerContext.cs b/src/libp2p/Libp2p.Core/IPeerContext.cs index 2aadaafd..5f8f5d44 100644 --- a/src/libp2p/Libp2p.Core/IPeerContext.cs +++ b/src/libp2p/Libp2p.Core/IPeerContext.cs @@ -11,21 +11,12 @@ public interface IContext IPeer Peer { get; } } -public interface IConnectionContext +public class Remote { - string Id { get; } - IPeer Peer { get; } - IRemotePeer RemotePeer { get; } + public Multiaddress? Address { get; set; } + public Identity? Identity { get; set; } } -public interface IRemotePeer -{ - Identity Identity { get; set; } - Multiaddress Address { get; set; } -} - - - public interface ITransportContext : IContext { void ListenerReady(Multiaddress addr); @@ -34,25 +25,31 @@ public interface ITransportContext : IContext public interface ITransportConnectionContext : IDisposable, IChannelFactory, IContext { + Remote Remote { get; } CancellationToken Token { get; } - IConnectionSessionContext CreateSession(PeerId peerId); + IConnectionSessionContext CreateSession(); } public interface IConnectionContext : IChannelFactory, IContext { + UpgradeOptions? UpgradeOptions { get; } + Remote Remote { get; } Task DisconnectAsync(); - IConnectionSessionContext CreateSession(PeerId peerId); + IConnectionSessionContext CreateSession(); } public interface IConnectionSessionContext : IDisposable { + Remote Remote { get; } string Id { get; } - IEnumerable DialRequests { get; } + IEnumerable DialRequests { get; } } public interface ISessionContext : IChannelFactory, IContext { - Task DialAsync() where TProtocol: ISessionProtocol; + UpgradeOptions? UpgradeOptions { get; } + Remote Remote { get; } + Task DialAsync() where TProtocol : ISessionProtocol; Task DialAsync(ISessionProtocol[] protocols); Task DisconnectAsync(); } diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index e7fbf4bd..0543dffc 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -4,7 +4,6 @@ using Multiformats.Address; using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Stack; -using Org.BouncyCastle.Tls; using System.Collections.Concurrent; using System.Collections.ObjectModel; @@ -23,10 +22,13 @@ public class LocalPeer(IProtocolStackSettings protocolStackSettings, Identity id protected virtual Task ConnectedTo(ISession peer, bool isDialer) => Task.CompletedTask; - public class Session(LocalPeer localPeer) : ISession, IRemotePeer + public class Session(LocalPeer localPeer, bool isListener) : ISession { - public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + public bool IsListener => isListener; + public Multiaddress RemoteAddress => Remote.Address ?? throw new Libp2pException("Session contains uninitialized remote address."); + public Multiaddress Address { get; set; } + public Remote Remote { get; } = new Remote(); public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { @@ -42,7 +44,15 @@ public Task DialAsync(ISessionProtocol[] protocols, CancellationToken token = de return tcs.Task; } + private readonly BlockingCollection SubDialRequests = []; + private CancellationTokenSource connectionTokenSource = new(); + public Task DisconnectAsync() + { + connectionTokenSource.Cancel(); + return Task.CompletedTask; + } + public CancellationToken ConnectionToken => connectionTokenSource.Token; @@ -51,20 +61,7 @@ public Task DialAsync(ISessionProtocol[] protocols, CancellationToken token = de public Task Connected => ConnectedTcs.Task; - public PeerId PeerId { get; set; } - - public Identity Identity { get; set; } - - public Multiaddress Address { get; set; } - - private BlockingCollection SubDialRequests = new(); - - internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); - - public Task DisconnectAsync() - { - return Task.CompletedTask; - } + internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); } protected virtual IProtocol SelectProtocol(Multiaddress addr) @@ -100,7 +97,7 @@ public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationTok throw new Libp2pSetupException($"{nameof(ITransportProtocol)} should be implemented by {listenerProtocol.GetType()}"); } - ITransportContext ctx = new TransportContext(this, transportProtocol); + ITransportContext ctx = new TransportContext(this, transportProtocol, true); TaskCompletionSource tcs = new(); listenerReadyTcs[ctx] = tcs; @@ -113,11 +110,6 @@ public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationTok await Task.WhenAll(listenTasks); } - public ITransportContext CreateContext(ITransportProtocol tProto) - { - return new TransportContext(this, tProto); - } - public void ListenerReady(ITransportContext sender, Multiaddress addr) { if (listenerReadyTcs.Remove(sender, out TaskCompletionSource? tcs)) @@ -126,25 +118,29 @@ public void ListenerReady(ITransportContext sender, Multiaddress addr) } } - public ITransportConnectionContext CreateConnection(ITransportProtocol proto) + public ITransportConnectionContext CreateConnection(ITransportProtocol proto, bool isListener) { - Session session = new(this); - Sessions.Add(session); + Session session = new(this, isListener); return new TransportConnectionContext(this, session, proto); } - public IConnectionSessionContext CreateSession(Session session, PeerId peerId) + public IConnectionSessionContext CreateSession(Session session) { + if (session.Remote.Address?.GetPeerId() is null) + { + throw new Libp2pSetupException($"{nameof(session.Remote)} should be initialiazed before session creation"); + } + lock (Sessions) { - if(Sessions.Any(s => !object.ReferenceEquals(session, s) && s.PeerId == peerId)) + if (Sessions.Any(s => !ReferenceEquals(session, s) && s.Remote.Address?.GetPeerId() == session.Remote.Address?.GetPeerId())) { throw new Libp2pException("Session is already established"); } - session.PeerId = peerId; - return new ConnectionSessionContext(this, session); + Sessions.Add(session); } + return new ConnectionSessionContext(this, session); } internal IEnumerable GetProtocolsFor(IProtocol protocol) @@ -176,7 +172,7 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token throw new Libp2pSetupException($"{nameof(ITransportProtocol)} should be implemented by {dialerProtocol.GetType()}"); } - Session session = new(this); + Session session = new(this, false); ITransportConnectionContext ctx = new TransportConnectionContext(this, session, transportProtocol); _ = transportProtocol.DialAsync(ctx, addr, token); @@ -185,7 +181,7 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token return session; } - internal IChannel SubDial(LocalPeer localPeer, Session session, IProtocol protocol, IChannelRequest? request) + internal IChannel Upgrade(LocalPeer localPeer, Session session, IProtocol protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) { if (protocolStackSettings.Protocols is null) { @@ -197,51 +193,24 @@ internal IChannel SubDial(LocalPeer localPeer, Session session, IProtocol protoc throw new Libp2pSetupException($"{protocol} is noty added"); } - IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); + IProtocol top = upgradeProtocol ?? protocolStackSettings.Protocols[protocol].First(); Channel res = new Channel(); if (top is IConnectionProtocol tProto) { var ctx = new ConnectionContext(this, session, top); - _ = tProto.DialAsync(res.Reverse, ctx); - } - if (top is ISessionProtocol sProto) - { - var ctx = new SessionContext(this, session, top); - _ = sProto.DialAsync(res.Reverse, ctx); + _ = session.IsListener ? tProto.ListenAsync(res.Reverse, ctx) : tProto.DialAsync(res.Reverse, ctx); } - return res; - } - internal IChannel SubListen(LocalPeer localPeer, Session session, IProtocol protocol, IChannelRequest? request) - { - if (protocolStackSettings.Protocols is null) - { - throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); - } - - if (!protocolStackSettings.Protocols.ContainsKey(protocol)) - { - throw new Libp2pSetupException($"{protocol} is noty added"); - } - - IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); - - Channel res = new Channel(); - if (top is IConnectionProtocol tProto) - { - var ctx = new ConnectionContext(this, session, top); - _ = tProto.ListenAsync(res.Reverse, ctx); - } if (top is ISessionProtocol sProto) { var ctx = new SessionContext(this, session, top); - _ = sProto.ListenAsync(res.Reverse, ctx); + _ = session.IsListener ? sProto.ListenAsync(res.Reverse, ctx) : sProto.DialAsync(res.Reverse, ctx); } return res; } - internal async Task SubDialAndBind(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IChannelRequest? request) + internal async Task Upgrade(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) { if (protocolStackSettings.Protocols is null) { @@ -253,47 +222,20 @@ internal async Task SubDialAndBind(LocalPeer localPeer, Session session, IChanne throw new Libp2pSetupException($"{protocol} is noty added"); } - IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); + IProtocol top = upgradeProtocol ?? protocolStackSettings.Protocols[protocol].First(); if (top is IConnectionProtocol tProto) { - var ctx = new ConnectionContext(this, session, top); + var ctx = new ConnectionContext(this, session, top) { UpgradeOptions = options }; await tProto.DialAsync(parentChannel, ctx); } if (top is ISessionProtocol sProto) { - var ctx = new SessionContext(this, session, top); + var ctx = new SessionContext(this, session, top) { UpgradeOptions = options }; await sProto.DialAsync(parentChannel, ctx); } } - - internal async Task SubListenAndBind(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IChannelRequest? request) - { - if (protocolStackSettings.Protocols is null) - { - throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); - } - - if (!protocolStackSettings.Protocols.ContainsKey(protocol)) - { - throw new Libp2pSetupException($"{protocol} is noty added"); - } - - IProtocol top = request?.SubProtocol ?? protocolStackSettings.Protocols[protocol].First(); - - if (top is IConnectionProtocol tProto) - { - var ctx = new ConnectionContext(this, session, top); - await tProto.ListenAsync(parentChannel, ctx); - } - if (top is ISessionProtocol sProto) - { - var ctx = new SessionContext(this, session, top); - await sProto.ListenAsync(parentChannel, ctx); - } - } - internal void DisposeConnection(TransportConnectionContext transportConnectionContext, Session session) { Sessions.Remove(session); @@ -301,16 +243,18 @@ internal void DisposeConnection(TransportConnectionContext transportConnectionCo internal void DisposeSession(Session session) { - + } } public class ConnectionSessionContext(LocalPeer localPeer, LocalPeer.Session session) : IConnectionSessionContext { - public IEnumerable DialRequests => session.GetRequestQueue(); + public IEnumerable DialRequests => session.GetRequestQueue(); public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + public Remote Remote => session.Remote; + public void Dispose() { localPeer.DisposeSession(session); @@ -319,6 +263,8 @@ public void Dispose() public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), ISessionContext { + public UpgradeOptions? UpgradeOptions { get; init; } + public Task DialAsync() where TProtocol : ISessionProtocol { return session.DialAsync(); @@ -339,9 +285,9 @@ public class TransportConnectionContext(LocalPeer localPeer, LocalPeer.Session s { public CancellationToken Token => session.ConnectionToken; - public IConnectionSessionContext CreateSession(PeerId peerId) + public IConnectionSessionContext CreateSession() { - return localPeer.CreateSession(session, peerId); + return localPeer.CreateSession(session); } public void Dispose() @@ -352,9 +298,11 @@ public void Dispose() public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), IConnectionContext { - public IConnectionSessionContext CreateSession(PeerId peerId) + public UpgradeOptions? UpgradeOptions { get; init; } + + public IConnectionSessionContext CreateSession() { - return localPeer.CreateSession(session, peerId); + return localPeer.CreateSession(session); } public Task DisconnectAsync() @@ -363,11 +311,10 @@ public Task DisconnectAsync() } } -public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) +public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : IChannelFactory { - public IChannelRequest SpecificProtocolRequest { get; set; } public IPeer Peer => localPeer; - public IRemotePeer RemotePeer => session; + public Remote Remote => session.Remote; public IEnumerable SubProtocols => localPeer.GetProtocolsFor(protocol); @@ -377,23 +324,23 @@ public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, IProtoc protected LocalPeer.Session session = session; protected IProtocol protocol = protocol; - public IChannel SubDial(IChannelRequest? request = null) + public IChannel Upgrade(UpgradeOptions? upgradeOptions = null) { - return localPeer.SubDial(localPeer, session, protocol, request); + return localPeer.Upgrade(localPeer, session, protocol, null, upgradeOptions); } - public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) + public Task Upgrade(IChannel parentChannel, UpgradeOptions? upgradeOptions = null) { - return localPeer.SubDialAndBind(localPeer, session, parentChannel, protocol, request); + return localPeer.Upgrade(localPeer, session, parentChannel, protocol, null, upgradeOptions); } - public IChannel SubListen(IChannelRequest? request = null) + public IChannel Upgrade(IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) { - return localPeer.SubListen(localPeer, session, protocol, request); + return localPeer.Upgrade(localPeer, session, protocol, specificProtocol, upgradeOptions); } - public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) + public Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) { - return localPeer.SubListenAndBind(localPeer, session, parentChannel, protocol, request); + return localPeer.Upgrade(localPeer, session, parentChannel, protocol, specificProtocol, upgradeOptions); } } diff --git a/src/libp2p/Libp2p.Core/PeerContext.cs b/src/libp2p/Libp2p.Core/PeerContext.cs index 9c51f356..f5ec5634 100644 --- a/src/libp2p/Libp2p.Core/PeerContext.cs +++ b/src/libp2p/Libp2p.Core/PeerContext.cs @@ -8,7 +8,7 @@ namespace Nethermind.Libp2p.Core; //public class PeerContext : IPeerContext //{ - + // public string Id { get; set; } // public IPeerInfo LocalPeer { get; set; } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index dd7a1347..f447f3af 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -27,6 +27,11 @@ internal static TProtocol CreateProtocolInstance(IServiceProvider ser } } +public class ProtocolRef(IProtocol protocol) +{ + IProtocol Protocol => protocol; +} + public abstract class PeerFactoryBuilderBase : IPeerFactoryBuilder where TBuilder : PeerFactoryBuilderBase, IPeerFactoryBuilder where TPeerFactory : IPeerFactory @@ -64,14 +69,12 @@ protected class ProtocolStack public ProtocolStack? PrevSwitch { get; private set; } public IProtocol Protocol { get; } public HashSet TopProtocols { get; } = new(); - public ChannelFactory UpChannelsFactory { get; } public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IProtocol protocol) { this.builder = builder; this.serviceProvider = serviceProvider; Protocol = protocol; - UpChannelsFactory = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); } public ProtocolStack AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol @@ -145,17 +148,18 @@ public IPeerFactory Build() ProtocolStack? root = transportLayer.Root; - if (root?.Protocol is null || root.UpChannelsFactory is null) + if (root?.Protocol is null) { throw new ApplicationException("Root protocol is not properly defined"); } Dictionary protocols = new(); - static void SetupChannelFactories(ProtocolStack root) + void SetupChannelFactories(ProtocolStack root) { - root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols - .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); + protocols.Add(root.Protocol, root.TopProtocols.Select(p => p.Protocol).ToArray()); + //root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols + // .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); foreach (ProtocolStack topProto in root.TopProtocols) { if (!root.TopProtocols.Any()) @@ -169,7 +173,8 @@ static void SetupChannelFactories(ProtocolStack root) SetupChannelFactories(root); IProtocolStackSettings protocolStackSettings = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); - protocolStackSettings.Protocols = protocols;//root.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol] + protocolStackSettings.Protocols = protocols; + protocolStackSettings.TopProtocols = root.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol]; TPeerFactory result = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); diff --git a/src/libp2p/Libp2p.Core/PeerId.cs b/src/libp2p/Libp2p.Core/PeerId.cs index 29c0a303..5ca6920b 100644 --- a/src/libp2p/Libp2p.Core/PeerId.cs +++ b/src/libp2p/Libp2p.Core/PeerId.cs @@ -127,7 +127,7 @@ static int ComputeHash(params byte[] data) return hashCode ??= ComputeHash(Bytes); } - public static bool operator ==(PeerId left, PeerId right) + public static bool operator ==(PeerId? left, PeerId? right) { if (left is null) { @@ -141,7 +141,7 @@ static int ComputeHash(params byte[] data) return left.Equals(right); } - public static bool operator !=(PeerId left, PeerId right) => !(left == right); + public static bool operator !=(PeerId? left, PeerId? right) => !(left == right); #endregion } diff --git a/src/libp2p/Libp2p.Core/TransportContext.cs b/src/libp2p/Libp2p.Core/TransportContext.cs index 3307b7fe..3d87f819 100644 --- a/src/libp2p/Libp2p.Core/TransportContext.cs +++ b/src/libp2p/Libp2p.Core/TransportContext.cs @@ -5,12 +5,12 @@ namespace Nethermind.Libp2p.Core; -public class TransportContext(LocalPeer peer, ITransportProtocol proto) : ITransportContext +public class TransportContext(LocalPeer peer, ITransportProtocol proto, bool isListener) : ITransportContext { public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); public Identity Identity => peer.Identity; public IPeer Peer => peer; - public IRemotePeer RemotePeer => throw new NotImplementedException(); + public bool IsListener => isListener; public void ListenerReady(Multiaddress addr) { @@ -19,6 +19,6 @@ public void ListenerReady(Multiaddress addr) public ITransportConnectionContext CreateConnection() { - return peer.CreateConnection(proto); + return peer.CreateConnection(proto, isListener); } } diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 7eb101a4..6b4bb819 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -38,9 +38,9 @@ public async Task DialAsync(IChannel channel, ISessionContext context) Identify.Dto.Identify identity = await channel.ReadPrefixedProtobufAsync(Identify.Dto.Identify.Parser); _logger?.LogInformation("Received peer info: {identify}", identity); - context.RemotePeer.Identity = new Identity(PublicKey.Parser.ParseFrom(identity.PublicKey)); + context.Remote.Identity = new Identity(PublicKey.Parser.ParseFrom(identity.PublicKey)); - if (context.RemotePeer.Identity.PublicKey.ToByteString() != identity.PublicKey) + if (context.Remote.Identity.PublicKey.ToByteString() != identity.PublicKey) { throw new PeerConnectionException(); } @@ -56,7 +56,7 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) AgentVersion = _agentVersion, PublicKey = context.Peer.Identity.PublicKey.ToByteString(), ListenAddrs = { ByteString.CopyFrom(context.Peer.Address.Get().ToBytes()) }, - ObservedAddr = ByteString.CopyFrom(context.RemotePeer.Address.Get().ToBytes()), + ObservedAddr = ByteString.CopyFrom(context.Remote.Address!.Get().ToBytes()), Protocols = { _peerFactoryBuilder.AppLayerProtocols.Select(p => p.Id) } }; byte[] ar = new byte[identify.CalculateSize()]; diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index cb7b432d..755edb77 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -47,7 +47,7 @@ await Task.Run(async () => ITransportConnectionContext connectionCtx = context.CreateConnection(); connectionCtx.Token.Register(client.Close); - IChannel upChannel = connectionCtx.SubListen(); + IChannel upChannel = connectionCtx.Upgrade(); Task readTask = Task.Run(async () => { @@ -122,7 +122,7 @@ public async Task DialAsync(ITransportConnectionContext context, Multiaddress re context.Token.Register(client.Close); token.Register(client.Close); - IChannel upChannel = context.SubDial(); + IChannel upChannel = context.Upgrade(); Task receiveTask = Task.Run(async () => { diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index aaf9dcd4..c337c880 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -47,14 +47,13 @@ public async Task DialAsync(IChannel channel, IConnectionContext context) IProtocol? selected = null; - if (context.SpecificProtocolRequest?.SubProtocol is not null) + if (context.UpgradeOptions?.SelectedProtocol is not null) { - _logger?.LogDebug($"Proposing just {context.SpecificProtocolRequest.SubProtocol}"); - if (await DialProtocol(context.SpecificProtocolRequest.SubProtocol) == true) + _logger?.LogDebug($"Proposing just {context.UpgradeOptions.SelectedProtocol}"); + if (await DialProtocol(context.UpgradeOptions.SelectedProtocol) == true) { - selected = context.SpecificProtocolRequest.SubProtocol; + selected = context.UpgradeOptions.SelectedProtocol; } - context.SpecificProtocolRequest = null; } else { @@ -79,7 +78,7 @@ public async Task DialAsync(IChannel channel, IConnectionContext context) return; } _logger?.LogDebug($"Protocol selected during dialing: {selected}"); - await context.SubDialAndBind(channel, selected); + await context.Upgrade(channel, selected); } public async Task ListenAsync(IChannel channel, IConnectionContext context) @@ -113,7 +112,7 @@ public async Task ListenAsync(IChannel channel, IConnectionContext context) } _logger?.LogDebug($"Protocol selected during listening: {selected}"); - await context.SubListenAndBind(channel, selected); + await context.Upgrade(channel, selected); } private async Task SendHello(IChannel channel) diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index c1c7ef6d..29b77548 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -61,9 +61,9 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) //var key = new byte[] { 0x1 }.Concat(clientStatic.PublicKey).ToArray(); PeerId remotePeerId = new(msg1KeyDecoded); - if (!context.RemotePeer.Address.Has()) + if (!context.Remote.Address.Has()) { - context.RemotePeer.Address.Add(new P2P(remotePeerId.ToString())); + context.Remote.Address.Add(new P2P(remotePeerId.ToString())); } byte[] msg = [.. Encoding.UTF8.GetBytes(PayloadSigPrefix), .. ByteString.CopyFrom(clientStatic.PublicKey)]; @@ -90,9 +90,9 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) await downChannel.WriteAsync(new ReadOnlySequence(buffer, 0, msg2.BytesWritten)); Transport? transport = msg2.Transport; - _logger?.LogDebug("Established connection to {peer}", context.RemotePeer.Address); + _logger?.LogDebug("Established connection to {peer}", context.Remote.Address); - IChannel upChannel = context.SubDial(); + IChannel upChannel = context.Upgrade(); await ExchangeData(transport, downChannel, upChannel); @@ -143,14 +143,14 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) PeerId remotePeerId = new(msg2KeyDecoded); - if (!context.RemotePeer.Address.Has()) + if (!context.Remote.Address.Has()) { - context.RemotePeer.Address.Add(new P2P(remotePeerId.ToString())); + context.Remote.Address.Add(new P2P(remotePeerId.ToString())); } - _logger?.LogDebug("Established connection to {peer}", context.RemotePeer.Address); + _logger?.LogDebug("Established connection to {peer}", context.Remote.Address); - IChannel upChannel = context.SubListen(); + IChannel upChannel = context.Upgrade(); await ExchangeData(transport, downChannel, upChannel); diff --git a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs index 5c0632d0..7f47e8aa 100644 --- a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs @@ -30,31 +30,31 @@ public async Task DialAsync(IChannel channel, ISessionContext context) _random.NextBytes(ping.AsSpan(0, PayloadLength)); ReadOnlySequence bytes = new(ping); - _logger?.LogPing(context.RemotePeer.Address); + _logger?.LogPing(context.Remote.Address); await channel.WriteAsync(bytes); _logger?.LogTrace("Sent ping: {ping}", Convert.ToHexString(ping)); - _logger?.ReadingPong(context.RemotePeer.Address); + _logger?.ReadingPong(context.Remote.Address); ReadOnlySequence response = await channel.ReadAsync(PayloadLength, ReadBlockingMode.WaitAll).OrThrow(); _logger?.LogTrace("Received pong: {ping}", Convert.ToHexString(ping)); - _logger?.VerifyingPong(context.RemotePeer.Address); + _logger?.VerifyingPong(context.Remote.Address); if (!ping[0..PayloadLength].SequenceEqual(response.ToArray())) { - _logger?.PingFailed(context.RemotePeer.Address); + _logger?.PingFailed(context.Remote.Address); throw new ApplicationException(); } - _logger?.LogPinged(context.RemotePeer.Address); + _logger?.LogPinged(context.Remote.Address); } public async Task ListenAsync(IChannel channel, ISessionContext context) { - _logger?.PingListenStarted(context.RemotePeer.Address); + _logger?.PingListenStarted(context.Remote.Address); while (true) { - _logger?.ReadingPing(context.RemotePeer.Address); + _logger?.ReadingPing(context.Remote.Address); ReadResult read = await channel.ReadAsync(PayloadLength, ReadBlockingMode.WaitAny); if (read.Result != IOResult.Ok) { @@ -64,11 +64,11 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) byte[] ping = read.Data.ToArray(); _logger?.LogTrace("Received ping: {ping}", Convert.ToHexString(ping)); - _logger?.ReturningPong(context.RemotePeer.Address); + _logger?.ReturningPong(context.Remote.Address); await channel.WriteAsync(new ReadOnlySequence(ping)); _logger?.LogTrace("Sent pong: {ping}", Convert.ToHexString(ping)); } - _logger?.PingFinished(context.RemotePeer.Address); + _logger?.PingFinished(context.Remote.Address); } } diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs index 16c8cc1c..6525e848 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs @@ -34,8 +34,6 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext buf = (await channel.ReadAsync(structSize).OrThrow()).ToArray(); Exchange? dest = Exchange.Parser.ParseFrom(buf); - await (isListener - ? context.SubListenAndBind(channel) - : context.SubDialAndBind(channel)); + await context.Upgrade(channel); } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs b/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs index f8c98f72..67cb3b6d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs @@ -29,7 +29,7 @@ internal async Task DialAsync(Multiaddress[] addrs, CancellationToken foreach (KeyValuePair c in cancellations) { - if (c.Key != firstConnected.Address) + if (c.Key != firstConnected.RemoteAddress) { c.Value.Cancel(false); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 3a7ceb76..7a4f2f0e 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -27,11 +27,11 @@ public PubsubProtocol(string protocolId, PubsubRouter router, ILoggerFactory? lo public async Task DialAsync(IChannel channel, ISessionContext context) { - string peerId = context.RemotePeer.Address.Get().ToString()!; - _logger?.LogDebug($"Dialed({context.Id}) {context.RemotePeer.Address}"); + string peerId = context.Remote.Address.Get().ToString()!; + _logger?.LogDebug($"Dialed({context.Id}) {context.Remote.Address}"); TaskCompletionSource dialTcs = new(); - CancellationToken token = router.OutboundConnection(context.RemotePeer.Address, Id, dialTcs.Task, (rpc) => + CancellationToken token = router.OutboundConnection(context.Remote.Address, Id, dialTcs.Task, (rpc) => { var t = channel.WriteSizeAndProtobufAsync(rpc); _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); @@ -47,20 +47,20 @@ public async Task DialAsync(IChannel channel, ISessionContext context) await channel; dialTcs.SetResult(); - _logger?.LogDebug($"Finished dial({context.Id}) {context.RemotePeer.Address}"); + _logger?.LogDebug($"Finished dial({context.Id}) {context.Remote.Address}"); } public async Task ListenAsync(IChannel channel, ISessionContext context) { - string peerId = context.RemotePeer.Address.Get().ToString()!; - _logger?.LogDebug($"Listen({context.Id}) to {context.RemotePeer.Address}"); + string peerId = context.Remote.Address.Get().ToString()!; + _logger?.LogDebug($"Listen({context.Id}) to {context.Remote.Address}"); TaskCompletionSource listTcs = new(); TaskCompletionSource dialTcs = new(); - CancellationToken token = router.InboundConnection(context.RemotePeer.Address, Id, listTcs.Task, dialTcs.Task, () => + CancellationToken token = router.InboundConnection(context.Remote.Address, Id, listTcs.Task, dialTcs.Task, () => { _ = context.DialAsync([this]); return dialTcs.Task; @@ -81,7 +81,7 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) } } listTcs.SetResult(); - _logger?.LogDebug($"Finished({context.Id}) list {context.RemotePeer.Address}"); + _logger?.LogDebug($"Finished({context.Id}) list {context.Remote.Address}"); } public override string ToString() diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 7b7c2d82..160f5a3f 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -197,10 +197,10 @@ private async Task StartDiscoveryAsync(IDiscoveryProtocol discoveryProtocol, Can { ISession session = await peer.DialAsync(addrs, token); - if (!peerState.ContainsKey(session.Address.Get().ToString())) + if (!peerState.ContainsKey(session.RemoteAddress.Get().ToString())) { await session.DialAsync(token); - if (peerState.TryGetValue(session.Address.GetPeerId()!, out PubsubPeer? state) && state.InititatedBy == ConnectionInitiation.Remote) + if (peerState.TryGetValue(session.RemoteAddress.GetPeerId()!, out PubsubPeer? state) && state.InititatedBy == ConnectionInitiation.Remote) { _ = session.DisconnectAsync(); } diff --git a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs index 294200fa..763c73f7 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs @@ -151,14 +151,14 @@ private async Task ProcessStreams(ITransportConnectionContext context, QuicConne { _logger?.LogDebug("New connection to {remote}", connection.RemoteEndPoint); - using IConnectionSessionContext session = context.CreateSession(null!); + using IConnectionSessionContext session = context.CreateSession(); _ = Task.Run(async () => { - foreach (IChannelRequest request in session.DialRequests) + foreach (ChannelRequest request in session.DialRequests) { QuicStream stream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); - IChannel upChannel = context.SubDial(request); + IChannel upChannel = context.Upgrade(new UpgradeOptions { SelectedProtocol = request.SubProtocol }); ExchangeData(stream, upChannel, request.CompletionSource); } }, token); @@ -166,7 +166,7 @@ private async Task ProcessStreams(ITransportConnectionContext context, QuicConne while (!token.IsCancellationRequested) { QuicStream inboundStream = await connection.AcceptInboundStreamAsync(token); - IChannel upChannel = context.SubListen(); + IChannel upChannel = context.Upgrade(); ExchangeData(inboundStream, upChannel, null); } } diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 16aac3f8..e945bf7c 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -37,7 +37,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext try { int streamIdCounter = isListener ? 2 : 1; - IConnectionSessionContext session = context.CreateSession(context.RemotePeer.Address.GetPeerId()!); + IConnectionSessionContext session = context.CreateSession(); int pingCounter = 0; using Timer timer = new((s) => @@ -47,7 +47,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext _ = Task.Run(() => { - foreach (IChannelRequest request in session.DialRequests) + foreach (ChannelRequest request in session.DialRequests) { int streamId = streamIdCounter; Interlocked.Add(ref streamIdCounter, 2); @@ -207,7 +207,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext await WriteGoAwayAsync(channel, SessionTerminationCode.Ok); - ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, IChannelRequest? channelRequest) + ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, ChannelRequest? channelRequest) { bool isListenerChannel = isListener ^ (streamId % 2 == 0); @@ -216,12 +216,11 @@ ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, ICha if (isListenerChannel) { - upChannel = context.SubListen(); + upChannel = context.Upgrade(); } else { - context.SpecificProtocolRequest = channelRequest; - upChannel = context.SubDial(); + upChannel = context.Upgrade(new UpgradeOptions { SelectedProtocol = channelRequest?.SubProtocol }); } ChannelState state = new(upChannel, channelRequest); @@ -352,10 +351,10 @@ private Task WriteGoAwayAsync(IWriter channel, SessionTerminationCode code) => StreamID = 0, }); - private class ChannelState(IChannel? channel = default, IChannelRequest? request = default) + private class ChannelState(IChannel? channel = default, ChannelRequest? request = default) { public IChannel? Channel { get; set; } = channel; - public IChannelRequest? Request { get; set; } = request; + public ChannelRequest? Request { get; set; } = request; public DataWindow LocalWindow { get; } = new(); public DataWindow RemoteWindow { get; } = new(); diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index afe58273..7a30bbda 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -49,7 +49,7 @@ "/ip4/0.0.0.0/udp/{0}/quic-v1" : "/ip4/0.0.0.0/tcp/{0}"; - peer.OnConnection += async newSession => logger.LogInformation("A peer connected {remote}", newSession.Address); + peer.OnConnection += async newSession => logger.LogInformation("A peer connected {remote}", newSession.RemoteAddress); await peer.StartListenAsync( [string.Format(addrTemplate, args.Length > 0 && args[0] == "-sp" ? args[1] : "0")], diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index a65f4b9d..6f19c802 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -85,7 +85,7 @@ CancellationTokenSource listennTcs = new(); await localPeer.StartListenAsync([builder.MakeAddress(ip)], listennTcs.Token); - localPeer.OnConnection += (peer) => { Log($"Connected {peer.Address}"); return Task.CompletedTask; }; + localPeer.OnConnection += (session) => { Log($"Connected {session.RemoteAddress}"); return Task.CompletedTask; }; Log($"Listening on {localPeer.Address}"); db.ListRightPush(new RedisKey("listenerAddr"), new RedisValue(localPeer.Address.ToString())); await Task.Delay(testTimeoutSeconds * 1000); From 49fb0d8429d4cab349496eaa6355d865f38db5ff Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 14 Aug 2024 16:22:37 +0300 Subject: [PATCH 05/25] Before merged context --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 12 ++--- .../Libp2p.Core/ILibp2pBuilderContext.cs | 4 +- src/libp2p/Libp2p.Core/Peer.cs | 45 +++++++++---------- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 35 ++++++++++----- src/libp2p/Libp2p.Core/TransportContext.cs | 2 +- .../PlainTextProtocol.cs | 17 ++++++- src/libp2p/Libp2p/Libp2pBuilderContext.cs | 4 +- src/libp2p/Libp2p/Libp2pPeerFactory.cs | 8 ++-- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 4 +- 9 files changed, 77 insertions(+), 54 deletions(-) diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index e5a7a246..e56ed8a2 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -17,13 +17,13 @@ public class ContextTests [Test] public async Task E2e() { - ITransportProtocol tProto = new TProto(); - IConnectionProtocol cProto = new CProto(); - ISessionProtocol sProto = new SProto(); + ProtocolRef tProto = new ProtocolRef(new TProto()); + ProtocolRef cProto = new ProtocolRef(new CProto()); + ProtocolRef sProto = new ProtocolRef(new SProto()); ProtocolStackSettings protocolStackSettings = new() { - Protocols = new Dictionary + Protocols = new Dictionary { { tProto, [ cProto] }, { cProto, [sProto] }, @@ -65,8 +65,8 @@ public async Task E2e() class ProtocolStackSettings : IProtocolStackSettings { - public Dictionary? Protocols { get; set; } = new Dictionary { }; - public IProtocol[]? TopProtocols { get; set; } = []; + public Dictionary? Protocols { get; set; } = []; + public ProtocolRef[]? TopProtocols { get; set; } = []; } class TProto : ITransportProtocol diff --git a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs index 739a704f..f6a1cfe7 100644 --- a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs +++ b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs @@ -7,6 +7,6 @@ namespace Nethermind.Libp2p.Stack; public interface IProtocolStackSettings { - Dictionary? Protocols { get; set; } - IProtocol[]? TopProtocols { get; set; } + Dictionary? Protocols { get; set; } + ProtocolRef[]? TopProtocols { get; set; } } diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 0543dffc..c114c34b 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -64,7 +64,7 @@ public Task DisconnectAsync() internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); } - protected virtual IProtocol SelectProtocol(Multiaddress addr) + protected virtual ProtocolRef SelectProtocol(Multiaddress addr) { if (protocolStackSettings.TopProtocols is null) { @@ -90,14 +90,14 @@ public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationTok foreach (Multiaddress addr in addrs) { - IProtocol listenerProtocol = SelectProtocol(addr); + ProtocolRef listenerProtocol = SelectProtocol(addr); - if (listenerProtocol is not ITransportProtocol transportProtocol) + if (listenerProtocol.Protocol is not ITransportProtocol transportProtocol) { throw new Libp2pSetupException($"{nameof(ITransportProtocol)} should be implemented by {listenerProtocol.GetType()}"); } - ITransportContext ctx = new TransportContext(this, transportProtocol, true); + ITransportContext ctx = new TransportContext(this, listenerProtocol, true); TaskCompletionSource tcs = new(); listenerReadyTcs[ctx] = tcs; @@ -118,7 +118,7 @@ public void ListenerReady(ITransportContext sender, Multiaddress addr) } } - public ITransportConnectionContext CreateConnection(ITransportProtocol proto, bool isListener) + public ITransportConnectionContext CreateConnection(ProtocolRef proto, bool isListener) { Session session = new(this, isListener); return new TransportConnectionContext(this, session, proto); @@ -143,7 +143,7 @@ public IConnectionSessionContext CreateSession(Session session) return new ConnectionSessionContext(this, session); } - internal IEnumerable GetProtocolsFor(IProtocol protocol) + internal IEnumerable GetProtocolsFor(ProtocolRef protocol) { if (protocolStackSettings.Protocols is null) { @@ -155,25 +155,25 @@ internal IEnumerable GetProtocolsFor(IProtocol protocol) throw new Libp2pSetupException($"{protocol} is noty added"); } - return protocolStackSettings.Protocols[protocol]; + return protocolStackSettings.Protocols[protocol].Select(p => p.Protocol); } internal IProtocol? GetProtocolInstance() { - return protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.GetType() == typeof(TProtocol)); + return protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.Protocol.GetType() == typeof(TProtocol))?.Protocol; } public async Task DialAsync(Multiaddress addr, CancellationToken token = default) { - IProtocol dialerProtocol = SelectProtocol(addr); + ProtocolRef dialerProtocol = SelectProtocol(addr); - if (dialerProtocol is not ITransportProtocol transportProtocol) + if (dialerProtocol.Protocol is not ITransportProtocol transportProtocol) { throw new Libp2pSetupException($"{nameof(ITransportProtocol)} should be implemented by {dialerProtocol.GetType()}"); } Session session = new(this, false); - ITransportConnectionContext ctx = new TransportConnectionContext(this, session, transportProtocol); + ITransportConnectionContext ctx = new TransportConnectionContext(this, session, dialerProtocol); _ = transportProtocol.DialAsync(ctx, addr, token); @@ -181,7 +181,7 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token return session; } - internal IChannel Upgrade(LocalPeer localPeer, Session session, IProtocol protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) + internal IChannel Upgrade(LocalPeer localPeer, Session session, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) { if (protocolStackSettings.Protocols is null) { @@ -193,7 +193,7 @@ internal IChannel Upgrade(LocalPeer localPeer, Session session, IProtocol protoc throw new Libp2pSetupException($"{protocol} is noty added"); } - IProtocol top = upgradeProtocol ?? protocolStackSettings.Protocols[protocol].First(); + ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].First(); Channel res = new Channel(); if (top is IConnectionProtocol tProto) @@ -210,19 +210,14 @@ internal IChannel Upgrade(LocalPeer localPeer, Session session, IProtocol protoc return res; } - internal async Task Upgrade(LocalPeer localPeer, Session session, IChannel parentChannel, IProtocol protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) + internal async Task Upgrade(LocalPeer localPeer, Session session, IChannel parentChannel, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) { if (protocolStackSettings.Protocols is null) { throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (!protocolStackSettings.Protocols.ContainsKey(protocol)) - { - throw new Libp2pSetupException($"{protocol} is noty added"); - } - - IProtocol top = upgradeProtocol ?? protocolStackSettings.Protocols[protocol].First(); + ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].First(); if (top is IConnectionProtocol tProto) { @@ -261,7 +256,7 @@ public void Dispose() } } -public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), ISessionContext +public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : ContextBase(localPeer, session, protocol), ISessionContext { public UpgradeOptions? UpgradeOptions { get; init; } @@ -281,7 +276,7 @@ public Task DisconnectAsync() } } -public class TransportConnectionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), ITransportConnectionContext +public class TransportConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : ContextBase(localPeer, session, protocol), ITransportConnectionContext { public CancellationToken Token => session.ConnectionToken; @@ -296,7 +291,7 @@ public void Dispose() } } -public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : ContextBase(localPeer, session, protocol), IConnectionContext +public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : ContextBase(localPeer, session, protocol), IConnectionContext { public UpgradeOptions? UpgradeOptions { get; init; } @@ -311,7 +306,7 @@ public Task DisconnectAsync() } } -public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, IProtocol protocol) : IChannelFactory +public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : IChannelFactory { public IPeer Peer => localPeer; public Remote Remote => session.Remote; @@ -322,7 +317,7 @@ public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, IProtoc protected LocalPeer localPeer = localPeer; protected LocalPeer.Session session = session; - protected IProtocol protocol = protocol; + protected ProtocolRef protocol = protocol; public IChannel Upgrade(UpgradeOptions? upgradeOptions = null) { diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index f447f3af..d17f07b8 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -29,7 +29,14 @@ internal static TProtocol CreateProtocolInstance(IServiceProvider ser public class ProtocolRef(IProtocol protocol) { - IProtocol Protocol => protocol; + static int IdCounter = 0; + public string RefId { get; } = Interlocked.Increment(ref IdCounter).ToString(); + public IProtocol Protocol => protocol; + + public override string ToString() + { + return $"ref#{RefId}({Protocol.Id})"; + } } public abstract class PeerFactoryBuilderBase : IPeerFactoryBuilder @@ -50,7 +57,7 @@ protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol { - return new ProtocolStack(this, ServiceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance)); + return new ProtocolStack(this, ServiceProvider, new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance))); } public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol @@ -67,10 +74,10 @@ protected class ProtocolStack public ProtocolStack? Root { get; private set; } public ProtocolStack Parent { get; private set; } public ProtocolStack? PrevSwitch { get; private set; } - public IProtocol Protocol { get; } + public ProtocolRef Protocol { get; } public HashSet TopProtocols { get; } = new(); - public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, IProtocol protocol) + public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, ProtocolRef protocol) { this.builder = builder; this.serviceProvider = serviceProvider; @@ -85,7 +92,7 @@ public ProtocolStack AddAppLayerProtocol(TProtocol? instance = defaul public ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol { - ProtocolStack nextNode = new(builder, serviceProvider, PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance)); + ProtocolStack nextNode = new(builder, serviceProvider, new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance))); return Over(nextNode); } @@ -93,10 +100,10 @@ public ProtocolStack Or(TProtocol? instance = default) where TProtoco { if (Parent is null) { - Parent = new ProtocolStack(builder, serviceProvider, new RootStub()); + Parent = new ProtocolStack(builder, serviceProvider, new ProtocolRef(RootStub.Instance)); Parent.Over(this); } - IProtocol protocol = PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance); + ProtocolRef protocol = new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance)); ProtocolStack stack = new(builder, serviceProvider, protocol); return Or(stack); } @@ -121,7 +128,7 @@ public ProtocolStack Or(ProtocolStack stack) { if (Parent is null) { - Parent = new ProtocolStack(builder, serviceProvider, new RootStub()); + Parent = new ProtocolStack(builder, serviceProvider, new ProtocolRef(RootStub.Instance)); Parent.Over(this); } stack.PrevSwitch = this; @@ -130,7 +137,7 @@ public ProtocolStack Or(ProtocolStack stack) public override string ToString() { - return $"{Protocol.Id}({TopProtocols.Count}): {string.Join(" or ", TopProtocols.Select(p => p.Protocol.Id))}"; + return $"{Protocol}({TopProtocols.Count}): {string.Join(" or ", TopProtocols.Select(p => p.Protocol))}"; } } @@ -153,11 +160,11 @@ public IPeerFactory Build() throw new ApplicationException("Root protocol is not properly defined"); } - Dictionary protocols = new(); + Dictionary protocols = new(); void SetupChannelFactories(ProtocolStack root) { - protocols.Add(root.Protocol, root.TopProtocols.Select(p => p.Protocol).ToArray()); + protocols.TryAdd(root.Protocol, root.TopProtocols.Select(p => p.Protocol).ToArray()); //root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols // .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); foreach (ProtocolStack topProto in root.TopProtocols) @@ -174,7 +181,7 @@ void SetupChannelFactories(ProtocolStack root) IProtocolStackSettings protocolStackSettings = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); protocolStackSettings.Protocols = protocols; - protocolStackSettings.TopProtocols = root.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol]; + protocolStackSettings.TopProtocols = root.Protocol.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol]; TPeerFactory result = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); @@ -194,6 +201,10 @@ public override string ToString() class RootStub : IProtocol { + public static RootStub Instance { get; } = new(); + + private RootStub() { } + public string Id => "protocol hierachy root"; } } diff --git a/src/libp2p/Libp2p.Core/TransportContext.cs b/src/libp2p/Libp2p.Core/TransportContext.cs index 3d87f819..aaf33c4b 100644 --- a/src/libp2p/Libp2p.Core/TransportContext.cs +++ b/src/libp2p/Libp2p.Core/TransportContext.cs @@ -5,7 +5,7 @@ namespace Nethermind.Libp2p.Core; -public class TransportContext(LocalPeer peer, ITransportProtocol proto, bool isListener) : ITransportContext +public class TransportContext(LocalPeer peer, ProtocolRef proto, bool isListener) : ITransportContext { public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); public Identity Identity => peer.Identity; diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs index 6525e848..5ce8d83c 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs @@ -10,7 +10,7 @@ namespace Nethermind.Libp2p.Protocols; /// /// -public class PlainTextProtocol : SymmetricProtocol, IProtocol +public class PlainTextProtocol : SymmetricProtocol, IConnectionProtocol { public string Id => "/plaintext/2.0.0"; @@ -37,3 +37,18 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext await context.Upgrade(channel); } } + +public class RelayProtocol : ISessionProtocol +{ + public string Id => throw new NotImplementedException(); + + public Task DialAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } + + public Task ListenAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } +} diff --git a/src/libp2p/Libp2p/Libp2pBuilderContext.cs b/src/libp2p/Libp2p/Libp2pBuilderContext.cs index d6b14555..6539a487 100644 --- a/src/libp2p/Libp2p/Libp2pBuilderContext.cs +++ b/src/libp2p/Libp2p/Libp2pBuilderContext.cs @@ -7,6 +7,6 @@ namespace Nethermind.Libp2p.Stack; internal class Libp2pBuilderContext : IProtocolStackSettings { - public IProtocol[]? TopProtocols { get; set; } - public Dictionary? Protocols { get; set; } + public ProtocolRef[]? TopProtocols { get; set; } + public Dictionary? Protocols { get; set; } } diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index d624843b..560220d8 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -21,19 +21,19 @@ protected override async Task ConnectedTo(ISession session, bool isDialer) await session.DialAsync(); } - protected override IProtocol SelectProtocol(Multiaddress addr) + protected override ProtocolRef SelectProtocol(Multiaddress addr) { ArgumentNullException.ThrowIfNull(protocolStackSettings.TopProtocols); - ITransportProtocol protocol = null!; + ProtocolRef? protocol; if (addr.Has()) { - protocol = protocolStackSettings.TopProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); + protocol = protocolStackSettings.TopProtocols.FirstOrDefault(proto => proto.Protocol.Id == "quic-v1") ?? throw new ApplicationException("QUICv1 is not supported"); } else if (addr.Has()) { - protocol = protocolStackSettings.TopProtocols!.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); + protocol = protocolStackSettings.TopProtocols!.FirstOrDefault(proto => proto.Protocol.Id == "ip-tcp") ?? throw new ApplicationException("TCP is not supported"); } else { diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index 39ef94d2..38b84aa7 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -37,8 +37,10 @@ protected override ProtocolStack BuildStack() .Over() .Over(); + ProtocolStack relayStack = Over().Over(); + return - Over().Or(tcpStack) + Over().Or(tcpStack).Or(relayStack) .Over() .AddAppLayerProtocol() //.AddAppLayerProtocol() From bc6b3836ab7f2f2d986c7fd09af67c76a1ed15ff Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 15 Aug 2024 15:00:20 +0300 Subject: [PATCH 06/25] Compilable, before tests --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 8 +- src/libp2p/Libp2p.Core/IChannelFactory.cs | 8 + src/libp2p/Libp2p.Core/IPeer.cs | 1 - src/libp2p/Libp2p.Core/IPeerContext.cs | 50 +++--- src/libp2p/Libp2p.Core/IProtocol.cs | 2 +- src/libp2p/Libp2p.Core/Peer.cs | 127 ++++++++------- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 151 ++---------------- src/libp2p/Libp2p.Core/TransportContext.cs | 2 +- .../IdentifyProtocol.cs | 6 +- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 6 +- .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 12 +- .../Libp2p.Protocols.Ping/PingProtocol.cs | 18 +-- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 16 +- .../Libp2p.Protocols.Quic/QuicProtocol.cs | 8 +- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 2 +- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 73 ++++++--- .../NoStackPeerFactoryBuilder.cs | 4 +- src/samples/transport-interop/Program.cs | 42 +++-- 18 files changed, 233 insertions(+), 303 deletions(-) diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index e56ed8a2..aabbdb25 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -78,7 +78,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr try { context.ListenerReady(Multiaddress.Decode("/ip4/127.0.0.1/tcp/4096")); - using ITransportConnectionContext connectionCtx = context.CreateConnection(); + using INewConnectionContext connectionCtx = context.CreateConnection(); IChannel topChan = connectionCtx.Upgrade(); @@ -109,7 +109,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr } } - public async Task DialAsync(ITransportConnectionContext context, Multiaddress listenAddr, CancellationToken token) + public async Task DialAsync(INewConnectionContext context, Multiaddress listenAddr, CancellationToken token) { IChannel topChan = context.Upgrade(); context.Token.Register(() => topChan.CloseAsync()); @@ -143,7 +143,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) { try { - using IConnectionSessionContext session = context.CreateSession(); + using INewSessionContext session = context.UpgradeToSession(); IChannel topChan = context.Upgrade(); ReadResult received; @@ -174,7 +174,7 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { try { - using IConnectionSessionContext session = context.CreateSession(); + using INewSessionContext session = context.UpgradeToSession(); IChannel topChan = context.Upgrade(); ReadResult received; diff --git a/src/libp2p/Libp2p.Core/IChannelFactory.cs b/src/libp2p/Libp2p.Core/IChannelFactory.cs index d745f59a..3d34ea51 100644 --- a/src/libp2p/Libp2p.Core/IChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/IChannelFactory.cs @@ -17,4 +17,12 @@ public interface IChannelFactory public class UpgradeOptions { public IProtocol? SelectedProtocol { get; init; } + public UpgradeModeOverride ModeOverride { get; init; } = UpgradeModeOverride.None; +} + +public enum UpgradeModeOverride +{ + None, + Dial, + Listen, } diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index ecfe1227..c9215581 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -27,7 +27,6 @@ public interface IPeer public interface ISession { Multiaddress RemoteAddress { get; } - Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol; Task DisconnectAsync(); } diff --git a/src/libp2p/Libp2p.Core/IPeerContext.cs b/src/libp2p/Libp2p.Core/IPeerContext.cs index 5f8f5d44..6b36b66e 100644 --- a/src/libp2p/Libp2p.Core/IPeerContext.cs +++ b/src/libp2p/Libp2p.Core/IPeerContext.cs @@ -2,54 +2,52 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using Nethermind.Libp2p.Core.Dto; namespace Nethermind.Libp2p.Core; -public interface IContext +public interface ITransportContext { - string Id { get; } IPeer Peer { get; } + void ListenerReady(Multiaddress addr); + INewConnectionContext CreateConnection(); } -public class Remote +public interface IContextState { - public Multiaddress? Address { get; set; } - public Identity? Identity { get; set; } + string Id { get; } + State State { get; } } -public interface ITransportContext : IContext +public interface IConnectionContext : ITransportContext, IChannelFactory, IContextState { - void ListenerReady(Multiaddress addr); - ITransportConnectionContext CreateConnection(); + UpgradeOptions? UpgradeOptions { get; } + Task DisconnectAsync(); + INewSessionContext UpgradeToSession(); } -public interface ITransportConnectionContext : IDisposable, IChannelFactory, IContext +public interface ISessionContext : IConnectionContext { - Remote Remote { get; } - CancellationToken Token { get; } - IConnectionSessionContext CreateSession(); + Task DialAsync() where TProtocol : ISessionProtocol; + Task DialAsync(ISessionProtocol[] protocols); } -public interface IConnectionContext : IChannelFactory, IContext + +public interface INewConnectionContext : IDisposable, IChannelFactory, IContextState { - UpgradeOptions? UpgradeOptions { get; } - Remote Remote { get; } - Task DisconnectAsync(); - IConnectionSessionContext CreateSession(); + IPeer Peer { get; } + CancellationToken Token { get; } + INewSessionContext UpgradeToSession(); } -public interface IConnectionSessionContext : IDisposable +public interface INewSessionContext : IDisposable, INewConnectionContext { - Remote Remote { get; } - string Id { get; } IEnumerable DialRequests { get; } } -public interface ISessionContext : IChannelFactory, IContext +public class State { - UpgradeOptions? UpgradeOptions { get; } - Remote Remote { get; } - Task DialAsync() where TProtocol : ISessionProtocol; - Task DialAsync(ISessionProtocol[] protocols); - Task DisconnectAsync(); + public Multiaddress? LocalAddress { get; set; } + public Multiaddress? RemoteAddress { get; set; } + public PublicKey? RemotePublicKey { get; set; } } diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index 47de4d9d..8b8f35f9 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -31,7 +31,7 @@ public interface ITransportProtocol : IProtocol /// Factory that spawns new channels used to interact with top layer protocols /// Holds information about local and remote peers /// - Task DialAsync(ITransportConnectionContext context, Multiaddress listenAddr, CancellationToken token); + Task DialAsync(INewConnectionContext context, Multiaddress listenAddr, CancellationToken token); } public interface IConnectionProtocol : IProtocol diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index c114c34b..d9f7f2df 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -25,10 +25,10 @@ public class LocalPeer(IProtocolStackSettings protocolStackSettings, Identity id public class Session(LocalPeer localPeer, bool isListener) : ISession { public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + public State State { get; } = new(); + public bool IsListener => isListener; - public Multiaddress RemoteAddress => Remote.Address ?? throw new Libp2pException("Session contains uninitialized remote address."); - public Multiaddress Address { get; set; } - public Remote Remote { get; } = new Remote(); + public Multiaddress RemoteAddress => State.RemoteAddress ?? throw new Libp2pException("Session contains uninitialized remote address."); public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { @@ -53,7 +53,6 @@ public Task DisconnectAsync() return Task.CompletedTask; } - public CancellationToken ConnectionToken => connectionTokenSource.Token; @@ -80,7 +79,7 @@ protected virtual ProtocolRef SelectProtocol(Multiaddress addr) return protocolStackSettings.TopProtocols.Single(); } - Dictionary> listenerReadyTcs = new(); + Dictionary> listenerReadyTcs = new(); public event OnConnection OnConnection; @@ -110,7 +109,7 @@ public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationTok await Task.WhenAll(listenTasks); } - public void ListenerReady(ITransportContext sender, Multiaddress addr) + public void ListenerReady(object sender, Multiaddress addr) { if (listenerReadyTcs.Remove(sender, out TaskCompletionSource? tcs)) { @@ -118,29 +117,29 @@ public void ListenerReady(ITransportContext sender, Multiaddress addr) } } - public ITransportConnectionContext CreateConnection(ProtocolRef proto, bool isListener) + public INewConnectionContext CreateConnection(ProtocolRef proto, bool isListener) { Session session = new(this, isListener); - return new TransportConnectionContext(this, session, proto); + return new NewConnectionContext(this, session, proto, isListener); } - public IConnectionSessionContext CreateSession(Session session) + public INewSessionContext CreateSession(Session session, ProtocolRef proto, bool isListener) { - if (session.Remote.Address?.GetPeerId() is null) + if (session.State.RemoteAddress?.GetPeerId() is null) { - throw new Libp2pSetupException($"{nameof(session.Remote)} should be initialiazed before session creation"); + throw new Libp2pSetupException($"{nameof(session.State.RemoteAddress)} should be initialiazed before session creation"); } lock (Sessions) { - if (Sessions.Any(s => !ReferenceEquals(session, s) && s.Remote.Address?.GetPeerId() == session.Remote.Address?.GetPeerId())) + if (Sessions.Any(s => !ReferenceEquals(session, s) && s.State.RemoteAddress.GetPeerId() == session.State.RemoteAddress?.GetPeerId())) { throw new Libp2pException("Session is already established"); } Sessions.Add(session); } - return new ConnectionSessionContext(this, session); + return new NewSessionContext(this, session, proto, isListener); } internal IEnumerable GetProtocolsFor(ProtocolRef protocol) @@ -173,7 +172,7 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token } Session session = new(this, false); - ITransportConnectionContext ctx = new TransportConnectionContext(this, session, dialerProtocol); + INewConnectionContext ctx = new NewConnectionContext(this, session, dialerProtocol, session.IsListener); _ = transportProtocol.DialAsync(ctx, addr, token); @@ -196,17 +195,25 @@ internal IChannel Upgrade(LocalPeer localPeer, Session session, ProtocolRef prot ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].First(); Channel res = new Channel(); - if (top is IConnectionProtocol tProto) - { - var ctx = new ConnectionContext(this, session, top); - _ = session.IsListener ? tProto.ListenAsync(res.Reverse, ctx) : tProto.DialAsync(res.Reverse, ctx); - } - if (top is ISessionProtocol sProto) + bool isListener = session.IsListener && options?.ModeOverride != UpgradeModeOverride.Dial; + + switch (top.Protocol) { - var ctx = new SessionContext(this, session, top); - _ = session.IsListener ? sProto.ListenAsync(res.Reverse, ctx) : sProto.DialAsync(res.Reverse, ctx); + case IConnectionProtocol tProto: + { + var ctx = new ConnectionContext(this, session, top, isListener); + _ = isListener ? tProto.ListenAsync(res.Reverse, ctx) : tProto.DialAsync(res.Reverse, ctx); + break; + } + case ISessionProtocol sProto: + { + var ctx = new SessionContext(this, session, top, isListener); + _ = isListener ? sProto.ListenAsync(res.Reverse, ctx) : sProto.DialAsync(res.Reverse, ctx); + break; + } } + return res; } @@ -218,45 +225,41 @@ internal async Task Upgrade(LocalPeer localPeer, Session session, IChannel paren } ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].First(); + bool isListener = session.IsListener && options?.ModeOverride != UpgradeModeOverride.Dial; - if (top is IConnectionProtocol tProto) - { - var ctx = new ConnectionContext(this, session, top) { UpgradeOptions = options }; - await tProto.DialAsync(parentChannel, ctx); - } - if (top is ISessionProtocol sProto) + switch (top.Protocol) { - var ctx = new SessionContext(this, session, top) { UpgradeOptions = options }; - await sProto.DialAsync(parentChannel, ctx); + case IConnectionProtocol tProto: + { + var ctx = new ConnectionContext(this, session, top, isListener) { UpgradeOptions = options }; + await tProto.DialAsync(parentChannel, ctx); + break; + } + case ISessionProtocol sProto: + { + var ctx = new SessionContext(this, session, top, isListener) { UpgradeOptions = options }; + await sProto.DialAsync(parentChannel, ctx); + break; + } } } - - internal void DisposeConnection(TransportConnectionContext transportConnectionContext, Session session) - { - Sessions.Remove(session); - } - - internal void DisposeSession(Session session) - { - - } } -public class ConnectionSessionContext(LocalPeer localPeer, LocalPeer.Session session) : IConnectionSessionContext +public class NewSessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), INewSessionContext { public IEnumerable DialRequests => session.GetRequestQueue(); public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); - public Remote Remote => session.Remote; + public CancellationToken Token => session.ConnectionToken; public void Dispose() { - localPeer.DisposeSession(session); + } } -public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : ContextBase(localPeer, session, protocol), ISessionContext +public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), ISessionContext { public UpgradeOptions? UpgradeOptions { get; init; } @@ -276,29 +279,20 @@ public Task DisconnectAsync() } } -public class TransportConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : ContextBase(localPeer, session, protocol), ITransportConnectionContext +public class NewConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), INewConnectionContext { public CancellationToken Token => session.ConnectionToken; - public IConnectionSessionContext CreateSession() - { - return localPeer.CreateSession(session); - } - public void Dispose() { - localPeer.DisposeConnection(this, session); + } } -public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : ContextBase(localPeer, session, protocol), IConnectionContext +public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), IConnectionContext { public UpgradeOptions? UpgradeOptions { get; init; } - public IConnectionSessionContext CreateSession() - { - return localPeer.CreateSession(session); - } public Task DisconnectAsync() { @@ -306,14 +300,15 @@ public Task DisconnectAsync() } } -public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol) : IChannelFactory +public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : IChannelFactory { + protected bool isListener = isListener; public IPeer Peer => localPeer; - public Remote Remote => session.Remote; + public State State => session.State; public IEnumerable SubProtocols => localPeer.GetProtocolsFor(protocol); - public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + public string Id { get; } = session.Id; protected LocalPeer localPeer = localPeer; protected LocalPeer.Session session = session; @@ -338,4 +333,18 @@ public Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeO { return localPeer.Upgrade(localPeer, session, parentChannel, protocol, specificProtocol, upgradeOptions); } + + public INewConnectionContext CreateConnection() + { + return localPeer.CreateConnection(protocol, session.IsListener); + } + public INewSessionContext UpgradeToSession() + { + return localPeer.CreateSession(session, protocol, isListener); + } + + public void ListenerReady(Multiaddress addr) + { + localPeer.ListenerReady(this, addr); + } } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index d17f07b8..4bb8d096 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -3,6 +3,8 @@ using Microsoft.Extensions.DependencyInjection; using Nethermind.Libp2p.Stack; +using Org.BouncyCastle.Tls; +using System; namespace Nethermind.Libp2p.Core; @@ -43,168 +45,47 @@ public abstract class PeerFactoryBuilderBase : IPeerFact where TBuilder : PeerFactoryBuilderBase, IPeerFactoryBuilder where TPeerFactory : IPeerFactory { - private readonly List _appLayerProtocols = new(); - public IEnumerable AppLayerProtocols { get => _appLayerProtocols; } + private readonly List _appLayerProtocols = new(); + public IEnumerable AppLayerProtocols => _appLayerProtocols.Select(x => x.Protocol); internal readonly IServiceProvider ServiceProvider; - protected readonly ProtocolStack? _stack; - protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) { ServiceProvider = serviceProvider ?? new ServiceCollection().BuildServiceProvider(); } - protected ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol - { - return new ProtocolStack(this, ServiceProvider, new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider, instance))); - } - public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol { - _appLayerProtocols.Add(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider!, instance)); + _appLayerProtocols.Add(new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider!, instance))); return (TBuilder)this; } - protected class ProtocolStack - { - private readonly IPeerFactoryBuilder builder; - private readonly IServiceProvider serviceProvider; - - public ProtocolStack? Root { get; private set; } - public ProtocolStack Parent { get; private set; } - public ProtocolStack? PrevSwitch { get; private set; } - public ProtocolRef Protocol { get; } - public HashSet TopProtocols { get; } = new(); - - public ProtocolStack(IPeerFactoryBuilder builder, IServiceProvider serviceProvider, ProtocolRef protocol) - { - this.builder = builder; - this.serviceProvider = serviceProvider; - Protocol = protocol; - } + protected abstract ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols); - public ProtocolStack AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol - { - builder.AddAppLayerProtocol(instance); - return this; - } - - public ProtocolStack Over(TProtocol? instance = default) where TProtocol : IProtocol - { - ProtocolStack nextNode = new(builder, serviceProvider, new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance))); - return Over(nextNode); - } + private Dictionary protocols = []; - public ProtocolStack Or(TProtocol? instance = default) where TProtocol : IProtocol - { - if (Parent is null) - { - Parent = new ProtocolStack(builder, serviceProvider, new ProtocolRef(RootStub.Instance)); - Parent.Over(this); - } - ProtocolRef protocol = new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(serviceProvider!, instance)); - ProtocolStack stack = new(builder, serviceProvider, protocol); - return Or(stack); - } - - public ProtocolStack Over(ProtocolStack stack) - { - ProtocolStack rootProto = stack.Root ?? stack; - TopProtocols.Add(rootProto); - - if (PrevSwitch != null) - { - PrevSwitch.Over(stack); - } - - rootProto.Root = stack.Root = Root ?? this; - rootProto.Parent = this; - - return stack; - } - - public ProtocolStack Or(ProtocolStack stack) - { - if (Parent is null) - { - Parent = new ProtocolStack(builder, serviceProvider, new ProtocolRef(RootStub.Instance)); - Parent.Over(this); - } - stack.PrevSwitch = this; - return Parent.Over(stack); - } - - public override string ToString() + protected void Connect(ProtocolRef[] protocols, ProtocolRef[] upgradeTo) + { + foreach (ProtocolRef protocolRef in protocols) { - return $"{Protocol}({TopProtocols.Count}): {string.Join(" or ", TopProtocols.Select(p => p.Protocol))}"; + this.protocols.TryAdd(protocolRef, upgradeTo); } } - protected abstract ProtocolStack BuildStack(); + protected ProtocolRef Get() where TProtocol : IProtocol + { + return new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider)); + } public IPeerFactory Build() { - ProtocolStack transportLayer = BuildStack(); - ProtocolStack? appLayer = default; - - foreach (IProtocol appLayerProtocol in _appLayerProtocols) - { - appLayer = appLayer is null ? transportLayer.Over(appLayerProtocol) : appLayer.Or(appLayerProtocol); - } - - ProtocolStack? root = transportLayer.Root; - - if (root?.Protocol is null) - { - throw new ApplicationException("Root protocol is not properly defined"); - } - - Dictionary protocols = new(); - - void SetupChannelFactories(ProtocolStack root) - { - protocols.TryAdd(root.Protocol, root.TopProtocols.Select(p => p.Protocol).ToArray()); - //root.UpChannelsFactory.Setup(new Dictionary(root.TopProtocols - // .Select(p => new KeyValuePair(p.Protocol, p.UpChannelsFactory)))); - foreach (ProtocolStack topProto in root.TopProtocols) - { - if (!root.TopProtocols.Any()) - { - return; - } - SetupChannelFactories(topProto); - } - } - - SetupChannelFactories(root); - IProtocolStackSettings protocolStackSettings = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); + protocolStackSettings.TopProtocols = BuildStack(_appLayerProtocols.ToArray()); protocolStackSettings.Protocols = protocols; - protocolStackSettings.TopProtocols = root.Protocol.Protocol is RootStub ? root.TopProtocols.Select(s => s.Protocol).ToArray() : [root?.Protocol]; TPeerFactory result = ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider); return result; } - - private class Layer - { - public List Protocols { get; } = new(); - public bool IsSelector { get; set; } - - public override string ToString() - { - return (IsSelector ? "(selector)" : "") + string.Join(",", Protocols.Select(p => p.Id)); - } - } - - class RootStub : IProtocol - { - public static RootStub Instance { get; } = new(); - - private RootStub() { } - - public string Id => "protocol hierachy root"; - } } diff --git a/src/libp2p/Libp2p.Core/TransportContext.cs b/src/libp2p/Libp2p.Core/TransportContext.cs index aaf33c4b..bfeb93d0 100644 --- a/src/libp2p/Libp2p.Core/TransportContext.cs +++ b/src/libp2p/Libp2p.Core/TransportContext.cs @@ -17,7 +17,7 @@ public void ListenerReady(Multiaddress addr) peer.ListenerReady(this, addr); } - public ITransportConnectionContext CreateConnection() + public INewConnectionContext CreateConnection() { return peer.CreateConnection(proto, isListener); } diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 6b4bb819..0300cadf 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -38,9 +38,9 @@ public async Task DialAsync(IChannel channel, ISessionContext context) Identify.Dto.Identify identity = await channel.ReadPrefixedProtobufAsync(Identify.Dto.Identify.Parser); _logger?.LogInformation("Received peer info: {identify}", identity); - context.Remote.Identity = new Identity(PublicKey.Parser.ParseFrom(identity.PublicKey)); + context.State.RemotePublicKey = PublicKey.Parser.ParseFrom(identity.PublicKey); - if (context.Remote.Identity.PublicKey.ToByteString() != identity.PublicKey) + if (context.State.RemotePublicKey.ToByteString() != identity.PublicKey) { throw new PeerConnectionException(); } @@ -56,7 +56,7 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) AgentVersion = _agentVersion, PublicKey = context.Peer.Identity.PublicKey.ToByteString(), ListenAddrs = { ByteString.CopyFrom(context.Peer.Address.Get().ToBytes()) }, - ObservedAddr = ByteString.CopyFrom(context.Remote.Address!.Get().ToBytes()), + ObservedAddr = ByteString.CopyFrom(context.State.RemoteAddress!.Get().ToBytes()), Protocols = { _peerFactoryBuilder.AppLayerProtocols.Select(p => p.Id) } }; byte[] ar = new byte[identify.CalculateSize()]; diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index 755edb77..3d93bb2c 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -44,7 +44,7 @@ await Task.Run(async () => { Socket client = await listener.AcceptAsync(); - ITransportConnectionContext connectionCtx = context.CreateConnection(); + INewConnectionContext connectionCtx = context.CreateConnection(); connectionCtx.Token.Register(client.Close); IChannel upChannel = connectionCtx.Upgrade(); @@ -91,7 +91,7 @@ await Task.Run(async () => } catch (SocketException) { - _logger?.LogInformation($"Disconnected({context.Id}) due to a socket exception"); + _logger?.LogInformation($"Disconnected due to a socket exception"); await upChannel.CloseAsync(); } }); @@ -101,7 +101,7 @@ await Task.Run(async () => }); } - public async Task DialAsync(ITransportConnectionContext context, Multiaddress remoteAddr, CancellationToken token) + public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAddr, CancellationToken token) { Socket client = new(SocketType.Stream, ProtocolType.Tcp); diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index 29b77548..2931b696 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -61,9 +61,9 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) //var key = new byte[] { 0x1 }.Concat(clientStatic.PublicKey).ToArray(); PeerId remotePeerId = new(msg1KeyDecoded); - if (!context.Remote.Address.Has()) + if (!context.State.RemoteAddress.Has()) { - context.Remote.Address.Add(new P2P(remotePeerId.ToString())); + context.State.RemoteAddress.Add(new P2P(remotePeerId.ToString())); } byte[] msg = [.. Encoding.UTF8.GetBytes(PayloadSigPrefix), .. ByteString.CopyFrom(clientStatic.PublicKey)]; @@ -90,7 +90,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) await downChannel.WriteAsync(new ReadOnlySequence(buffer, 0, msg2.BytesWritten)); Transport? transport = msg2.Transport; - _logger?.LogDebug("Established connection to {peer}", context.Remote.Address); + _logger?.LogDebug("Established connection to {peer}", context.State.RemoteAddress); IChannel upChannel = context.Upgrade(); @@ -143,12 +143,12 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) PeerId remotePeerId = new(msg2KeyDecoded); - if (!context.Remote.Address.Has()) + if (!(context.State.RemoteAddress?.Has() ?? false)) { - context.Remote.Address.Add(new P2P(remotePeerId.ToString())); + context.State.RemoteAddress!.Add(new P2P(remotePeerId.ToString())); } - _logger?.LogDebug("Established connection to {peer}", context.Remote.Address); + _logger?.LogDebug("Established connection to {peer}", context.State.RemoteAddress); IChannel upChannel = context.Upgrade(); diff --git a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs index 7f47e8aa..b934778a 100644 --- a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs @@ -30,31 +30,31 @@ public async Task DialAsync(IChannel channel, ISessionContext context) _random.NextBytes(ping.AsSpan(0, PayloadLength)); ReadOnlySequence bytes = new(ping); - _logger?.LogPing(context.Remote.Address); + _logger?.LogPing(context.State.RemoteAddress); await channel.WriteAsync(bytes); _logger?.LogTrace("Sent ping: {ping}", Convert.ToHexString(ping)); - _logger?.ReadingPong(context.Remote.Address); + _logger?.ReadingPong(context.State.RemoteAddress); ReadOnlySequence response = await channel.ReadAsync(PayloadLength, ReadBlockingMode.WaitAll).OrThrow(); _logger?.LogTrace("Received pong: {ping}", Convert.ToHexString(ping)); - _logger?.VerifyingPong(context.Remote.Address); + _logger?.VerifyingPong(context.State.RemoteAddress); if (!ping[0..PayloadLength].SequenceEqual(response.ToArray())) { - _logger?.PingFailed(context.Remote.Address); + _logger?.PingFailed(context.State.RemoteAddress); throw new ApplicationException(); } - _logger?.LogPinged(context.Remote.Address); + _logger?.LogPinged(context.State.RemoteAddress); } public async Task ListenAsync(IChannel channel, ISessionContext context) { - _logger?.PingListenStarted(context.Remote.Address); + _logger?.PingListenStarted(context.State.RemoteAddress); while (true) { - _logger?.ReadingPing(context.Remote.Address); + _logger?.ReadingPing(context.State.RemoteAddress); ReadResult read = await channel.ReadAsync(PayloadLength, ReadBlockingMode.WaitAny); if (read.Result != IOResult.Ok) { @@ -64,11 +64,11 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) byte[] ping = read.Data.ToArray(); _logger?.LogTrace("Received ping: {ping}", Convert.ToHexString(ping)); - _logger?.ReturningPong(context.Remote.Address); + _logger?.ReturningPong(context.State.RemoteAddress); await channel.WriteAsync(new ReadOnlySequence(ping)); _logger?.LogTrace("Sent pong: {ping}", Convert.ToHexString(ping)); } - _logger?.PingFinished(context.Remote.Address); + _logger?.PingFinished(context.State.RemoteAddress); } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 7a4f2f0e..e1de2705 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -27,11 +27,11 @@ public PubsubProtocol(string protocolId, PubsubRouter router, ILoggerFactory? lo public async Task DialAsync(IChannel channel, ISessionContext context) { - string peerId = context.Remote.Address.Get().ToString()!; - _logger?.LogDebug($"Dialed({context.Id}) {context.Remote.Address}"); + string peerId = context.State.RemoteAddress.Get().ToString()!; + _logger?.LogDebug($"Dialed({context.Id}) {context.State.RemoteAddress}"); TaskCompletionSource dialTcs = new(); - CancellationToken token = router.OutboundConnection(context.Remote.Address, Id, dialTcs.Task, (rpc) => + CancellationToken token = router.OutboundConnection(context.State.RemoteAddress, Id, dialTcs.Task, (rpc) => { var t = channel.WriteSizeAndProtobufAsync(rpc); _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); @@ -47,20 +47,20 @@ public async Task DialAsync(IChannel channel, ISessionContext context) await channel; dialTcs.SetResult(); - _logger?.LogDebug($"Finished dial({context.Id}) {context.Remote.Address}"); + _logger?.LogDebug($"Finished dial({context.Id}) {context.State.RemoteAddress}"); } public async Task ListenAsync(IChannel channel, ISessionContext context) { - string peerId = context.Remote.Address.Get().ToString()!; - _logger?.LogDebug($"Listen({context.Id}) to {context.Remote.Address}"); + string peerId = context.State.RemoteAddress.Get().ToString()!; + _logger?.LogDebug($"Listen({context.Id}) to {context.State.RemoteAddress}"); TaskCompletionSource listTcs = new(); TaskCompletionSource dialTcs = new(); - CancellationToken token = router.InboundConnection(context.Remote.Address, Id, listTcs.Task, dialTcs.Task, () => + CancellationToken token = router.InboundConnection(context.State.RemoteAddress, Id, listTcs.Task, dialTcs.Task, () => { _ = context.DialAsync([this]); return dialTcs.Task; @@ -81,7 +81,7 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) } } listTcs.SetResult(); - _logger?.LogDebug($"Finished({context.Id}) list {context.Remote.Address}"); + _logger?.LogDebug($"Finished({context.Id}) list {context.State.RemoteAddress}"); } public override string ToString() diff --git a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs index 763c73f7..9d460c66 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs @@ -92,7 +92,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, try { QuicConnection connection = await listener.AcceptConnectionAsync(); - ITransportConnectionContext clientContext = context.CreateConnection(); + INewConnectionContext clientContext = context.CreateConnection(); _ = ProcessStreams(clientContext, connection, token).ContinueWith(t => clientContext.Dispose()); } @@ -104,7 +104,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, } } - public async Task DialAsync(ITransportConnectionContext context, Multiaddress remoteAddr, CancellationToken token) + public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAddr, CancellationToken token) { if (!QuicConnection.IsSupported) { @@ -147,11 +147,11 @@ public async Task DialAsync(ITransportConnectionContext context, Multiaddress re private static bool VerifyRemoteCertificate(Multiaddress remoteAddr, X509Certificate certificate) => CertificateHelper.ValidateCertificate(certificate as X509Certificate2, remoteAddr.Get().ToString()); - private async Task ProcessStreams(ITransportConnectionContext context, QuicConnection connection, CancellationToken token = default) + private async Task ProcessStreams(INewConnectionContext context, QuicConnection connection, CancellationToken token = default) { _logger?.LogDebug("New connection to {remote}", connection.RemoteEndPoint); - using IConnectionSessionContext session = context.CreateSession(); + using INewSessionContext session = context.UpgradeToSession(); _ = Task.Run(async () => { diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index e945bf7c..9e2d0297 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -37,7 +37,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext try { int streamIdCounter = isListener ? 2 : 1; - IConnectionSessionContext session = context.CreateSession(); + INewSessionContext session = context.UpgradeToSession(); int pingCounter = 0; using Timer timer = new((s) => diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index 38b84aa7..b2855adc 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -9,10 +9,11 @@ namespace Nethermind.Libp2p.Stack; [RequiresPreviewFeatures] -public class Libp2pPeerFactoryBuilder : PeerFactoryBuilderBase, +public class Libp2pPeerFactoryBuilder(IServiceProvider? serviceProvider = default) : PeerFactoryBuilderBase(serviceProvider), ILibp2pPeerFactoryBuilder { private bool enforcePlaintext; + private bool addPubsub; public ILibp2pPeerFactoryBuilder WithPlaintextEnforced() { @@ -20,32 +21,56 @@ public ILibp2pPeerFactoryBuilder WithPlaintextEnforced() return this; } - public Libp2pPeerFactoryBuilder(IServiceProvider? serviceProvider = default) : base(serviceProvider) + public ILibp2pPeerFactoryBuilder WithPubsub() { + addPubsub = true; + return this; } - protected override ProtocolStack BuildStack() + protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { - ProtocolStack tcpEncryptionStack = enforcePlaintext ? - Over() : - Over(); - - ProtocolStack tcpStack = - Over() - .Over() - .Over(tcpEncryptionStack) - .Over() - .Over(); - - ProtocolStack relayStack = Over().Over(); - - return - Over().Or(tcpStack).Or(relayStack) - .Over() - .AddAppLayerProtocol() - //.AddAppLayerProtocol() - //.AddAppLayerProtocol() - .AddAppLayerProtocol() - .AddAppLayerProtocol(); + ProtocolRef[] transports = [ + Get(), + Get() + ]; + + ProtocolRef[] selector1 = [Get()]; + Connect(transports, selector1); + + ProtocolRef[] encryption = [enforcePlaintext ? + Get() : + Get()]; + Connect(selector1, encryption); + + ProtocolRef[] selector2 = [Get()]; + Connect(encryption, selector2); + + ProtocolRef[] muxers = [Get()]; + Connect(selector2, muxers); + + ProtocolRef[] selector3 = [Get()]; + Connect(muxers, selector3); + + ProtocolRef relay = Get(); + ProtocolRef[] pubsub = addPubsub ? [ + Get(), + Get(), + Get(), + Get() + ] : []; + + ProtocolRef[] apps = [ + Get(), + .. additionalProtocols, + relay, + .. pubsub, + ]; + Connect(selector3, apps); + + ProtocolRef[] relaySelector = [Get()]; + Connect([relay], relaySelector); + Connect(relaySelector, apps.Where(a => a != relay).ToArray()); + + return transports; } } diff --git a/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs b/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs index 45d94a9f..dee72361 100644 --- a/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs +++ b/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs @@ -16,8 +16,8 @@ public NoStackPeerFactoryBuilder() : base(default) public static Libp2pPeerFactoryBuilder Create => new(); - protected override ProtocolStack BuildStack() + protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { - return Over(); + return [Get()]; } } diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index 6f19c802..3febb216 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -129,36 +129,46 @@ public TestPlansPeerFactoryBuilder(string transport, string? muxer, string? secu private static readonly string[] stacklessProtocols = ["quic", "quic-v1", "webtransport"]; - protected override ProtocolStack BuildStack() + protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { - ProtocolStack stack = transport switch + ProtocolRef[] transportStack = [transport switch { - "tcp" => Over(), + "tcp" => Get(), // TODO: Improve QUIC imnteroperability - "quic-v1" => Over(), + "quic-v1" => Get(), _ => throw new NotImplementedException(), - }; + }]; - stack = stack.Over(); + ProtocolRef[] selector = [Get()]; + Connect(transportStack, selector); if (!stacklessProtocols.Contains(transport)) { - stack = security switch + ProtocolRef[] securityStack = [security switch { - "noise" => stack.Over(), + "noise" => Get(), _ => throw new NotImplementedException(), - }; - stack = stack.Over(); - stack = muxer switch + }]; + Connect(selector, transportStack); + + selector = [Get()]; + Connect(transportStack, selector); + + ProtocolRef[] muxerStack = [muxer switch { - "yamux" => stack.Over(), + "yamux" => Get(), _ => throw new NotImplementedException(), - }; - stack = stack.Over(); + }]; + Connect(selector, muxerStack); + + selector = [Get()]; + Connect(muxerStack, selector); } - return stack.AddAppLayerProtocol() - .AddAppLayerProtocol(); + ProtocolRef[] apps = [Get(), Get()]; + Connect(selector, apps); + + return transportStack; } public string MakeAddress(string ip = "0.0.0.0", string port = "0") => transport switch From 7b05f29de900264cb944773ee280c58c907ff997 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 19 Aug 2024 17:07:08 +0300 Subject: [PATCH 07/25] Before pubsub --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 15 +- .../TestDiscoveryProtocol.cs | 2 +- .../Libp2p.Core.TestsBase/TestLocalPeer.cs | 10 +- src/libp2p/Libp2p.Core/ChannelRequest.cs | 18 +- src/libp2p/Libp2p.Core/ConnectionContext.cs | 2 +- .../Discovery/IDiscoveryProtocol.cs | 2 +- .../Libp2p.Core/Exceptions/Libp2pException.cs | 5 +- src/libp2p/Libp2p.Core/IChannelFactory.cs | 5 +- src/libp2p/Libp2p.Core/IPeer.cs | 5 +- src/libp2p/Libp2p.Core/IPeerContext.cs | 4 +- src/libp2p/Libp2p.Core/IPeerFactoryBuilder.cs | 2 +- src/libp2p/Libp2p.Core/IProtocol.cs | 2 +- src/libp2p/Libp2p.Core/Peer.cs | 259 +++++++++++++----- src/libp2p/Libp2p.Core/PeerFactory.cs | 5 +- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 32 ++- src/libp2p/Libp2p.Core/TransportContext.cs | 13 +- .../IPAddressExtensions.cs | 62 +++++ .../IdentifyProtocol.cs | 22 +- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 70 +++-- .../MDnsDiscoveryProtocol.cs | 36 +-- .../MultistreamProtocol.cs | 5 +- .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 2 +- .../PlainTextProtocol.cs | 18 +- .../FloodsubProtocolTests.cs | 2 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 2 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 5 +- .../Libp2p.Protocols.Quic/QuicProtocol.cs | 19 +- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 25 +- src/libp2p/Libp2p/Libp2pPeerFactory.cs | 7 +- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 44 +-- ...derContext.cs => ProtocolStackSettings.cs} | 2 +- .../Libp2p/ServiceProviderExtensions.cs | 2 +- src/samples/chat/Program.cs | 5 +- .../chat/Properties/launchSettings.json | 12 + src/samples/pubsub-chat/Program.cs | 3 +- src/samples/transport-interop/Program.cs | 13 +- 36 files changed, 503 insertions(+), 234 deletions(-) create mode 100644 src/libp2p/Libp2p.Protocols.Identify/IPAddressExtensions.cs rename src/libp2p/Libp2p/{Libp2pBuilderContext.cs => ProtocolStackSettings.cs} (82%) create mode 100644 src/samples/chat/Properties/launchSettings.json diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index aabbdb25..cd894c32 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -32,8 +32,8 @@ public async Task E2e() TopProtocols = [tProto] }; - LocalPeer peer1 = new LocalPeer(protocolStackSettings, new Identity()); - LocalPeer peer2 = new LocalPeer(protocolStackSettings, new Identity()); + LocalPeer peer1 = new LocalPeer(new Identity(), protocolStackSettings); + LocalPeer peer2 = new LocalPeer(new Identity(), protocolStackSettings); await peer1.StartListenAsync([new Multiaddress()]); await peer2.StartListenAsync([new Multiaddress()]); @@ -51,7 +51,7 @@ public async Task E2e() //IConnectionContext cContext = peer.CreateContext(cProto); //cContext.SubDial(); - //ISession connectionSessionContext = cContext.CreateSession(); + //ISession connectionSessionContext = cContext.UpgradeToSession(); //ISessionContext sContext = peer.CreateContext(sProto); @@ -79,7 +79,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr { context.ListenerReady(Multiaddress.Decode("/ip4/127.0.0.1/tcp/4096")); using INewConnectionContext connectionCtx = context.CreateConnection(); - + connectionCtx.State.RemoteAddress = Multiaddress.Decode("/ip4/127.0.0.1/tcp/1000"); IChannel topChan = connectionCtx.Upgrade(); connectionCtx.Token.Register(() => topChan.CloseAsync()); @@ -109,10 +109,11 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr } } - public async Task DialAsync(INewConnectionContext context, Multiaddress listenAddr, CancellationToken token) + public async Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) { - IChannel topChan = context.Upgrade(); - context.Token.Register(() => topChan.CloseAsync()); + INewConnectionContext connectionContext = context.CreateConnection(); + IChannel topChan = connectionContext.Upgrade(); + connectionContext.Token.Register(() => topChan.CloseAsync()); ReadResult received; diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs index 197bd3b3..fb0b956c 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestDiscoveryProtocol.cs @@ -11,7 +11,7 @@ public class TestDiscoveryProtocol : IDiscoveryProtocol public Func? OnAddPeer { get; set; } public Func? OnRemovePeer { get; set; } - public Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) + public Task DiscoverAsync(IPeer peer, CancellationToken token = default) { TaskCompletionSource task = new(); token.Register(task.SetResult); diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs index e5eb3233..30a592fe 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs @@ -19,17 +19,17 @@ public TestLocalPeer() public ObservableCollection ListenAddresses => throw new NotImplementedException(); - public event OnConnection OnConnection; + public event OnConnection? OnConnection; public Task DialAsync(Multiaddress addr, CancellationToken token = default) { return Task.FromResult(new TestRemotePeer(addr)); } - //public Task ListenAsync(Multiaddress addr, CancellationToken token = default) - //{ - // return Task.FromResult(null); - //} + public Task DisconnectAsync() + { + return Task.CompletedTask; + } public Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) { diff --git a/src/libp2p/Libp2p.Core/ChannelRequest.cs b/src/libp2p/Libp2p.Core/ChannelRequest.cs index 97ca93b8..ea3d3362 100644 --- a/src/libp2p/Libp2p.Core/ChannelRequest.cs +++ b/src/libp2p/Libp2p.Core/ChannelRequest.cs @@ -3,13 +3,13 @@ namespace Nethermind.Libp2p.Core; -public class ChannelRequest -{ - public IProtocol? SubProtocol { get; init; } - public TaskCompletionSource? CompletionSource { get; init; } +//public class ChannelRequest +//{ +// public IProtocol? SubProtocol { get; init; } +// public TaskCompletionSource? CompletionSource { get; init; } - public override string ToString() - { - return $"Request for {SubProtocol?.Id ?? "unknown protocol"}"; - } -} +// public override string ToString() +// { +// return $"Request for {SubProtocol?.Id ?? "unknown protocol"}"; +// } +//} diff --git a/src/libp2p/Libp2p.Core/ConnectionContext.cs b/src/libp2p/Libp2p.Core/ConnectionContext.cs index 26385c63..dcf0bb4b 100644 --- a/src/libp2p/Libp2p.Core/ConnectionContext.cs +++ b/src/libp2p/Libp2p.Core/ConnectionContext.cs @@ -20,7 +20,7 @@ namespace Nethermind.Libp2p.Core; // public Identity Identity => throw new NotImplementedException(); -// public ISessionContext CreateSession() +// public ISessionContext UpgradeToSession() // { // throw new NotImplementedException(); // } diff --git a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs index e883d11a..1a6b52ea 100644 --- a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs @@ -7,7 +7,7 @@ namespace Nethermind.Libp2p.Core.Discovery; public interface IDiscoveryProtocol { - Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default); + Task DiscoverAsync(IPeer peer, CancellationToken token = default); Func? OnAddPeer { set; } Func? OnRemovePeer { set; } } diff --git a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs index c3d1637f..d225cd12 100644 --- a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs +++ b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs @@ -17,7 +17,10 @@ public Libp2pException() : base() public class ChannelClosedException : Libp2pException { - + public ChannelClosedException() + { + + } } public class Libp2pSetupException(string? message = null) : Libp2pException(message) diff --git a/src/libp2p/Libp2p.Core/IChannelFactory.cs b/src/libp2p/Libp2p.Core/IChannelFactory.cs index 3d34ea51..a83d6acb 100644 --- a/src/libp2p/Libp2p.Core/IChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/IChannelFactory.cs @@ -14,10 +14,11 @@ public interface IChannelFactory Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeOptions? options = null); } -public class UpgradeOptions +public record UpgradeOptions { public IProtocol? SelectedProtocol { get; init; } - public UpgradeModeOverride ModeOverride { get; init; } = UpgradeModeOverride.None; + public UpgradeModeOverride ModeOverride { get; init; } + public TaskCompletionSource? CompletionSource { get; init; } } public enum UpgradeModeOverride diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index c9215581..7376c9df 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -9,17 +9,16 @@ namespace Nethermind.Libp2p.Core; public interface IPeer { Identity Identity { get; } - Multiaddress Address { get; } Task DialAsync(Multiaddress addr, CancellationToken token = default); Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default); - //Task DisconnectAsync(); + Task DisconnectAsync(); ObservableCollection ListenAddresses { get; } - event OnConnection OnConnection; + event OnConnection? OnConnection; } public delegate Task OnConnection(ISession newSession); diff --git a/src/libp2p/Libp2p.Core/IPeerContext.cs b/src/libp2p/Libp2p.Core/IPeerContext.cs index 6b36b66e..6612cb20 100644 --- a/src/libp2p/Libp2p.Core/IPeerContext.cs +++ b/src/libp2p/Libp2p.Core/IPeerContext.cs @@ -29,7 +29,7 @@ public interface IConnectionContext : ITransportContext, IChannelFactory, IConte public interface ISessionContext : IConnectionContext { Task DialAsync() where TProtocol : ISessionProtocol; - Task DialAsync(ISessionProtocol[] protocols); + Task DialAsync(ISessionProtocol protocol); } @@ -42,7 +42,7 @@ public interface INewConnectionContext : IDisposable, IChannelFactory, IContextS public interface INewSessionContext : IDisposable, INewConnectionContext { - IEnumerable DialRequests { get; } + IEnumerable DialRequests { get; } } public class State diff --git a/src/libp2p/Libp2p.Core/IPeerFactoryBuilder.cs b/src/libp2p/Libp2p.Core/IPeerFactoryBuilder.cs index e04af45b..2be44d06 100644 --- a/src/libp2p/Libp2p.Core/IPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p.Core/IPeerFactoryBuilder.cs @@ -5,7 +5,7 @@ namespace Nethermind.Libp2p.Core; public interface IPeerFactoryBuilder { - IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol; + IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default, bool isExposed = true) where TProtocol : IProtocol; IPeerFactory Build(); IEnumerable AppLayerProtocols { get; } } diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index 8b8f35f9..8d562b20 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -31,7 +31,7 @@ public interface ITransportProtocol : IProtocol /// Factory that spawns new channels used to interact with top layer protocols /// Holds information about local and remote peers /// - Task DialAsync(INewConnectionContext context, Multiaddress listenAddr, CancellationToken token); + Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token); } public interface IConnectionProtocol : IProtocol diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index d9f7f2df..929574a0 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.Logging; using Multiformats.Address; +using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Stack; using System.Collections.Concurrent; @@ -9,58 +11,63 @@ namespace Nethermind.Libp2p.Core; -public class LocalPeer(IProtocolStackSettings protocolStackSettings, Identity identity) : IPeer +public class LocalPeer(Identity identity, IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : IPeer { + private readonly ILogger? _logger = loggerFactory?.CreateLogger(); protected IProtocolStackSettings protocolStackSettings = protocolStackSettings; public Identity Identity { get; } = identity; - public ObservableCollection ListenAddresses { get; } = new(); - public ObservableCollection Sessions { get; } = new(); - public Multiaddress Address => throw new NotImplementedException(); + public ObservableCollection ListenAddresses { get; } = []; + + private ObservableCollection sessions { get; } = []; protected virtual Task ConnectedTo(ISession peer, bool isDialer) => Task.CompletedTask; - public class Session(LocalPeer localPeer, bool isListener) : ISession + public class Session(LocalPeer peer) : ISession { public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); public State State { get; } = new(); - - public bool IsListener => isListener; public Multiaddress RemoteAddress => State.RemoteAddress ?? throw new Libp2pException("Session contains uninitialized remote address."); - public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol + private readonly BlockingCollection SubDialRequests = []; + + public async Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { + await Connected; TaskCompletionSource tcs = new(); - SubDialRequests.Add(new ChannelRequest() { CompletionSource = tcs, SubProtocol = localPeer.GetProtocolInstance() }); - return tcs.Task; + SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance() }, token); + await tcs.Task; } - public Task DialAsync(ISessionProtocol[] protocols, CancellationToken token = default) + public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = default) { + await Connected; TaskCompletionSource tcs = new(); - SubDialRequests.Add(new ChannelRequest() { CompletionSource = tcs, SubProtocol = protocols[0] }); - return tcs.Task; + SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = protocol }, token); + await tcs.Task; } - private readonly BlockingCollection SubDialRequests = []; - private CancellationTokenSource connectionTokenSource = new(); + public Task DisconnectAsync() { connectionTokenSource.Cancel(); + peer.sessions.Remove(this); return Task.CompletedTask; } public CancellationToken ConnectionToken => connectionTokenSource.Token; - private TaskCompletionSource ConnectedTcs = new(); - + public TaskCompletionSource ConnectedTcs = new(); public Task Connected => ConnectedTcs.Task; + internal void MarkAsConnected() => ConnectedTcs.SetResult(); + + + internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); - internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); } protected virtual ProtocolRef SelectProtocol(Multiaddress addr) @@ -79,15 +86,30 @@ protected virtual ProtocolRef SelectProtocol(Multiaddress addr) return protocolStackSettings.TopProtocols.Single(); } + protected virtual IEnumerable PrepareAddresses(Multiaddress[] addrs) + { + foreach (Multiaddress addr in addrs) + { + if (!addr.Has()) + { + yield return addr.Add(Identity.PeerId.ToString()); + } + else + { + yield return addr; + } + } + } + Dictionary> listenerReadyTcs = new(); - public event OnConnection OnConnection; + public event OnConnection? OnConnection; public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) { List listenTasks = new(addrs.Length); - foreach (Multiaddress addr in addrs) + foreach (Multiaddress addr in PrepareAddresses(addrs)) { ProtocolRef listenerProtocol = SelectProtocol(addr); @@ -100,7 +122,14 @@ public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationTok TaskCompletionSource tcs = new(); listenerReadyTcs[ctx] = tcs; - _ = transportProtocol.ListenAsync(ctx, addr, token).ContinueWith(t => ListenAddresses.Remove(tcs.Task.Result)); + _ = transportProtocol.ListenAsync(ctx, addr, token).ContinueWith(t => + { + if (t.IsFaulted) + { + tcs.SetException(t.Exception); + } + ListenAddresses.Remove(tcs.Task.Result); + }); listenTasks.Add(tcs.Task.WaitAsync(TimeSpan.FromMilliseconds(5000))); ListenAddresses.Add(tcs.Task.Result); @@ -117,29 +146,42 @@ public void ListenerReady(object sender, Multiaddress addr) } } - public INewConnectionContext CreateConnection(ProtocolRef proto, bool isListener) + public INewConnectionContext CreateConnection(ProtocolRef proto, Session? session, bool isListener) { - Session session = new(this, isListener); - return new NewConnectionContext(this, session, proto, isListener); + session ??= new(this); + session.MarkAsConnected(); + return new NewConnectionContext(this, session, proto, isListener, null); } - public INewSessionContext CreateSession(Session session, ProtocolRef proto, bool isListener) + public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, bool isListener) { if (session.State.RemoteAddress?.GetPeerId() is null) { throw new Libp2pSetupException($"{nameof(session.State.RemoteAddress)} should be initialiazed before session creation"); } - lock (Sessions) + lock (sessions) { - if (Sessions.Any(s => !ReferenceEquals(session, s) && s.State.RemoteAddress.GetPeerId() == session.State.RemoteAddress?.GetPeerId())) + if (sessions.Any(s => !ReferenceEquals(session, s) && s.State.RemoteAddress.GetPeerId() == session.State.RemoteAddress?.GetPeerId())) { throw new Libp2pException("Session is already established"); } - Sessions.Add(session); + sessions.Add(session); } - return new NewSessionContext(this, session, proto, isListener); + + Task initializeSession = ConnectedTo(session, !isListener); + initializeSession.ContinueWith(t => + { + if (t.IsFaulted) + { + session.DisconnectAsync(); + return; + } + OnConnection?.Invoke(session); + session.ConnectedTcs.SetResult(); + }); + return new NewSessionContext(this, session, proto, isListener, null); } internal IEnumerable GetProtocolsFor(ProtocolRef protocol) @@ -151,7 +193,7 @@ internal IEnumerable GetProtocolsFor(ProtocolRef protocol) if (!protocolStackSettings.Protocols.ContainsKey(protocol)) { - throw new Libp2pSetupException($"{protocol} is noty added"); + throw new Libp2pSetupException($"{protocol} is not added"); } return protocolStackSettings.Protocols[protocol].Select(p => p.Protocol); @@ -171,16 +213,25 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token throw new Libp2pSetupException($"{nameof(ITransportProtocol)} should be implemented by {dialerProtocol.GetType()}"); } - Session session = new(this, false); - INewConnectionContext ctx = new NewConnectionContext(this, session, dialerProtocol, session.IsListener); + Session session = new(this); + ITransportContext ctx = new DialerTransportContext(this, session, dialerProtocol); - _ = transportProtocol.DialAsync(ctx, addr, token); + Task dialingTask = transportProtocol.DialAsync(ctx, addr, token); - await session.Connected; + Task dialingResult = await Task.WhenAny(dialingTask, session.Connected); + + if (dialingResult == dialingTask) + { + if (dialingResult.IsFaulted) + { + throw dialingResult.Exception; + } + throw new Libp2pException("Not able to dial the peer"); + } return session; } - internal IChannel Upgrade(LocalPeer localPeer, Session session, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) + internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) { if (protocolStackSettings.Protocols is null) { @@ -189,67 +240,128 @@ internal IChannel Upgrade(LocalPeer localPeer, Session session, ProtocolRef prot if (!protocolStackSettings.Protocols.ContainsKey(protocol)) { - throw new Libp2pSetupException($"{protocol} is noty added"); + throw new Libp2pSetupException($"{protocol} is not added"); } - ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].First(); + ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].Single(); + + Channel res = new(); + + isListener = options?.ModeOverride switch { UpgradeModeOverride.Dial => false, UpgradeModeOverride.Listen => true, _ => isListener }; - Channel res = new Channel(); + Task upgradeTask; - bool isListener = session.IsListener && options?.ModeOverride != UpgradeModeOverride.Dial; + _logger?.LogInformation($"Upgrade {protocol} to {top}, listen={isListener}"); switch (top.Protocol) { case IConnectionProtocol tProto: { - var ctx = new ConnectionContext(this, session, top, isListener); - _ = isListener ? tProto.ListenAsync(res.Reverse, ctx) : tProto.DialAsync(res.Reverse, ctx); + var ctx = new ConnectionContext(this, session, top, isListener, options); + upgradeTask = isListener ? tProto.ListenAsync(res.Reverse, ctx) : tProto.DialAsync(res.Reverse, ctx); + break; } case ISessionProtocol sProto: { - var ctx = new SessionContext(this, session, top, isListener); - _ = isListener ? sProto.ListenAsync(res.Reverse, ctx) : sProto.DialAsync(res.Reverse, ctx); + var ctx = new SessionContext(this, session, top, isListener, options); + upgradeTask = isListener ? sProto.ListenAsync(res.Reverse, ctx) : sProto.DialAsync(res.Reverse, ctx); break; } + default: + throw new Libp2pSetupException($"Protocol {top.Protocol} does not implement proper protocol interface"); } + if (options?.SelectedProtocol == top.Protocol && options?.CompletionSource is not null) + { + _ = upgradeTask.ContinueWith(t => MapToTaskCompletionSource(t, options.CompletionSource)); + } + + upgradeTask.ContinueWith(t => + { + if (t.IsFaulted) + { + _logger?.LogError($"Upgrade task failed with {t.Exception}"); + } + }); + return res; } - internal async Task Upgrade(LocalPeer localPeer, Session session, IChannel parentChannel, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options) + private static void MapToTaskCompletionSource(Task t, TaskCompletionSource tcs) + { + if (t.IsCompletedSuccessfully) + { + tcs.SetResult(); + return; + } + if (t.IsCanceled) + { + tcs.SetCanceled(); + return; + } + tcs.SetException(t.Exception!); + } + + internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) { if (protocolStackSettings.Protocols is null) { throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].First(); - bool isListener = session.IsListener && options?.ModeOverride != UpgradeModeOverride.Dial; + if(upgradeProtocol is not null && !protocolStackSettings.Protocols[protocol].Any(p => p.Protocol == upgradeProtocol)) + { + protocolStackSettings.Protocols.Add(new ProtocolRef(upgradeProtocol, false), []); + } + + ProtocolRef top = upgradeProtocol is not null ? + protocolStackSettings.Protocols[protocol].FirstOrDefault(p => p.Protocol == upgradeProtocol, protocolStackSettings.Protocols.Keys.First(k => k.Protocol == upgradeProtocol)) : + protocolStackSettings.Protocols[protocol].Single(); + isListener = options?.ModeOverride switch { UpgradeModeOverride.Dial => false, UpgradeModeOverride.Listen => true, _ => isListener }; + + _logger?.LogInformation($"Upgrade and bind {protocol} to {top}, listen={isListener}"); + + Task upgradeTask; switch (top.Protocol) { case IConnectionProtocol tProto: { - var ctx = new ConnectionContext(this, session, top, isListener) { UpgradeOptions = options }; - await tProto.DialAsync(parentChannel, ctx); + var ctx = new ConnectionContext(this, session, top, isListener, options); + upgradeTask = isListener ? tProto.ListenAsync(parentChannel, ctx): tProto.DialAsync(parentChannel, ctx); break; } case ISessionProtocol sProto: { - var ctx = new SessionContext(this, session, top, isListener) { UpgradeOptions = options }; - await sProto.DialAsync(parentChannel, ctx); + var ctx = new SessionContext(this, session, top, isListener, options); + upgradeTask = isListener ? sProto.ListenAsync(parentChannel, ctx) : sProto.DialAsync(parentChannel, ctx); break; } + default: + throw new Libp2pSetupException($"Protocol {top.Protocol} does not implement proper protocol interface"); + } + + if (options?.SelectedProtocol == top.Protocol && options?.CompletionSource is not null) + { + _ = upgradeTask.ContinueWith(t => MapToTaskCompletionSource(t, options.CompletionSource)); } + + await upgradeTask.ContinueWith(t => + { + if (t.IsFaulted) + { + _logger?.LogError("failed"); + } + }); } + + public Task DisconnectAsync() => Task.WhenAll(sessions.Select(s => s.DisconnectAsync())); } -public class NewSessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), INewSessionContext +public class NewSessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), INewSessionContext { - public IEnumerable DialRequests => session.GetRequestQueue(); - - public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); + public IEnumerable DialRequests => session.GetRequestQueue(); public CancellationToken Token => session.ConnectionToken; @@ -259,18 +371,20 @@ public void Dispose() } } -public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), ISessionContext +public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), ISessionContext { - public UpgradeOptions? UpgradeOptions { get; init; } + public UpgradeOptions? UpgradeOptions => upgradeOptions; - public Task DialAsync() where TProtocol : ISessionProtocol + public async Task DialAsync() where TProtocol : ISessionProtocol { - return session.DialAsync(); + await session.Connected; + await session.DialAsync(); } - public Task DialAsync(ISessionProtocol[] protocols) + public async Task DialAsync(ISessionProtocol protocol) { - return session.DialAsync(protocols); + await session.Connected; + await session.DialAsync(protocol); } public Task DisconnectAsync() @@ -279,7 +393,7 @@ public Task DisconnectAsync() } } -public class NewConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), INewConnectionContext +public class NewConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), INewConnectionContext { public CancellationToken Token => session.ConnectionToken; @@ -289,18 +403,17 @@ public void Dispose() } } -public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : ContextBase(localPeer, session, protocol, isListener), IConnectionContext +public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), IConnectionContext { - public UpgradeOptions? UpgradeOptions { get; init; } - - + public UpgradeOptions? UpgradeOptions => upgradeOptions; + public Task DisconnectAsync() { return session.DisconnectAsync(); } } -public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener) : IChannelFactory +public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : IChannelFactory { protected bool isListener = isListener; public IPeer Peer => localPeer; @@ -313,34 +426,36 @@ public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, Protoco protected LocalPeer localPeer = localPeer; protected LocalPeer.Session session = session; protected ProtocolRef protocol = protocol; + protected UpgradeOptions? upgradeOptions = upgradeOptions; public IChannel Upgrade(UpgradeOptions? upgradeOptions = null) { - return localPeer.Upgrade(localPeer, session, protocol, null, upgradeOptions); + return localPeer.Upgrade(session, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); } public Task Upgrade(IChannel parentChannel, UpgradeOptions? upgradeOptions = null) { - return localPeer.Upgrade(localPeer, session, parentChannel, protocol, null, upgradeOptions); + return localPeer.Upgrade(session, parentChannel, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); } public IChannel Upgrade(IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) { - return localPeer.Upgrade(localPeer, session, protocol, specificProtocol, upgradeOptions); + return localPeer.Upgrade(session, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); } public Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) { - return localPeer.Upgrade(localPeer, session, parentChannel, protocol, specificProtocol, upgradeOptions); + return localPeer.Upgrade(session, parentChannel, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); } public INewConnectionContext CreateConnection() { - return localPeer.CreateConnection(protocol, session.IsListener); + return localPeer.CreateConnection(protocol, null, isListener); } + public INewSessionContext UpgradeToSession() { - return localPeer.CreateSession(session, protocol, isListener); + return localPeer.UpgradeToSession(session, protocol, isListener); } public void ListenerReady(Multiaddress addr) diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 3fa12d00..01285f92 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -1,16 +1,17 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; -public class PeerFactory(IProtocolStackSettings protocolStackSettings) : IPeerFactory +public class PeerFactory(IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : IPeerFactory { protected IProtocolStackSettings protocolStackSettings = protocolStackSettings; public virtual IPeer Create(Identity? identity = default) { - return new LocalPeer(protocolStackSettings, identity ?? new Identity()); + return new LocalPeer(identity ?? new Identity(), protocolStackSettings, loggerFactory); } } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 4bb8d096..7ae19970 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -1,16 +1,15 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.DependencyInjection; using Nethermind.Libp2p.Stack; -using Org.BouncyCastle.Tls; -using System; namespace Nethermind.Libp2p.Core; public static class PeerFactoryBuilderBase { - private static HashSet protocols = new(); + private static readonly HashSet protocols = []; internal static TProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol { @@ -29,11 +28,14 @@ internal static TProtocol CreateProtocolInstance(IServiceProvider ser } } -public class ProtocolRef(IProtocol protocol) +public class ProtocolRef(IProtocol protocol, bool isExposed = true) { static int IdCounter = 0; + private readonly bool l; + public string RefId { get; } = Interlocked.Increment(ref IdCounter).ToString(); public IProtocol Protocol => protocol; + public bool IsExposed => isExposed; public override string ToString() { @@ -55,9 +57,9 @@ protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) ServiceProvider = serviceProvider ?? new ServiceCollection().BuildServiceProvider(); } - public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default) where TProtocol : IProtocol + public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default, bool isExposed = true) where TProtocol : IProtocol { - _appLayerProtocols.Add(new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider!, instance))); + _appLayerProtocols.Add(new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider!, instance), isExposed)); return (TBuilder)this; } @@ -65,12 +67,24 @@ public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = private Dictionary protocols = []; - protected void Connect(ProtocolRef[] protocols, ProtocolRef[] upgradeTo) + protected ProtocolRef[] Connect(ProtocolRef[] protocols, params ProtocolRef[][] upgradeToStacks) { - foreach (ProtocolRef protocolRef in protocols) + ProtocolRef[] previous = protocols; + foreach (ProtocolRef[] upgradeTo in upgradeToStacks) { - this.protocols.TryAdd(protocolRef, upgradeTo); + foreach (ProtocolRef protocolRef in previous) + { + this.protocols[protocolRef] = upgradeTo; + + foreach (ProtocolRef upgradeToRef in upgradeTo) + { + this.protocols.TryAdd(upgradeToRef, []); + } + } + previous = upgradeTo; } + + return previous; } protected ProtocolRef Get() where TProtocol : IProtocol diff --git a/src/libp2p/Libp2p.Core/TransportContext.cs b/src/libp2p/Libp2p.Core/TransportContext.cs index bfeb93d0..667dd841 100644 --- a/src/libp2p/Libp2p.Core/TransportContext.cs +++ b/src/libp2p/Libp2p.Core/TransportContext.cs @@ -7,7 +7,6 @@ namespace Nethermind.Libp2p.Core; public class TransportContext(LocalPeer peer, ProtocolRef proto, bool isListener) : ITransportContext { - public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); public Identity Identity => peer.Identity; public IPeer Peer => peer; public bool IsListener => isListener; @@ -17,8 +16,16 @@ public void ListenerReady(Multiaddress addr) peer.ListenerReady(this, addr); } - public INewConnectionContext CreateConnection() + public virtual INewConnectionContext CreateConnection() { - return peer.CreateConnection(proto, isListener); + return peer.CreateConnection(proto, null, isListener); + } +} + +public class DialerTransportContext(LocalPeer peer, LocalPeer.Session session, ProtocolRef proto) : TransportContext(peer, proto, false) +{ + public override INewConnectionContext CreateConnection() + { + return peer.CreateConnection(proto, session, false); } } diff --git a/src/libp2p/Libp2p.Protocols.Identify/IPAddressExtensions.cs b/src/libp2p/Libp2p.Protocols.Identify/IPAddressExtensions.cs new file mode 100644 index 00000000..c72802b5 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Identify/IPAddressExtensions.cs @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using System.Net; +using System.Net.Sockets; + +namespace Nethermind.Libp2p.Protocols.Identify; + + +// Picked from https://gist.github.com/angularsen/f77b53ee9966fcd914025e25a2b3a085 creds: Andreas Gullberg Larsen +public static class IPAddressExtensions +{ + /// + /// Returns true if the IP address is in a private range.
+ /// IPv4: Loopback, link local ("169.254.x.x"), class A ("10.x.x.x"), class B ("172.16.x.x" to "172.31.x.x") and class C ("192.168.x.x").
+ /// IPv6: Loopback, link local, site local, unique local and private IPv4 mapped to IPv6.
+ ///
+ /// The IP address. + /// True if the IP address was in a private range. + /// bool isPrivate = IPAddress.Parse("127.0.0.1").IsPrivate(); + public static bool IsPrivate(this IPAddress ip) + { + // Map back to IPv4 if mapped to IPv6, for example "::ffff:1.2.3.4" to "1.2.3.4". + if (ip.IsIPv4MappedToIPv6) + ip = ip.MapToIPv4(); + + // Checks loopback ranges for both IPv4 and IPv6. + if (IPAddress.IsLoopback(ip)) return true; + + // IPv4 + if (ip.AddressFamily == AddressFamily.InterNetwork) + return IsPrivateIPv4(ip.GetAddressBytes()); + + // IPv6 + if (ip.AddressFamily == AddressFamily.InterNetworkV6) + { + return ip.IsIPv6LinkLocal || + ip.IsIPv6UniqueLocal || + ip.IsIPv6SiteLocal; + } + + throw new NotSupportedException( + $"IP address family {ip.AddressFamily} is not supported, expected only IPv4 (InterNetwork) or IPv6 (InterNetworkV6)."); + } + + private static bool IsPrivateIPv4(byte[] ipv4Bytes) + { + // Link local (no IP assigned by DHCP): 169.254.0.0 to 169.254.255.255 (169.254.0.0/16) + bool IsLinkLocal() => ipv4Bytes[0] == 169 && ipv4Bytes[1] == 254; + + // Class A private range: 10.0.0.0 – 10.255.255.255 (10.0.0.0/8) + bool IsClassA() => ipv4Bytes[0] == 10; + + // Class B private range: 172.16.0.0 – 172.31.255.255 (172.16.0.0/12) + bool IsClassB() => ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31; + + // Class C private range: 192.168.0.0 – 192.168.255.255 (192.168.0.0/16) + bool IsClassC() => ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168; + + return IsLinkLocal() || IsClassA() || IsClassC() || IsClassB(); + } +} diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 0300cadf..d8d25d45 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -2,10 +2,13 @@ // SPDX-License-Identifier: MIT using Google.Protobuf; -using Nethermind.Libp2p.Core; using Microsoft.Extensions.Logging; +using Multiformats.Address.Net; +using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Dto; -using Multiformats.Address.Protocols; +using Nethermind.Libp2p.Protocols.Identify; +using Nethermind.Libp2p.Stack; +using System.Net.Sockets; namespace Nethermind.Libp2p.Protocols; @@ -18,12 +21,12 @@ public class IdentifyProtocol : ISessionProtocol private readonly string _protocolVersion; private readonly ILogger? _logger; - private readonly IPeerFactoryBuilder _peerFactoryBuilder; + private readonly IProtocolStackSettings _protocolStackSettings; - public IdentifyProtocol(IPeerFactoryBuilder peerFactoryBuilder, IdentifyProtocolSettings? settings = null, ILoggerFactory? loggerFactory = null) + public IdentifyProtocol(IProtocolStackSettings protocolStackSettings, IdentifyProtocolSettings? settings = null, ILoggerFactory? loggerFactory = null) { _logger = loggerFactory?.CreateLogger(); - _peerFactoryBuilder = peerFactoryBuilder; + _protocolStackSettings = protocolStackSettings; _agentVersion = settings?.AgentVersion ?? IdentifyProtocolSettings.Default.AgentVersion!; _protocolVersion = settings?.ProtocolVersion ?? IdentifyProtocolSettings.Default.ProtocolVersion!; @@ -55,10 +58,13 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) ProtocolVersion = _protocolVersion, AgentVersion = _agentVersion, PublicKey = context.Peer.Identity.PublicKey.ToByteString(), - ListenAddrs = { ByteString.CopyFrom(context.Peer.Address.Get().ToBytes()) }, - ObservedAddr = ByteString.CopyFrom(context.State.RemoteAddress!.Get().ToBytes()), - Protocols = { _peerFactoryBuilder.AppLayerProtocols.Select(p => p.Id) } + ObservedAddr = ByteString.CopyFrom(context.State.RemoteAddress!.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto).ToBytes()), + Protocols = { _protocolStackSettings.Protocols!.Select(r => r.Key.Protocol).OfType().Select(p => p.Id) } }; + + ByteString[] endpoints = context.Peer.ListenAddresses.Where(a => !a.ToEndPoint().Address.IsPrivate()).Select(a => a.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto)).Select(a => ByteString.CopyFrom(a.ToBytes())).ToArray(); + identify.ListenAddrs.AddRange(endpoints); + byte[] ar = new byte[identify.CalculateSize()]; identify.WriteTo(ar); diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index 3d93bb2c..170a01b4 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -8,6 +8,8 @@ using Microsoft.Extensions.Logging; using Multiformats.Address; using Multiformats.Address.Protocols; +using Multiformats.Address.Net; +using Nethermind.Libp2p.Core.Exceptions; namespace Nethermind.Libp2p.Protocols; @@ -21,7 +23,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr { Socket listener = new(SocketType.Stream, ProtocolType.Tcp); - IPEndPoint endpoint = ToEndPoint(listenAddr); + IPEndPoint endpoint = listenAddr.ToEndPoint(); listener.Bind(endpoint); listener.Listen(); @@ -29,7 +31,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr if (endpoint.Port is 0) { IPEndPoint localIpEndpoint = (IPEndPoint)listener.LocalEndPoint!; - listenAddr.Add(localIpEndpoint.Port); + listenAddr.ReplaceOrAdd(localIpEndpoint.Port); } token.Register(listener.Close); @@ -101,11 +103,11 @@ await Task.Run(async () => }); } - public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAddr, CancellationToken token) + public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) { Socket client = new(SocketType.Stream, ProtocolType.Tcp); - IPEndPoint remoteEndpoint = ToEndPoint(remoteAddr); + IPEndPoint remoteEndpoint = remoteAddr.ToEndPoint(); _logger?.LogDebug("Dialing {0}:{1}", remoteEndpoint.Address, remoteEndpoint.Port); try @@ -114,15 +116,28 @@ public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAd } catch (SocketException e) { - _logger?.LogDebug($"Failed({context.Id}) to connect {remoteAddr}"); + _logger?.LogDebug($"Failed to connect {remoteAddr}"); _logger?.LogTrace($"Failed with {e.GetType()}: {e.Message}"); - return; + throw; } - context.Token.Register(client.Close); + if (client.LocalEndPoint is null) + { + throw new Libp2pException($"{nameof(client.LocalEndPoint)} is not set for client connection."); + } + if (client.RemoteEndPoint is null) + { + throw new Libp2pException($"{nameof(client.RemoteEndPoint)} is not set for client connection."); + } + + INewConnectionContext connectionCtx = context.CreateConnection(); + connectionCtx.State.RemoteAddress = ToMultiaddress(client.RemoteEndPoint, ProtocolType.Tcp); + connectionCtx.State.LocalAddress = ToMultiaddress(client.LocalEndPoint, ProtocolType.Tcp); + + connectionCtx.Token.Register(client.Close); token.Register(client.Close); - IChannel upChannel = context.Upgrade(); + IChannel upChannel = connectionCtx.Upgrade(); Task receiveTask = Task.Run(async () => { @@ -132,7 +147,7 @@ public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAd for (; client.Connected;) { int dataLength = await client.ReceiveAsync(buf, SocketFlags.None); - _logger?.LogDebug("Receive {0} data, len={1}", context.Id, dataLength); + _logger?.LogDebug("Ctx{0}: receive, length={1}", connectionCtx.Id, dataLength); if (dataLength == 0 || (await upChannel.WriteAsync(new ReadOnlySequence(buf[..dataLength]))) != IOResult.Ok) { @@ -152,7 +167,7 @@ public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAd { await foreach (ReadOnlySequence data in upChannel.ReadAllAsync()) { - _logger?.LogDebug("Send {0} data, len={1}", context.Id, data.Length); + _logger?.LogDebug("Ctx{0}: send, length={2}", connectionCtx.Id, data.Length); int sent = await client.SendAsync(data.ToArray(), SocketFlags.None); if (sent is 0 || !client.Connected) { @@ -166,16 +181,39 @@ public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAd } }); - await Task.WhenAll(receiveTask, sendTask).ContinueWith(t => context.Dispose()); + await Task.WhenAll(receiveTask, sendTask).ContinueWith(t => connectionCtx.Dispose()); _ = upChannel.CloseAsync(); } - private static IPEndPoint ToEndPoint(Multiaddress addr) + + public static Multiaddress ToMultiaddress(EndPoint ep, ProtocolType protocolType) { - MultiaddressProtocol ipProtocol = addr.Has() ? addr.Get() : addr.Get(); - IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); - int tcpPort = int.Parse(addr.Get().ToString()); - return new IPEndPoint(ipAddress, tcpPort); + Multiaddress multiaddress = new Multiaddress(); + IPEndPoint iPEndPoint = (IPEndPoint)ep; + if (iPEndPoint != null) + { + if (iPEndPoint.AddressFamily == AddressFamily.InterNetwork) + { + multiaddress.Add(iPEndPoint.Address.MapToIPv4()); + } + + if (iPEndPoint.AddressFamily == AddressFamily.InterNetworkV6) + { + multiaddress.Add(iPEndPoint.Address.MapToIPv6()); + } + + if (protocolType == ProtocolType.Tcp) + { + multiaddress.Add(iPEndPoint.Port); + } + + if (protocolType == ProtocolType.Udp) + { + multiaddress.Add(iPEndPoint.Port); + } + } + + return multiaddress; } } diff --git a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs index 2d18cb29..28ac8572 100644 --- a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs @@ -9,6 +9,7 @@ using Makaretu.Dns; using Multiformats.Address; using Multiformats.Address.Protocols; +using Nethermind.Libp2p.Core; namespace Nethermind.Libp2p.Protocols; @@ -32,7 +33,7 @@ public MDnsDiscoveryProtocol(ILoggerFactory? loggerFactory = null) private string PeerName = null!; - public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) + public async Task DiscoverAsync(IPeer peer, CancellationToken token = default) { ObservableCollection peers = new(); @@ -41,26 +42,29 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to PeerName = RandomString(32); ServiceProfile service = new(PeerName, ServiceNameOverride ?? ServiceName, 0); - if (localPeerAddr.Get().ToString() == "0.0.0.0") + foreach (var localPeerAddr in peer.ListenAddresses) { - service.Resources.Add(new TXTRecord() + if (localPeerAddr.Get().ToString() == "0.0.0.0") { - Name = service.FullyQualifiedName, - Strings = new List(MulticastService.GetLinkLocalAddresses() - .Where(x => x.AddressFamily == AddressFamily.InterNetwork) - .Select(item => $"dnsaddr={localPeerAddr.ReplaceOrAdd(item.ToString())}")) - }); - } - else - { - service.Resources.Add(new TXTRecord() + service.Resources.Add(new TXTRecord() + { + Name = service.FullyQualifiedName, + Strings = new List(MulticastService.GetLinkLocalAddresses() + .Where(x => x.AddressFamily == AddressFamily.InterNetwork) + .Select(item => $"dnsaddr={localPeerAddr.ReplaceOrAdd(item.ToString())}")) + }); + } + else { - Name = service.FullyQualifiedName, - Strings = new List + service.Resources.Add(new TXTRecord() + { + Name = service.FullyQualifiedName, + Strings = new List { $"dnsaddr={localPeerAddr}" } - }); + }); + } } _logger?.LogInformation("Started as {0} {1}", PeerName, ServiceNameOverride ?? ServiceName); @@ -77,7 +81,7 @@ public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken to .Select(x => x.Strings.Where(x => x.StartsWith("dnsaddr"))) .SelectMany(x => x).Select(x => Multiaddress.Decode(x.Replace("dnsaddr=", ""))).ToArray(); _logger?.LogTrace("Inst disc {0}, nmsg: {1}", e.ServiceInstanceName, e.Message); - if (Enumerable.Any(records) && !peers.Contains(Enumerable.First(records)) && localPeerAddr.Get().ToString() != Enumerable.First(records).Get().ToString()) + if (records.Length != 0 && !peers.Contains(records[0]) && peer.Identity.PeerId.ToString() != records[0].Get().ToString()) { List peerAddresses = new(); foreach (Multiaddress peer in records) diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index c337c880..1cbd0006 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -9,7 +9,7 @@ namespace Nethermind.Libp2p.Protocols; /// /// https://github.com/multiformats/multistream-select /// -public class MultistreamProtocol : IProtocol +public class MultistreamProtocol : IConnectionProtocol { private readonly ILogger? _logger; private const string ProtocolNotSupported = "na"; @@ -19,6 +19,7 @@ public MultistreamProtocol(ILoggerFactory? loggerFactory = null) { _logger = loggerFactory?.CreateLogger(); } + public async Task DialAsync(IChannel channel, IConnectionContext context) { if (!await SendHello(channel)) @@ -77,7 +78,7 @@ public async Task DialAsync(IChannel channel, IConnectionContext context) _logger?.LogDebug($"Negotiation failed"); return; } - _logger?.LogDebug($"Protocol selected during dialing: {selected}"); + _logger?.LogDebug($"Protocol selected during dialing: {selected.Id}"); await context.Upgrade(channel, selected); } diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index 2931b696..edae4782 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -17,7 +17,7 @@ namespace Nethermind.Libp2p.Protocols; /// /// -public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILoggerFactory? loggerFactory = null) : IProtocol +public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILoggerFactory? loggerFactory = null) : IConnectionProtocol { private readonly Protocol _protocol = new( HandshakePattern.XX, diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs index 5ce8d83c..aa979b79 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs @@ -38,9 +38,23 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext } } -public class RelayProtocol : ISessionProtocol +public class RelayHopProtocol : ISessionProtocol { - public string Id => throw new NotImplementedException(); + public string Id => "/libp2p/circuit/relay/0.2.0/hop"; + + public Task DialAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } + + public Task ListenAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } +} +public class RelayStopProtocol : ISessionProtocol +{ + public string Id => "/libp2p/circuit/relay/0.2.0/stop"; public Task DialAsync(IChannel downChannel, ISessionContext context) { diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index ef4d8d06..b8d31634 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -21,7 +21,7 @@ public async Task Test_Peer_is_in_fpeers() const string commonTopic = "topic1"; IPeer peer = Substitute.For(); - peer.Address.Returns(localPeerAddr); + peer.ListenAddresses.Returns([localPeerAddr]); peer.DialAsync(discoveredPeer, Arg.Any()).Returns(new TestRemotePeer(discoveredPeer)); TestDiscoveryProtocol discovery = new(); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index e1de2705..f56dcc3f 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -62,7 +62,7 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) CancellationToken token = router.InboundConnection(context.State.RemoteAddress, Id, listTcs.Task, dialTcs.Task, () => { - _ = context.DialAsync([this]); + _ = context.DialAsync(this); return dialTcs.Task; }); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 160f5a3f..d0c85206 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -163,9 +163,8 @@ public async Task RunAsync(IPeer localPeer, IDiscoveryProtocol discoveryProtocol limboMessageCache = new(this.settings.MessageCacheTtl); dontWantMessages = new(this.settings.MessageCacheTtl); - LocalPeerId = new PeerId(localPeer.Address.Get().ToString()!); + LocalPeerId = localPeer.Identity.PeerId; - _ = localPeer.StartListenAsync([localPeer.Address], token); _ = StartDiscoveryAsync(discoveryProtocol, token); logger?.LogInformation("Started"); @@ -223,7 +222,7 @@ private async Task StartDiscoveryAsync(IDiscoveryProtocol discoveryProtocol, Can } }, token); - await discoveryProtocol.DiscoverAsync(localPeer.Address, token); + await discoveryProtocol.DiscoverAsync(localPeer, token); } private async Task Reconnect(CancellationToken token) diff --git a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs index 9d460c66..01075083 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs @@ -11,7 +11,6 @@ using System.Net.Quic; using System.Net.Security; using System.Net.Sockets; -using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -104,7 +103,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, } } - public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAddr, CancellationToken token) + public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) { if (!QuicConnection.IsSupported) { @@ -139,9 +138,10 @@ public async Task DialAsync(INewConnectionContext context, Multiaddress remoteAd QuicConnection connection = await QuicConnection.ConnectAsync(clientConnectionOptions); _logger?.Connected(connection.LocalEndPoint, connection.RemoteEndPoint); + INewConnectionContext connectionContext = context.CreateConnection(); token.Register(() => _ = connection.CloseAsync(0)); - await ProcessStreams(context, connection, token); + await ProcessStreams(connectionContext, connection, token); } private static bool VerifyRemoteCertificate(Multiaddress remoteAddr, X509Certificate certificate) => @@ -155,28 +155,27 @@ private async Task ProcessStreams(INewConnectionContext context, QuicConnection _ = Task.Run(async () => { - foreach (ChannelRequest request in session.DialRequests) + foreach (UpgradeOptions upgradeOptions in session.DialRequests) { QuicStream stream = await connection.OpenOutboundStreamAsync(QuicStreamType.Bidirectional); - IChannel upChannel = context.Upgrade(new UpgradeOptions { SelectedProtocol = request.SubProtocol }); - ExchangeData(stream, upChannel, request.CompletionSource); + IChannel upChannel = context.Upgrade(upgradeOptions with { ModeOverride = UpgradeModeOverride.Dial }); + ExchangeData(stream, upChannel); } }, token); while (!token.IsCancellationRequested) { QuicStream inboundStream = await connection.AcceptInboundStreamAsync(token); - IChannel upChannel = context.Upgrade(); - ExchangeData(inboundStream, upChannel, null); + IChannel upChannel = context.Upgrade(new UpgradeOptions { ModeOverride = UpgradeModeOverride.Listen }); + ExchangeData(inboundStream, upChannel); } } - private void ExchangeData(QuicStream stream, IChannel upChannel, TaskCompletionSource? tcs) + private void ExchangeData(QuicStream stream, IChannel upChannel) { upChannel.GetAwaiter().OnCompleted(() => { stream.Close(); - tcs?.SetResult(); _logger?.LogDebug("Stream {stream id}: Closed", stream.Id); }); diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 9e2d0297..7679c097 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Exceptions; -using Org.BouncyCastle.Asn1.X509; using System.Buffers; using System.Runtime.CompilerServices; @@ -12,7 +11,7 @@ namespace Nethermind.Libp2p.Protocols; -public class YamuxProtocol : SymmetricProtocol, IProtocol +public class YamuxProtocol : SymmetricProtocol, IConnectionProtocol { private const int HeaderLength = 12; private const int PingDelay = 30_000; @@ -32,7 +31,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext _logger?.LogInformation(isListener ? "Listen" : "Dial"); TaskAwaiter downChannelAwaiter = channel.GetAwaiter(); - Dictionary channels = new(); + Dictionary channels = []; try { @@ -47,12 +46,12 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext _ = Task.Run(() => { - foreach (ChannelRequest request in session.DialRequests) + foreach (UpgradeOptions request in session.DialRequests) { int streamId = streamIdCounter; Interlocked.Add(ref streamIdCounter, 2); - _logger?.LogDebug("Stream {stream id}: Dialing with protocol {proto}", streamId, request.SubProtocol?.Id); + _logger?.LogDebug("Stream {stream id}: Dialing with protocol {proto}", streamId, request.SelectedProtocol?.Id); channels[streamId] = CreateUpchannel(streamId, YamuxHeaderFlags.Syn, request); } }); @@ -105,7 +104,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext if ((header.Flags & YamuxHeaderFlags.Syn) == YamuxHeaderFlags.Syn && !channels.ContainsKey(header.StreamID)) { - channels[header.StreamID] = CreateUpchannel(header.StreamID, YamuxHeaderFlags.Ack, null); + channels[header.StreamID] = CreateUpchannel(header.StreamID, YamuxHeaderFlags.Ack, new UpgradeOptions()); } if (!channels.ContainsKey(header.StreamID)) @@ -207,7 +206,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext await WriteGoAwayAsync(channel, SessionTerminationCode.Ok); - ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, ChannelRequest? channelRequest) + ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, UpgradeOptions upgradeOptions) { bool isListenerChannel = isListener ^ (streamId % 2 == 0); @@ -216,19 +215,17 @@ ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, Chan if (isListenerChannel) { - upChannel = context.Upgrade(); + upChannel = context.Upgrade(upgradeOptions with { ModeOverride = UpgradeModeOverride.Listen }); } else { - upChannel = context.Upgrade(new UpgradeOptions { SelectedProtocol = channelRequest?.SubProtocol }); + upChannel = context.Upgrade(upgradeOptions with { ModeOverride = UpgradeModeOverride.Dial }); } - ChannelState state = new(upChannel, channelRequest); - TaskCompletionSource? tcs = state.Request?.CompletionSource; + ChannelState state = new(upChannel); upChannel.GetAwaiter().OnCompleted(() => { - tcs?.SetResult(); channels.Remove(streamId); _logger?.LogDebug("Stream {stream id}: Closed", streamId); }); @@ -351,10 +348,10 @@ private Task WriteGoAwayAsync(IWriter channel, SessionTerminationCode code) => StreamID = 0, }); - private class ChannelState(IChannel? channel = default, ChannelRequest? request = default) + private class ChannelState(IChannel? channel = default) { public IChannel? Channel { get; set; } = channel; - public ChannelRequest? Request { get; set; } = request; + //public ChannelRequest? Request { get; set; } = request; public DataWindow LocalWindow { get; } = new(); public DataWindow RemoteWindow { get; } = new(); diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 560220d8..29bdb78a 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Multiformats.Address; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; @@ -9,12 +10,12 @@ namespace Nethermind.Libp2p.Stack; -public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings) : PeerFactory(protocolStackSettings) +public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, loggerFactory) { - public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, identity ?? new Identity()); + public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, identity ?? new Identity(), loggerFactory); } -class Libp2pPeer(IProtocolStackSettings protocolStackSettings, Identity identity) : LocalPeer(protocolStackSettings, identity) +class Libp2pPeer(IProtocolStackSettings protocolStackSettings, Identity identity, ILoggerFactory? loggerFactory = null) : LocalPeer(identity, protocolStackSettings, loggerFactory) { protected override async Task ConnectedTo(ISession session, bool isDialer) { diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index b2855adc..0029d9c5 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -14,6 +14,7 @@ public class Libp2pPeerFactoryBuilder(IServiceProvider? serviceProvider = defaul { private bool enforcePlaintext; private bool addPubsub; + private bool addRelay; public ILibp2pPeerFactoryBuilder WithPlaintextEnforced() { @@ -27,31 +28,29 @@ public ILibp2pPeerFactoryBuilder WithPubsub() return this; } - protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) + public ILibp2pPeerFactoryBuilder WithRelay() { - ProtocolRef[] transports = [ - Get(), - Get() - ]; + addRelay = true; + return this; + } - ProtocolRef[] selector1 = [Get()]; - Connect(transports, selector1); + protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) + { + ProtocolRef tcp = Get(); ProtocolRef[] encryption = [enforcePlaintext ? Get() : Get()]; - Connect(selector1, encryption); - - ProtocolRef[] selector2 = [Get()]; - Connect(encryption, selector2); ProtocolRef[] muxers = [Get()]; - Connect(selector2, muxers); - ProtocolRef[] selector3 = [Get()]; - Connect(muxers, selector3); + ProtocolRef[] commonSelector = [Get()]; + Connect([tcp], [Get()], encryption, [Get()], muxers, commonSelector); + + ProtocolRef quic = Get(); + Connect([quic], commonSelector); - ProtocolRef relay = Get(); + ProtocolRef[] relay = addRelay ? [Get(), Get()] : []; ProtocolRef[] pubsub = addPubsub ? [ Get(), Get(), @@ -62,15 +61,18 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) ProtocolRef[] apps = [ Get(), .. additionalProtocols, - relay, + .. relay, .. pubsub, ]; - Connect(selector3, apps); + Connect(commonSelector, apps); - ProtocolRef[] relaySelector = [Get()]; - Connect([relay], relaySelector); - Connect(relaySelector, apps.Where(a => a != relay).ToArray()); + if (addRelay) + { + ProtocolRef[] relaySelector = [Get()]; + Connect(relay, relaySelector); + Connect(relaySelector, apps.Where(a => !relay.Contains(a)).ToArray()); + } - return transports; + return [tcp, quic]; } } diff --git a/src/libp2p/Libp2p/Libp2pBuilderContext.cs b/src/libp2p/Libp2p/ProtocolStackSettings.cs similarity index 82% rename from src/libp2p/Libp2p/Libp2pBuilderContext.cs rename to src/libp2p/Libp2p/ProtocolStackSettings.cs index 6539a487..1a2eb1ac 100644 --- a/src/libp2p/Libp2p/Libp2pBuilderContext.cs +++ b/src/libp2p/Libp2p/ProtocolStackSettings.cs @@ -5,7 +5,7 @@ namespace Nethermind.Libp2p.Stack; -internal class Libp2pBuilderContext : IProtocolStackSettings +internal class ProtocolStackSettings : IProtocolStackSettings { public ProtocolRef[]? TopProtocols { get; set; } public Dictionary? Protocols { get; set; } diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index 517c510f..d41111b7 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -15,7 +15,7 @@ public static IServiceCollection AddLibp2p(this IServiceCollection services, Fun { return services .AddSingleton(sp => new Libp2pPeerFactoryBuilder(sp)) - .AddSingleton() + .AddSingleton() .AddSingleton(sp => factorySetup is null ? sp.GetRequiredService() : factorySetup(sp.GetRequiredService())) .AddSingleton(sp => (ILibp2pPeerFactoryBuilder)sp.GetRequiredService()) .AddScoped(sp => sp.GetService()!.Build()) diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index 7a30bbda..860f52e3 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -54,9 +54,10 @@ await peer.StartListenAsync( [string.Format(addrTemplate, args.Length > 0 && args[0] == "-sp" ? args[1] : "0")], ts.Token); - logger.LogInformation("Listener started at {address}", peer.Address); + logger.LogInformation("Listener started at {address}", string.Join(", ", peer.ListenAddresses)); Console.CancelKeyPress += delegate { ts.Cancel(); }; - await Task.FromCanceled(ts.Token); + await Task.Delay(-1, ts.Token); + await peer.DisconnectAsync(); } diff --git a/src/samples/chat/Properties/launchSettings.json b/src/samples/chat/Properties/launchSettings.json new file mode 100644 index 00000000..9f74c6ad --- /dev/null +++ b/src/samples/chat/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "profiles": { + "Chat": { + "commandName": "Project", + "commandLineArgs": "-d /ip4/127.0.0.1/tcp/9001/p2p/QmcJcjTEp33VYj4FjsyM9G7NTYW76HCrTvKWqoJmzXucJk --trace" + }, + "Chat server": { + "commandName": "Project", + "commandLineArgs": "--trace" + } + } +} diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 72e5d46d..0559b000 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -51,10 +51,9 @@ } }; +_ = peer.StartListenAsync([addr], ts.Token); _ = router.RunAsync(peer, new MDnsDiscoveryProtocol(serviceProvider.GetService()), token: ts.Token); - - string peerId = peer.Identity.PeerId.ToString(); string nickName = "libp2p-dotnet"; diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index 3febb216..651cf069 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -86,8 +86,8 @@ CancellationTokenSource listennTcs = new(); await localPeer.StartListenAsync([builder.MakeAddress(ip)], listennTcs.Token); localPeer.OnConnection += (session) => { Log($"Connected {session.RemoteAddress}"); return Task.CompletedTask; }; - Log($"Listening on {localPeer.Address}"); - db.ListRightPush(new RedisKey("listenerAddr"), new RedisValue(localPeer.Address.ToString())); + Log($"Listening on {string.Join(", ", localPeer.ListenAddresses)}"); + db.ListRightPush(new RedisKey("listenerAddr"), new RedisValue(localPeer.ListenAddresses.First().ToString())); await Task.Delay(testTimeoutSeconds * 1000); await listennTcs.CancelAsync(); return -1; @@ -149,20 +149,13 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) "noise" => Get(), _ => throw new NotImplementedException(), }]; - Connect(selector, transportStack); - - selector = [Get()]; - Connect(transportStack, selector); - ProtocolRef[] muxerStack = [muxer switch { "yamux" => Get(), _ => throw new NotImplementedException(), }]; - Connect(selector, muxerStack); - selector = [Get()]; - Connect(muxerStack, selector); + selector = Connect(selector, transportStack, [Get()], muxerStack, [Get()]); } ProtocolRef[] apps = [Get(), Get()]; From e4d6e5cf428ca4f2213faca0fb98a62e3601893b Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Tue, 20 Aug 2024 15:57:29 +0300 Subject: [PATCH 08/25] Improve dialing --- .../Libp2p.Core.Tests/TaskHelperTests.cs | 44 ++++++++++++++ .../Libp2p.Core.TestsBase/TestLocalPeer.cs | 5 ++ .../Libp2p.Core/Extensions/TaskHelper.cs | 33 +++++++++++ .../Libp2p.Core/ILibp2pPeerFactoryBuilder.cs | 2 + src/libp2p/Libp2p.Core/IPeer.cs | 1 + src/libp2p/Libp2p.Core/IProtocol.cs | 51 +--------------- src/libp2p/Libp2p.Core/Peer.cs | 58 +++++++++++++++---- .../Libp2p.Protocols.Pubsub/ManagedPeer.cs | 40 ------------- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 8 +-- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 7 +-- src/samples/pubsub-chat/Program.cs | 9 +-- 11 files changed, 144 insertions(+), 114 deletions(-) create mode 100644 src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs create mode 100644 src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs delete mode 100644 src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs diff --git a/src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs b/src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs new file mode 100644 index 00000000..a76a25c7 --- /dev/null +++ b/src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core.Extensions; + +namespace Nethermind.Libp2p.Core.Tests; +internal class TaskHelperTests +{ + + [Test] + public async Task Test_AllExceptions_RaiseAggregateException() + { + TaskCompletionSource tcs1 = new(); + TaskCompletionSource tcs2 = new(); + TaskCompletionSource tcs3 = new(); + + Task t = TaskHelper.FirstSuccess(tcs1.Task, tcs2.Task, tcs3.Task); + + tcs1.SetException(new Exception()); + tcs2.SetException(new Exception()); + tcs3.SetException(new Exception()); + + + Task r = await t; + } + + [Test] + public async Task Test_SingleSuccess_ReturnsCompletedTask() + { + TaskCompletionSource tcs1 = new(); + TaskCompletionSource tcs2 = new(); + TaskCompletionSource tcs3 = new(); + + Task t = TaskHelper.FirstSuccess(tcs1.Task, tcs2.Task, tcs3.Task); + + tcs1.SetException(new Exception()); + tcs2.SetException(new Exception()); + _ = Task.Delay(100).ContinueWith(t => tcs3.SetResult(true)); + + Task result = await t; + + Assert.That(result, Is.EqualTo(tcs3.Task)); + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs index 30a592fe..35fa8594 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestLocalPeer.cs @@ -26,6 +26,11 @@ public Task DialAsync(Multiaddress addr, CancellationToken token = def return Task.FromResult(new TestRemotePeer(addr)); } + public Task DialAsync(Multiaddress[] samePeerAddrs, CancellationToken token = default) + { + return Task.FromResult(new TestRemotePeer(samePeerAddrs.First())); + } + public Task DisconnectAsync() { return Task.CompletedTask; diff --git a/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs new file mode 100644 index 00000000..e758f632 --- /dev/null +++ b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Libp2p.Core.Extensions; + +internal static class TaskHelper +{ + public static async Task FirstSuccess(params Task[] tasks) + { + TaskCompletionSource tcs = new(); + + Task all = Task.WhenAll(tasks.Select(t => t.ContinueWith(t => + { + if (t.IsCompletedSuccessfully) + { + tcs.TrySetResult(t); + } + }))); + + Task result = await Task.WhenAny(tcs.Task, all); + if (result == all) + { + throw new AggregateException(tasks.Select(t => t.Exception).Where(ex => ex is not null)!); + } + return tcs.Task.Result; + } +} diff --git a/src/libp2p/Libp2p.Core/ILibp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p.Core/ILibp2pPeerFactoryBuilder.cs index 353b8ad0..8c57cf37 100644 --- a/src/libp2p/Libp2p.Core/ILibp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p.Core/ILibp2pPeerFactoryBuilder.cs @@ -6,4 +6,6 @@ namespace Nethermind.Libp2p.Core; public interface ILibp2pPeerFactoryBuilder : IPeerFactoryBuilder { public ILibp2pPeerFactoryBuilder WithPlaintextEnforced(); + public ILibp2pPeerFactoryBuilder WithPubsub(); + public ILibp2pPeerFactoryBuilder WithRelay(); } diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index 7376c9df..5839ab1c 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -11,6 +11,7 @@ public interface IPeer Identity Identity { get; } Task DialAsync(Multiaddress addr, CancellationToken token = default); + Task DialAsync(Multiaddress[] samePeerAddrs, CancellationToken token = default); Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default); diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index 8d562b20..a13860b9 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -6,73 +6,24 @@ namespace Nethermind.Libp2p.Core; public interface IProtocol -{ - /// - /// Id used to during connection establishedment, exchanging information about protocol versions and so on - /// +{ string Id { get; } } public interface ITransportProtocol : IProtocol { - /// - /// Opens a channel to listen to a remote peer - /// - /// A channel to communicate with a bottom layer protocol - /// Factory that spawns new channels used to interact with top layer protocols - /// Holds information about local and remote peers - /// Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token); - - /// - /// Actively dials a peer - /// - /// A channel to communicate with a bottom layer protocol - /// Factory that spawns new channels used to interact with top layer protocols - /// Holds information about local and remote peers - /// Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token); } public interface IConnectionProtocol : IProtocol { - /// - /// Opens a channel to listen to a remote peer - /// - /// A channel to communicate with a bottom layer protocol - /// Factory that spawns new channels used to interact with top layer protocols - /// Holds information about local and remote peers - /// Task ListenAsync(IChannel downChannel, IConnectionContext context); - - /// - /// Actively dials a peer - /// - /// A channel to communicate with a bottom layer protocol - /// Factory that spawns new channels used to interact with top layer protocols - /// Holds information about local and remote peers - /// Task DialAsync(IChannel downChannel, IConnectionContext context); } public interface ISessionProtocol : IProtocol { - - /// - /// Opens a channel to listen to a remote peer - /// - /// A channel to communicate with a bottom layer protocol - /// Factory that spawns new channels used to interact with top layer protocols - /// Holds information about local and remote peers - /// Task ListenAsync(IChannel downChannel, ISessionContext context); - - /// - /// Actively dials a peer - /// - /// A channel to communicate with a bottom layer protocol - /// Factory that spawns new channels used to interact with top layer protocols - /// Holds information about local and remote peers - /// Task DialAsync(IChannel downChannel, ISessionContext context); } diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 929574a0..00c2300f 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -5,6 +5,7 @@ using Multiformats.Address; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core.Exceptions; +using Nethermind.Libp2p.Core.Extensions; using Nethermind.Libp2p.Stack; using System.Collections.Concurrent; using System.Collections.ObjectModel; @@ -47,6 +48,7 @@ public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = TaskCompletionSource tcs = new(); SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = protocol }, token); await tcs.Task; + MarkAsConnected(); } private CancellationTokenSource connectionTokenSource = new(); @@ -149,7 +151,6 @@ public void ListenerReady(object sender, Multiaddress addr) public INewConnectionContext CreateConnection(ProtocolRef proto, Session? session, bool isListener) { session ??= new(this); - session.MarkAsConnected(); return new NewConnectionContext(this, session, proto, isListener, null); } @@ -178,8 +179,8 @@ public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, b session.DisconnectAsync(); return; } - OnConnection?.Invoke(session); session.ConnectedTcs.SetResult(); + OnConnection?.Invoke(session); }); return new NewSessionContext(this, session, proto, isListener, null); } @@ -204,6 +205,35 @@ internal IEnumerable GetProtocolsFor(ProtocolRef protocol) return protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.Protocol.GetType() == typeof(TProtocol))?.Protocol; } + public async Task DialAsync(Multiaddress[] addrs, CancellationToken token) + { + Dictionary cancellations = new(); + foreach (Multiaddress addr in addrs) + { + cancellations[addr] = CancellationTokenSource.CreateLinkedTokenSource(token); + } + + Task timeoutTask = Task.Delay(15_000, token); + Task wait = await TaskHelper.FirstSuccess([timeoutTask, ..addrs.Select(addr => DialAsync(addr, cancellations[addr].Token))]); + + if (wait == timeoutTask) + { + throw new TimeoutException(); + } + + ISession firstConnected = (wait as Task)!.Result; + + foreach (KeyValuePair c in cancellations) + { + if (c.Key != firstConnected.RemoteAddress) + { + c.Value.Cancel(false); + } + } + + return firstConnected; + } + public async Task DialAsync(Multiaddress addr, CancellationToken token = default) { ProtocolRef dialerProtocol = SelectProtocol(addr); @@ -228,6 +258,7 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token } throw new Libp2pException("Not able to dial the peer"); } + await session.Connected; return session; } @@ -245,7 +276,7 @@ internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgr ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].Single(); - Channel res = new(); + Channel downChannel = new(); isListener = options?.ModeOverride switch { UpgradeModeOverride.Dial => false, UpgradeModeOverride.Listen => true, _ => isListener }; @@ -258,14 +289,14 @@ internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgr case IConnectionProtocol tProto: { var ctx = new ConnectionContext(this, session, top, isListener, options); - upgradeTask = isListener ? tProto.ListenAsync(res.Reverse, ctx) : tProto.DialAsync(res.Reverse, ctx); + upgradeTask = isListener ? tProto.ListenAsync(downChannel.Reverse, ctx) : tProto.DialAsync(downChannel.Reverse, ctx); break; } case ISessionProtocol sProto: { var ctx = new SessionContext(this, session, top, isListener, options); - upgradeTask = isListener ? sProto.ListenAsync(res.Reverse, ctx) : sProto.DialAsync(res.Reverse, ctx); + upgradeTask = isListener ? sProto.ListenAsync(downChannel.Reverse, ctx) : sProto.DialAsync(downChannel.Reverse, ctx); break; } default: @@ -274,7 +305,11 @@ internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgr if (options?.SelectedProtocol == top.Protocol && options?.CompletionSource is not null) { - _ = upgradeTask.ContinueWith(t => MapToTaskCompletionSource(t, options.CompletionSource)); + _ = upgradeTask.ContinueWith(async t => + { + MapToTaskCompletionSource(t, options.CompletionSource); + await downChannel.CloseAsync(); + }); } upgradeTask.ContinueWith(t => @@ -285,7 +320,7 @@ internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgr } }); - return res; + return downChannel; } private static void MapToTaskCompletionSource(Task t, TaskCompletionSource tcs) @@ -344,14 +379,17 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef if (options?.SelectedProtocol == top.Protocol && options?.CompletionSource is not null) { - _ = upgradeTask.ContinueWith(t => MapToTaskCompletionSource(t, options.CompletionSource)); + _ = upgradeTask.ContinueWith(async t => { + MapToTaskCompletionSource(t, options.CompletionSource); + await parentChannel.CloseAsync(); + }); } await upgradeTask.ContinueWith(t => { if (t.IsFaulted) { - _logger?.LogError("failed"); + _logger?.LogError($"Upgrade task failed with {t.Exception}"); } }); } @@ -377,13 +415,11 @@ public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, Prot public async Task DialAsync() where TProtocol : ISessionProtocol { - await session.Connected; await session.DialAsync(); } public async Task DialAsync(ISessionProtocol protocol) { - await session.Connected; await session.DialAsync(protocol); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs b/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs deleted file mode 100644 index 67cb3b6d..00000000 --- a/src/libp2p/Libp2p.Protocols.Pubsub/ManagedPeer.cs +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Nethermind.Libp2p.Core; - -namespace Nethermind.Libp2p.Protocols.Pubsub; -internal class ManagedPeer(IPeer peer) -{ - internal async Task DialAsync(Multiaddress[] addrs, CancellationToken token) - { - Dictionary cancellations = new(); - foreach (Multiaddress addr in addrs) - { - cancellations[addr] = CancellationTokenSource.CreateLinkedTokenSource(token); - } - - Task timoutTask = Task.Delay(15_000, token); - Task> firstConnectedTask = Task.WhenAny(addrs - .Select(addr => peer.DialAsync(addr, cancellations[addr].Token))); - - Task wait = await Task.WhenAny(firstConnectedTask, timoutTask); - - if (wait == timoutTask) - { - throw new TimeoutException(); - } - - ISession firstConnected = firstConnectedTask.Result.Result; - - foreach (KeyValuePair c in cancellations) - { - if (c.Key != firstConnected.RemoteAddress) - { - c.Value.Cancel(false); - } - } - - return firstConnected; - } -} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index d0c85206..37de1d59 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -118,8 +118,7 @@ public Action? SendRpc private TtlCache limboMessageCache; private TtlCache<(PeerId, MessageId)> dontWantMessages; - private IPeer? localPeer; - private ManagedPeer peer; + private IPeer localPeer; private ILogger? logger = loggerFactory?.CreateLogger(); // all floodsub peers in topics @@ -157,7 +156,6 @@ public async Task RunAsync(IPeer localPeer, IDiscoveryProtocol discoveryProtocol throw new InvalidOperationException("Router has been already started"); } this.localPeer = localPeer; - peer = new ManagedPeer(localPeer); this.settings = settings ?? Settings.Default; messageCache = new(this.settings.MessageCacheTtl); limboMessageCache = new(this.settings.MessageCacheTtl); @@ -194,7 +192,7 @@ private async Task StartDiscoveryAsync(IDiscoveryProtocol discoveryProtocol, Can { try { - ISession session = await peer.DialAsync(addrs, token); + ISession session = await localPeer.DialAsync(addrs, token); if (!peerState.ContainsKey(session.RemoteAddress.Get().ToString())) { @@ -231,7 +229,7 @@ private async Task Reconnect(CancellationToken token) { try { - ISession remotePeer = await peer.DialAsync(rec.Addresses, token); + ISession remotePeer = await localPeer.DialAsync(rec.Addresses, token); await remotePeer.DialAsync(token); } catch diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 7679c097..08c98acc 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -215,11 +215,11 @@ ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, Upgr if (isListenerChannel) { - upChannel = context.Upgrade(upgradeOptions with { ModeOverride = UpgradeModeOverride.Listen }); + upChannel = session.Upgrade(upgradeOptions with { ModeOverride = UpgradeModeOverride.Listen }); } else { - upChannel = context.Upgrade(upgradeOptions with { ModeOverride = UpgradeModeOverride.Dial }); + upChannel = session.Upgrade(upgradeOptions with { ModeOverride = UpgradeModeOverride.Dial }); } ChannelState state = new(upChannel); @@ -309,8 +309,7 @@ await WriteHeaderAsync(channel, catch (Exception ex) { await WriteGoAwayAsync(channel, SessionTerminationCode.InternalError); - _logger?.LogDebug("Closed with exception {exception}", ex.Message); - _logger?.LogTrace("{stackTrace}", ex.StackTrace); + _logger?.LogDebug("Closed with exception \"{exception}\" {stackTrace}", ex.Message, ex.StackTrace); } foreach (ChannelState upChannel in channels.Values) diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 0559b000..e71bd368 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -8,18 +8,19 @@ using System.Text; using System.Text.Json; using Nethermind.Libp2p.Protocols.Pubsub; -using Multiformats.Address.Protocols; -using Multiformats.Address; +using System.Text.RegularExpressions; + +Regex omittedLogs = new Regex(".*(MDnsDiscoveryProtocol|IpTcpProtocol).*"); ServiceProvider serviceProvider = new ServiceCollection() - .AddLibp2p(builder => builder) + .AddLibp2p(builder => builder.WithPubsub()) .AddLogging(builder => builder.SetMinimumLevel(args.Contains("--trace") ? LogLevel.Trace : LogLevel.Information) .AddSimpleConsole(l => { l.SingleLine = true; l.TimestampFormat = "[HH:mm:ss.FFF]"; - })) + }).AddFilter((_, type, lvl) => !omittedLogs.IsMatch(type!))) .BuildServiceProvider(); IPeerFactory peerFactory = serviceProvider.GetService()!; From 0e730be615f1a0b478774bda769e5072b7d51be5 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 2 Dec 2024 11:14:00 +0300 Subject: [PATCH 09/25] Autoupdated protobuf --- src/libp2p/Libp2p.Core/Dto/KeyPair.cs | 26 +++- .../Libp2p.Protocols.Identify/Dto/Identify.cs | 13 +- .../Libp2p.Protocols.Noise/Dto/Exchange.cs | 52 +++++-- .../Dto/Exchange.cs | 13 +- src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs | 130 +++++++++++++++--- .../Dto/TopicDescriptor.cs | 39 +++++- 6 files changed, 231 insertions(+), 42 deletions(-) diff --git a/src/libp2p/Libp2p.Core/Dto/KeyPair.cs b/src/libp2p/Libp2p.Core/Dto/KeyPair.cs index 7bf8edf9..32ce19fd 100644 --- a/src/libp2p/Libp2p.Core/Dto/KeyPair.cs +++ b/src/libp2p/Libp2p.Core/Dto/KeyPair.cs @@ -50,6 +50,7 @@ public enum KeyType { #endregion #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class PublicKey : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -265,7 +266,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -288,7 +293,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -307,6 +316,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class PrivateKey : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -522,7 +532,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -545,7 +559,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; diff --git a/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs b/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs index 90d75e5f..a3491133 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/Dto/Identify.cs @@ -39,6 +39,7 @@ static IdentifyReflection() { } #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class Identify : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -373,7 +374,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -412,7 +417,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; diff --git a/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs b/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs index 89e7782c..5c210f6f 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/Dto/Exchange.cs @@ -56,6 +56,7 @@ public enum KeyType { #endregion #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class Exchange : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -268,7 +269,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -291,7 +296,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -310,6 +319,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class NoiseExtensions : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -472,7 +482,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -495,7 +509,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -514,6 +532,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class PublicKey : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -729,7 +748,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -752,7 +775,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -771,6 +798,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class NoiseHandshakePayload : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -1015,7 +1043,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -1045,7 +1077,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs b/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs index a5dee6f0..6b32d93d 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/Dto/Exchange.cs @@ -37,6 +37,7 @@ static ExchangeReflection() { } #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class Exchange : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -249,7 +250,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -272,7 +277,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs index ce0e9f34..80d458ee 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/Rpc.cs @@ -60,6 +60,7 @@ static RpcReflection() { } #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class Rpc : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -254,7 +255,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -284,7 +289,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -313,6 +322,7 @@ public void MergeFrom(pb::CodedInputStream input) { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static partial class Types { + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class SubOpts : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -528,7 +538,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -551,7 +565,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -575,6 +593,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class Message : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -959,7 +978,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -998,7 +1021,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -1033,6 +1060,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class ControlMessage : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -1249,7 +1277,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -1284,7 +1316,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -1315,6 +1351,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class ControlIHave : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -1502,7 +1539,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -1525,7 +1566,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -1544,6 +1589,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class ControlIWant : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -1688,7 +1734,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -1707,7 +1757,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -1722,6 +1776,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class ControlGraft : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -1891,7 +1946,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -1910,7 +1969,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -1925,6 +1988,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class ControlPrune : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -2164,7 +2228,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -2191,7 +2259,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -2214,6 +2286,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class PeerInfo : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -2426,7 +2499,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -2449,7 +2526,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -2468,6 +2549,7 @@ public void MergeFrom(pb::CodedInputStream input) { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class ControlIDontWant : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -2612,7 +2694,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -2631,7 +2717,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs index 05c12f39..531eb295 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/Dto/TopicDescriptor.cs @@ -45,6 +45,7 @@ static TopicDescriptorReflection() { } #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class TopicDescriptor : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -282,7 +283,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -315,7 +320,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -347,6 +356,7 @@ public void MergeFrom(pb::CodedInputStream input) { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static partial class Types { + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class AuthOpts : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -537,7 +547,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -560,7 +574,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; @@ -593,6 +611,7 @@ public enum AuthMode { } + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] public sealed partial class EncOpts : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage @@ -783,7 +802,11 @@ public void MergeFrom(pb::CodedInputStream input) { #else uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); break; @@ -806,7 +829,11 @@ public void MergeFrom(pb::CodedInputStream input) { void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { uint tag; while ((tag = input.ReadTag()) != 0) { - switch(tag) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { default: _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); break; From b0181d04b0cee056b211cde67441136aaf1d6c02 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 2 Dec 2024 18:38:00 +0300 Subject: [PATCH 10/25] Fix compilation --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 4 - .../Libp2p.Core.TestsBase/E2e/ChannelBus.cs | 1 - .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 15 +- .../E2e/TestLocalPeer.cs | 22 +- .../E2e/TestMuxerProtocol.cs | 69 ++++--- .../E2e/TestMuxerTests.cs | 13 +- .../E2e/TestPeerFactory.cs | 8 +- .../E2e/TestPingProtocol.cs | 6 +- .../Libp2p.Core.TestsBase/LocalPeerStub.cs | 2 +- src/libp2p/Libp2p.Core/ConnectionContext.cs | 6 - .../Discovery/IDiscoveryProtocol.cs | 2 +- src/libp2p/Libp2p.Core/Discovery/PeerStore.cs | 1 - .../Libp2p.Core/Exceptions/Libp2pException.cs | 2 +- .../Libp2p.Core/Extensions/TaskHelper.cs | 6 - src/libp2p/Libp2p.Core/IListener.cs | 3 - src/libp2p/Libp2p.Core/IPeer.cs | 4 +- src/libp2p/Libp2p.Core/IPeerContext.cs | 1 + src/libp2p/Libp2p.Core/IPeerFactory.cs | 2 - src/libp2p/Libp2p.Core/IProtocol.cs | 4 +- src/libp2p/Libp2p.Core/IRemotePeer.cs | 2 - src/libp2p/Libp2p.Core/Peer.cs | 24 +-- src/libp2p/Libp2p.Core/PeerContext.cs | 3 - src/libp2p/Libp2p.Core/PeerFactory.cs | 4 - .../Libp2p.Core/PeerFactoryBuilderBase.cs | 5 +- .../IdentifyProtocol.cs | 28 ++- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 1 - .../MDnsDiscoveryProtocol.cs | 18 +- .../Usings.cs | 3 - .../MultistreamProtocol.cs | 4 - .../NoiseProtocolTests.cs | 190 +++++++++--------- .../Libp2p.Protocols.Noise.Tests/Usings.cs | 6 - .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 19 +- .../Program.cs | 18 +- .../FloodsubProtocolTests.cs | 7 +- .../GossipsubProtocolTests.cs | 90 ++++----- .../PubsubProtocolTests.cs | 3 - .../PubsubRouter.Topics.cs | 2 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 9 +- .../MultistreamProtocolTests.cs | 32 +-- .../PubSubTestSetup.cs | 9 +- .../PubsubPeerDiscoveryProtocol.cs | 18 +- .../TlsProtocolTests.cs | 88 ++++---- .../Libp2p.Protocols.Tls/TlsProtocol.cs | 52 +++-- .../YamuxProtocolTests.cs | 4 - .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 88 ++++---- src/libp2p/Libp2p/Libp2p.csproj | 1 + src/libp2p/Libp2p/Libp2pLocalPeer.cs | 7 - src/libp2p/Libp2p/Libp2pPeerFactory.cs | 1 - src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 11 +- .../MultiaddressBasedSelectorProtocol.cs | 6 - .../Libp2p/ServiceProviderExtensions.cs | 1 - src/samples/chat/Program.cs | 2 +- src/samples/pubsub-chat/Program.cs | 6 +- src/samples/transport-interop/Program.cs | 4 +- 54 files changed, 454 insertions(+), 483 deletions(-) diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index cd894c32..95176303 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -2,11 +2,7 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; -using Multiformats.Address.Protocols; using Nethermind.Libp2p.Stack; -using Nethermind.Libp2p.Core; -using NUnit.Framework.Constraints; -using System.Net; namespace Nethermind.Libp2p.Core.Tests; public class ContextTests diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs index e7db3c7d..0cdb874e 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using System.Threading.Channels; namespace Nethermind.Libp2p.Core.TestsBase.E2e; diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index 91f51f44..569319c7 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -3,11 +3,18 @@ namespace Nethermind.Libp2p.Core.TestsBase.E2e; -public class TestBuilder(ChannelBus? commmonBus = null, IServiceProvider? serviceProvider = null) : PeerFactoryBuilderBase(serviceProvider) +public class TestBuilder(IServiceProvider? serviceProvider = null) : PeerFactoryBuilderBase(serviceProvider) { - protected override ProtocolStack BuildStack() + protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { - return Over(new TestMuxerProtocol(commmonBus ?? new ChannelBus(), new TestContextLoggerFactory())) - .AddAppLayerProtocol(); + var root = Get(); + + Connect([root], + [ + Get(), + .. additionalProtocols + ]); + + return [root]; } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs index d09db191..4ff6aa02 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs @@ -2,20 +2,36 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using System.Collections.ObjectModel; namespace Nethermind.Libp2p.Core.TestsBase.E2e; -internal class TestLocalPeer(Identity id) : ILocalPeer +internal class TestLocalPeer(Identity id) : IPeer { public Identity Identity { get => id; set => throw new NotImplementedException(); } public Multiaddress Address { get => $"/p2p/{id.PeerId}"; set => throw new NotImplementedException(); } - public Task DialAsync(Multiaddress addr, CancellationToken token = default) + public ObservableCollection ListenAddresses => throw new NotImplementedException(); + + public event Connected? OnConnected; + + public Task DialAsync(Multiaddress[] samePeerAddrs, CancellationToken token = default) + { + throw new NotImplementedException(); + } + + public Task DisconnectAsync() + { + throw new NotImplementedException(); + } + + + public Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) { throw new NotImplementedException(); } - public Task ListenAsync(Multiaddress addr, CancellationToken token = default) + Task IPeer.DialAsync(Multiaddress addr, CancellationToken token) { throw new NotImplementedException(); } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index 4ead3772..293345d3 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -1,13 +1,14 @@ using Google.Protobuf; using Microsoft.Extensions.Logging; +using Multiformats.Address; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.TestsBase.Dto; using Nethermind.Libp2p.Core.TestsBase.E2e; using Org.BouncyCastle.Utilities.Encoders; using System.Buffers; -class TestMuxerProtocol(ChannelBus bus, ILoggerFactory? loggerFactory = null) : IProtocol +class TestMuxerProtocol(ChannelBus bus, ILoggerFactory? loggerFactory = null) : ITransportProtocol { private const string id = "test-muxer"; @@ -15,69 +16,77 @@ class TestMuxerProtocol(ChannelBus bus, ILoggerFactory? loggerFactory = null) : public string Id => id; - public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) { - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Dial async"); - context.Connected(context.RemotePeer); - await Task.Run(() => HandleRemote(bus.Dial(context.LocalPeer.Identity.PeerId, context.RemotePeer.Address.GetPeerId()!), upChannelFactory!, context)); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Dial async"); + + await Task.Run(async () => + { + IChannel chan = bus.Dial(context.Peer.Identity.PeerId, remoteAddr.GetPeerId()!); + using INewConnectionContext connection = context.CreateConnection(); + connection.State.RemoteAddress = remoteAddr; + + await HandleRemote(chan, connection, context); + }); } - public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) { - context.ListenerReady(); - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Listen async"); - await foreach (var item in bus.GetIncomingRequests(context.LocalPeer.Identity.PeerId)) + context.ListenerReady(listenAddr); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listen async"); + await foreach (var item in bus.GetIncomingRequests(context.Peer.Identity.PeerId)) { - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Listener handles new con"); - _ = HandleRemote(item, upChannelFactory!, context, true); + using INewConnectionContext connection = context.CreateConnection(); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener handles new con"); + _ = HandleRemote(item, connection, context, true); } } - private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelFactory, IPeerContext context, bool isListen = false) + private async Task HandleRemote(IChannel downChannel, INewConnectionContext connection, ITransportContext context, bool isListen = false) { uint counter = isListen ? 1u : 0u; Dictionary chans = []; string peer = ""; - context = context.Fork(); if (isListen) { peer = await downChannel.ReadLineAsync(); - await downChannel.WriteLineAsync(context.LocalPeer.Identity.PeerId!.ToString()); - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Listener handles remote {peer}"); + await downChannel.WriteLineAsync(context.Peer.Identity.PeerId!.ToString()); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener handles remote {peer}"); } else { - await downChannel.WriteLineAsync(context.LocalPeer.Identity.PeerId!.ToString()); + await downChannel.WriteLineAsync(context.Peer.Identity.PeerId!.ToString()); peer = await downChannel.ReadLineAsync(); - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: Dialer handles remote {peer}"); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Dialer handles remote {peer}"); } - context.RemotePeer.Address = $"/p2p/{peer}"; + using INewSessionContext session = connection.UpgradeToSession(); + connection.State.RemoteAddress = $"/p2p/{peer}"; - string logPrefix = $"{context.LocalPeer.Identity.PeerId}<>{peer}"; + string logPrefix = $"{context.Peer.Identity.PeerId}<>{peer}"; _ = Task.Run(async () => { - foreach (var item in context.SubDialRequests.GetConsumingEnumerable()) + foreach (var item in session.DialRequests) { uint chanId = Interlocked.Add(ref counter, 2); - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}({chanId}): Sub-request {item.SubProtocol} {item.CompletionSource is not null} from {context.RemotePeer.Address.GetPeerId()}"); + logger?.LogDebug($"{context.Peer.Identity.PeerId}({chanId}): Sub-request {item.SelectedProtocol} {item.CompletionSource is not null} from {connection.State.RemoteAddress.GetPeerId()}"); chans[chanId] = new MuxerChannel { Tcs = item.CompletionSource }; var response = new MuxerPacket() { ChannelId = chanId, Type = MuxerPacketType.NewStreamRequest, - Protocols = { item.SubProtocol!.Id } + Protocols = { item.SelectedProtocol!.Id } }; logger?.LogDebug($"{logPrefix}({response.ChannelId}): > Packet {response.Type} {string.Join(",", response.Protocols)} {response.Data?.Length ?? 0}"); _ = downChannel.WriteSizeAndProtobufAsync(response); } - logger?.LogDebug($"{context.LocalPeer.Identity.PeerId}: SubDialRequests End"); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: SubDialRequests End"); }); @@ -87,7 +96,7 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF { logger?.LogDebug($"{logPrefix}: < READY({(isListen ? "list" : "dial")})"); - var packet = await downChannel.ReadPrefixedProtobufAsync(MuxerPacket.Parser); + MuxerPacket packet = await downChannel.ReadPrefixedProtobufAsync(MuxerPacket.Parser); logger?.LogDebug($"{logPrefix}({packet.ChannelId}): < Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); @@ -97,7 +106,7 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF IProtocol? selected = null; foreach (var proto in packet.Protocols) { - selected = upChannelFactory.SubProtocols.FirstOrDefault(x => x.Id == proto); + selected = session.SubProtocols.FirstOrDefault(x => x.Id == proto); if (selected is not null) break; } if (selected is not null) @@ -113,9 +122,9 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF } }; - var req = new ChannelRequest { SubProtocol = selected }; + var req = new UpgradeOptions { SelectedProtocol = selected, ModeOverride = UpgradeModeOverride.Dial }; - IChannel upChannel = upChannelFactory.SubListen(context, req); + IChannel upChannel = session.Upgrade(req); chans[packet.ChannelId] = new MuxerChannel { UpChannel = upChannel }; _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); @@ -141,10 +150,10 @@ private async Task HandleRemote(IChannel downChannel, IChannelFactory upChannelF case MuxerPacketType.NewStreamResponse: if (packet.Protocols.Any()) { - var req = new ChannelRequest { SubProtocol = upChannelFactory.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First()) }; - IChannel upChannel = upChannelFactory.SubDial(context, req); + var req = new UpgradeOptions { SelectedProtocol = session.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First()), ModeOverride = UpgradeModeOverride.Dial }; + IChannel upChannel = session.Upgrade(req); chans[packet.ChannelId].UpChannel = upChannel; - logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SubProtocol}"); + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SelectedProtocol}"); _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); } else diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs index b5593514..653153e7 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs @@ -12,19 +12,20 @@ internal class TestMuxerTests public async Task Test_ConnectionEstablished_AfterHandshake() { ServiceProvider sp = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(null, sp)) + .AddSingleton(sp => new TestBuilder(sp)) .AddSingleton() + .AddSingleton() .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); IPeerFactory peerFactory = sp.GetService()!; - ILocalPeer peerA = peerFactory.Create(TestPeers.Identity(1)); - await peerA.ListenAsync(TestPeers.Multiaddr(1)); - ILocalPeer peerB = peerFactory.Create(TestPeers.Identity(2)); - await peerB.ListenAsync(TestPeers.Multiaddr(2)); + IPeer peerA = peerFactory.Create(TestPeers.Identity(1)); + await peerA.StartListenAsync([TestPeers.Multiaddr(1)]); + IPeer peerB = peerFactory.Create(TestPeers.Identity(2)); + await peerB.StartListenAsync([TestPeers.Multiaddr(2)]); - IRemotePeer remotePeerB = await peerA.DialAsync(peerB.Address); + ISession remotePeerB = await peerA.DialAsync(TestPeers.Multiaddr(1)); await remotePeerB.DialAsync(); } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs index dcfcaebb..3079f3ee 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs @@ -1,16 +1,16 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; +using Nethermind.Libp2p.Stack; using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core.TestsBase.E2e; -internal class TestPeerFactory(IServiceProvider serviceProvider) : PeerFactory(serviceProvider) +internal class TestPeerFactory(IProtocolStackSettings protocolStackSettings) : PeerFactory(protocolStackSettings) { - ConcurrentDictionary peers = new(); + ConcurrentDictionary peers = new(); - public override ILocalPeer Create(Identity? identity = null, Multiaddress? localAddr = null) + public override IPeer Create(Identity? identity = default) { ArgumentNullException.ThrowIfNull(identity); return peers.GetOrAdd(identity.PeerId, (p) => new TestLocalPeer(identity)); diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPingProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPingProtocol.cs index 0a735853..6d4438d6 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPingProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPingProtocol.cs @@ -4,11 +4,11 @@ using NUnit.Framework; namespace Nethermind.Libp2p.Core.TestsBase.E2e; -class TestPingProtocol : IProtocol +class TestPingProtocol : ISessionProtocol { public string Id => "test-ping"; - public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + public async Task DialAsync(IChannel downChannel, ISessionContext context) { string str = "hello"; await downChannel.WriteLineAsync(str); @@ -16,7 +16,7 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? upChannelFact Assert.That(res, Is.EqualTo(str + " there")); } - public async Task ListenAsync(IChannel downChannel, IChannelFactory? upChannelFactory, IPeerContext context) + public async Task ListenAsync(IChannel downChannel, ISessionContext context) { string str = await downChannel.ReadLineAsync(); await downChannel.WriteLineAsync(str + " there"); diff --git a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs index a1bb5437..9f755b04 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs @@ -19,7 +19,7 @@ public LocalPeerStub() public ObservableCollection ListenAddresses => throw new NotImplementedException(); - public event OnConnection? OnConnection; + public event Connected? OnConnected; public Task DialAsync(Multiaddress addr, CancellationToken token = default) { diff --git a/src/libp2p/Libp2p.Core/ConnectionContext.cs b/src/libp2p/Libp2p.Core/ConnectionContext.cs index dcf0bb4b..0bcf4890 100644 --- a/src/libp2p/Libp2p.Core/ConnectionContext.cs +++ b/src/libp2p/Libp2p.Core/ConnectionContext.cs @@ -1,12 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Nethermind.Libp2p.Core; //public class ConnectionContext(LocalPeer localPeer, ITransportProtocol transportProtocol) : ITransportConnectionContext diff --git a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs index abd933ec..0b4c0e80 100644 --- a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs @@ -7,5 +7,5 @@ namespace Nethermind.Libp2p.Core.Discovery; public interface IDiscoveryProtocol { - Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default); + Task DiscoverAsync(IReadOnlyList localPeerAddr, CancellationToken token = default); } diff --git a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs index 7008217c..93267bb8 100644 --- a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs +++ b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs @@ -3,7 +3,6 @@ using Google.Protobuf; using Multiformats.Address; -using Nethermind.Libp2p.Core.Dto; using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core.Discovery; diff --git a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs index d225cd12..3da5c397 100644 --- a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs +++ b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs @@ -19,7 +19,7 @@ public class ChannelClosedException : Libp2pException { public ChannelClosedException() { - + } } diff --git a/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs index e758f632..39ce2f36 100644 --- a/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs +++ b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs @@ -1,12 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Nethermind.Libp2p.Core.Extensions; internal static class TaskHelper diff --git a/src/libp2p/Libp2p.Core/IListener.cs b/src/libp2p/Libp2p.Core/IListener.cs index 2c17e0a9..b1b0ff47 100644 --- a/src/libp2p/Libp2p.Core/IListener.cs +++ b/src/libp2p/Libp2p.Core/IListener.cs @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; -using System.Runtime.CompilerServices; - namespace Nethermind.Libp2p.Core; diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index 5839ab1c..7fa44d2d 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -19,10 +19,10 @@ public interface IPeer ObservableCollection ListenAddresses { get; } - event OnConnection? OnConnection; + event Connected? OnConnected; } -public delegate Task OnConnection(ISession newSession); +public delegate Task Connected(ISession newSession); public interface ISession { diff --git a/src/libp2p/Libp2p.Core/IPeerContext.cs b/src/libp2p/Libp2p.Core/IPeerContext.cs index 6612cb20..154b755c 100644 --- a/src/libp2p/Libp2p.Core/IPeerContext.cs +++ b/src/libp2p/Libp2p.Core/IPeerContext.cs @@ -50,4 +50,5 @@ public class State public Multiaddress? LocalAddress { get; set; } public Multiaddress? RemoteAddress { get; set; } public PublicKey? RemotePublicKey { get; set; } + public PeerId? RemotePeerId => RemoteAddress?.GetPeerId(); } diff --git a/src/libp2p/Libp2p.Core/IPeerFactory.cs b/src/libp2p/Libp2p.Core/IPeerFactory.cs index bed6ba98..e5875146 100644 --- a/src/libp2p/Libp2p.Core/IPeerFactory.cs +++ b/src/libp2p/Libp2p.Core/IPeerFactory.cs @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; - namespace Nethermind.Libp2p.Core; public interface IPeerFactory diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index a13860b9..0a4cdff8 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -6,14 +6,14 @@ namespace Nethermind.Libp2p.Core; public interface IProtocol -{ +{ string Id { get; } } public interface ITransportProtocol : IProtocol { Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token); - Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token); + Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token); } public interface IConnectionProtocol : IProtocol diff --git a/src/libp2p/Libp2p.Core/IRemotePeer.cs b/src/libp2p/Libp2p.Core/IRemotePeer.cs index 40a4bee9..9b38be09 100644 --- a/src/libp2p/Libp2p.Core/IRemotePeer.cs +++ b/src/libp2p/Libp2p.Core/IRemotePeer.cs @@ -1,7 +1,5 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; - namespace Nethermind.Libp2p.Core; diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 00c2300f..05e2c55f 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -36,7 +36,6 @@ public class Session(LocalPeer peer) : ISession public async Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { - await Connected; TaskCompletionSource tcs = new(); SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance() }, token); await tcs.Task; @@ -44,7 +43,6 @@ public async Task DialAsync(CancellationToken token = default) where public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = default) { - await Connected; TaskCompletionSource tcs = new(); SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = protocol }, token); await tcs.Task; @@ -105,7 +103,7 @@ protected virtual IEnumerable PrepareAddresses(Multiaddress[] addr Dictionary> listenerReadyTcs = new(); - public event OnConnection? OnConnection; + public event Connected? OnConnected; public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) { @@ -165,6 +163,7 @@ public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, b { if (sessions.Any(s => !ReferenceEquals(session, s) && s.State.RemoteAddress.GetPeerId() == session.State.RemoteAddress?.GetPeerId())) { + _ = session.DisconnectAsync(); throw new Libp2pException("Session is already established"); } @@ -176,11 +175,11 @@ public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, b { if (t.IsFaulted) { - session.DisconnectAsync(); + _ = session.DisconnectAsync(); return; } session.ConnectedTcs.SetResult(); - OnConnection?.Invoke(session); + OnConnected?.Invoke(session); }); return new NewSessionContext(this, session, proto, isListener, null); } @@ -214,7 +213,7 @@ public async Task DialAsync(Multiaddress[] addrs, CancellationToken to } Task timeoutTask = Task.Delay(15_000, token); - Task wait = await TaskHelper.FirstSuccess([timeoutTask, ..addrs.Select(addr => DialAsync(addr, cancellations[addr].Token))]); + Task wait = await TaskHelper.FirstSuccess([timeoutTask, .. addrs.Select(addr => DialAsync(addr, cancellations[addr].Token))]); if (wait == timeoutTask) { @@ -290,7 +289,7 @@ internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgr { var ctx = new ConnectionContext(this, session, top, isListener, options); upgradeTask = isListener ? tProto.ListenAsync(downChannel.Reverse, ctx) : tProto.DialAsync(downChannel.Reverse, ctx); - + break; } case ISessionProtocol sProto: @@ -316,7 +315,7 @@ internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgr { if (t.IsFaulted) { - _logger?.LogError($"Upgrade task failed with {t.Exception}"); + _logger?.LogError($"Upgrade task failed for {top} with {t.Exception}"); } }); @@ -345,7 +344,7 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if(upgradeProtocol is not null && !protocolStackSettings.Protocols[protocol].Any(p => p.Protocol == upgradeProtocol)) + if (upgradeProtocol is not null && !protocolStackSettings.Protocols[protocol].Any(p => p.Protocol == upgradeProtocol)) { protocolStackSettings.Protocols.Add(new ProtocolRef(upgradeProtocol, false), []); } @@ -364,7 +363,7 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef case IConnectionProtocol tProto: { var ctx = new ConnectionContext(this, session, top, isListener, options); - upgradeTask = isListener ? tProto.ListenAsync(parentChannel, ctx): tProto.DialAsync(parentChannel, ctx); + upgradeTask = isListener ? tProto.ListenAsync(parentChannel, ctx) : tProto.DialAsync(parentChannel, ctx); break; } case ISessionProtocol sProto: @@ -379,7 +378,8 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef if (options?.SelectedProtocol == top.Protocol && options?.CompletionSource is not null) { - _ = upgradeTask.ContinueWith(async t => { + _ = upgradeTask.ContinueWith(async t => + { MapToTaskCompletionSource(t, options.CompletionSource); await parentChannel.CloseAsync(); }); @@ -442,7 +442,7 @@ public void Dispose() public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), IConnectionContext { public UpgradeOptions? UpgradeOptions => upgradeOptions; - + public Task DisconnectAsync() { return session.DisconnectAsync(); diff --git a/src/libp2p/Libp2p.Core/PeerContext.cs b/src/libp2p/Libp2p.Core/PeerContext.cs index f5ec5634..6462fd0c 100644 --- a/src/libp2p/Libp2p.Core/PeerContext.cs +++ b/src/libp2p/Libp2p.Core/PeerContext.cs @@ -1,9 +1,6 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; -using System.Collections.Concurrent; - namespace Nethermind.Libp2p.Core; //public class PeerContext : IPeerContext diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index a1b74280..01285f92 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -3,10 +3,6 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Stack; -using Microsoft.Extensions.DependencyInjection; -using Multiformats.Address; -using Multiformats.Address.Protocols; -using System.Runtime.CompilerServices; namespace Nethermind.Libp2p.Core; diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 7e43c8ee..ff2bf765 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -1,13 +1,12 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.DependencyInjection; using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; -public interface ICreateProtocolInstance +public static class PeerFactoryBuilderBase { private static readonly HashSet protocols = []; @@ -26,6 +25,7 @@ internal static TProtocol CreateProtocolInstance(IServiceProvider ser } return (TProtocol)existing; } +} public class ProtocolRef(IProtocol protocol, bool isExposed = true) { @@ -42,6 +42,7 @@ public override string ToString() } } + public abstract class PeerFactoryBuilderBase : IPeerFactoryBuilder where TBuilder : PeerFactoryBuilderBase, IPeerFactoryBuilder where TPeerFactory : IPeerFactory diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 366b99cd..5a93b502 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -13,6 +13,7 @@ using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols.Identify.Dto; +using Nethermind.Libp2p.Core.Exceptions; namespace Nethermind.Libp2p.Protocols; @@ -51,20 +52,24 @@ public async Task DialAsync(IChannel channel, ISessionContext context) Identify.Dto.Identify identify = await channel.ReadPrefixedProtobufAsync(Identify.Dto.Identify.Parser); _logger?.LogInformation("Received peer info: {identify}", identify); - context.RemotePeer.Identity = new Identity(PublicKey.Parser.ParseFrom(identify.PublicKey)); if (_peerStore is not null && identify.SignedPeerRecord is not null) { - if (!VerifyPeerRecord(identify.SignedPeerRecord, context.RemotePeer.Identity)) + if (!VerifyPeerRecord(identify.SignedPeerRecord, context.State.RemotePublicKey)) { throw new PeerConnectionException(); } - _peerStore.GetPeerInfo(context.RemotePeer.Identity.PeerId).SignedPeerRecord = identify.SignedPeerRecord; + + if (context.State.RemotePeerId is null) + { + throw new Libp2pException("No remote peer id is set"); + } + _peerStore.GetPeerInfo(context.State.RemotePeerId).SignedPeerRecord = identify.SignedPeerRecord; + + _logger?.LogInformation("Confirmed peer record: {peerId}", context.State.RemotePeerId); } - _logger?.LogInformation("Received peer info: {identify}", identity); - context.State.RemotePublicKey = PublicKey.Parser.ParseFrom(identity.PublicKey); - if (context.State.RemotePublicKey.ToByteString() != identity.PublicKey) + if (context.State.RemotePublicKey.ToByteString() != identify.PublicKey) { throw new PeerConnectionException(); } @@ -78,11 +83,11 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) { ProtocolVersion = _protocolVersion, AgentVersion = _agentVersion, - PublicKey = context.LocalPeer.Identity.PublicKey.ToByteString(), - ListenAddrs = { ByteString.CopyFrom(ToEndpoint(context.LocalEndpoint).ToBytes()) }, - ObservedAddr = ByteString.CopyFrom(State.RemoteAddress!.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto).ToBytes()), + PublicKey = context.Peer.Identity.PublicKey.ToByteString(), + ListenAddrs = { context.Peer.ListenAddresses.Select(x => ByteString.CopyFrom(x.ToBytes())) }, + ObservedAddr = ByteString.CopyFrom(context.State.RemoteAddress!.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto).ToBytes()), Protocols = { _protocolStackSettings.Protocols!.Select(r => r.Key.Protocol).OfType().Select(p => p.Id) }, - SignedPeerRecord = CreateSignedEnvelope(context.LocalPeer.Identity, [context.LocalPeer.Address], 1), + SignedPeerRecord = CreateSignedEnvelope(context.Peer.Identity, context.Peer.ListenAddresses.ToArray(), 1), }; ByteString[] endpoints = context.Peer.ListenAddresses.Where(a => !a.ToEndPoint().Address.IsPrivate()).Select(a => a.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto)).Select(a => ByteString.CopyFrom(a.ToBytes())).ToArray(); @@ -95,8 +100,9 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) _logger?.LogDebug("Sent peer info {identify}", identify); } - private static bool VerifyPeerRecord(ByteString signedPeerRecordBytes, Identity identity) + private static bool VerifyPeerRecord(ByteString signedPeerRecordBytes, PublicKey remotePublicKey) { + Identity identity = new(remotePublicKey); SignedEnvelope envelope = SignedEnvelope.Parser.ParseFrom(signedPeerRecordBytes); if (envelope.PayloadType?.Take(2).SequenceEqual(Libp2pPeerRecordAsArray) is not true) diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index ae0a2b36..170a01b4 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -10,7 +10,6 @@ using Multiformats.Address.Protocols; using Multiformats.Address.Net; using Nethermind.Libp2p.Core.Exceptions; -using System.Threading.Channels; namespace Nethermind.Libp2p.Protocols; diff --git a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs index d0c41f1c..f4f0a948 100644 --- a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs @@ -10,6 +10,7 @@ using Multiformats.Address; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Exceptions; namespace Nethermind.Libp2p.Protocols; @@ -23,17 +24,23 @@ public class MDnsDiscoveryProtocol(PeerStore peerStore, ILoggerFactory? loggerFa private string PeerName = null!; - public async Task DiscoverAsync(IPeer peer, CancellationToken token = default) + public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, CancellationToken token = default) { ObservableCollection peers = []; ServiceDiscovery sd = new(); + string? localPeerId = localPeerAddrs.First().GetPeerId()?.ToString(); + + if (localPeerId is null) + { + throw new Libp2pException(); + } try { PeerName = RandomString(32); ServiceProfile service = new(PeerName, ServiceNameOverride ?? ServiceName, 0); - foreach (var localPeerAddr in peer.ListenAddresses) + foreach (Multiaddress localPeerAddr in localPeerAddrs) { if (localPeerAddr.Get().ToString() == "0.0.0.0") { @@ -50,10 +57,7 @@ public async Task DiscoverAsync(IPeer peer, CancellationToken token = default) service.Resources.Add(new TXTRecord() { Name = service.FullyQualifiedName, - Strings = new List - { - $"dnsaddr={localPeerAddr}" - } + Strings = [$"dnsaddr={localPeerAddr}"] }); } } @@ -72,7 +76,7 @@ public async Task DiscoverAsync(IPeer peer, CancellationToken token = default) .Select(x => x.Strings.Where(x => x.StartsWith("dnsaddr"))) .SelectMany(x => x).Select(x => Multiaddress.Decode(x.Replace("dnsaddr=", ""))).ToArray(); _logger?.LogTrace("Inst disc {0}, nmsg: {1}", e.ServiceInstanceName, e.Message); - if (records.Length != 0 && !peers.Contains(records[0]) && peer.Identity.PeerId.ToString() != records[0].Get().ToString()) + if (records.Length != 0 && !peers.Contains(records[0]) && localPeerId != records[0].Get().ToString()) { List peerAddresses = new(); foreach (Multiaddress peer in records) diff --git a/src/libp2p/Libp2p.Protocols.Multistream.Tests/Usings.cs b/src/libp2p/Libp2p.Protocols.Multistream.Tests/Usings.cs index 852eb690..e160e2a1 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream.Tests/Usings.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream.Tests/Usings.cs @@ -1,7 +1,4 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -global using Nethermind.Libp2p.Core; -global using Nethermind.Libp2p.Core.TestsBase; -global using NSubstitute; global using NUnit.Framework; diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index 52e5fcaa..1cbd0006 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -50,14 +50,10 @@ public async Task DialAsync(IChannel channel, IConnectionContext context) if (context.UpgradeOptions?.SelectedProtocol is not null) { - selected = context.SpecificProtocolRequest.SubProtocol; - - context.SpecificProtocolRequest = null; _logger?.LogDebug($"Proposing just {context.UpgradeOptions.SelectedProtocol}"); if (await DialProtocol(context.UpgradeOptions.SelectedProtocol) == true) { selected = context.UpgradeOptions.SelectedProtocol; - return; } } else diff --git a/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs index fa53f1f0..1224b515 100644 --- a/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs @@ -1,123 +1,123 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT +//// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +//// SPDX-License-Identifier: MIT -namespace Nethermind.Libp2p.Protocols.Noise.Tests; - -[TestFixture] -[Parallelizable(scope: ParallelScope.All)] -public class NoiseProtocolTests -{ - [Test] - public async Task Test_ConnectionEstablished_AfterHandshake() - { - // Arrange - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - IPeerContext listenerContext = Substitute.For(); +//namespace Nethermind.Libp2p.Protocols.Noise.Tests; + +//[TestFixture] +//[Parallelizable(scope: ParallelScope.All)] +//public class NoiseProtocolTests +//{ +// [Test] +// public async Task Test_ConnectionEstablished_AfterHandshake() +// { +// // Arrange +// IChannel downChannel = new TestChannel(); +// IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); +// IChannelFactory channelFactory = Substitute.For(); +// IPeerContext peerContext = Substitute.For(); +// IPeerContext listenerContext = Substitute.For(); - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); +// IProtocol? proto1 = Substitute.For(); +// proto1.Id.Returns("proto1"); - IProtocol? proto2 = Substitute.For(); - proto2.Id.Returns("proto2"); +// IProtocol? proto2 = Substitute.For(); +// proto2.Id.Returns("proto2"); - channelFactory.SubProtocols.Returns([proto1, proto2]); +// channelFactory.SubProtocols.Returns([proto1, proto2]); - TestChannel upChannel = new TestChannel(); - channelFactory.SubDial(Arg.Any(), Arg.Any()) - .Returns(upChannel); +// TestChannel upChannel = new TestChannel(); +// channelFactory.SubDial(Arg.Any(), Arg.Any()) +// .Returns(upChannel); - TestChannel listenerUpChannel = new TestChannel(); +// TestChannel listenerUpChannel = new TestChannel(); - channelFactory.SubListen(Arg.Any(), Arg.Any()) - .Returns(listenerUpChannel); +// channelFactory.SubListen(Arg.Any(), Arg.Any()) +// .Returns(listenerUpChannel); - var i_multiplexerSettings = new MultiplexerSettings(); - var r_multiplexerSettings = new MultiplexerSettings(); - r_multiplexerSettings.Add(proto2); - r_multiplexerSettings.Add(proto1); - i_multiplexerSettings.Add(proto1); +// var i_multiplexerSettings = new MultiplexerSettings(); +// var r_multiplexerSettings = new MultiplexerSettings(); +// r_multiplexerSettings.Add(proto2); +// r_multiplexerSettings.Add(proto1); +// i_multiplexerSettings.Add(proto1); - NoiseProtocol proto_initiator = new(i_multiplexerSettings); - NoiseProtocol proto_responder = new(r_multiplexerSettings); +// NoiseProtocol proto_initiator = new(i_multiplexerSettings); +// NoiseProtocol proto_responder = new(r_multiplexerSettings); - peerContext.LocalPeer.Identity.Returns(new Identity()); - listenerContext.LocalPeer.Identity.Returns(new Identity()); +// peerContext.LocalPeer.Identity.Returns(new Identity()); +// listenerContext.LocalPeer.Identity.Returns(new Identity()); - string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); - Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; - peerContext.RemotePeer.Address.Returns(localAddr); +// string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); +// Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; +// peerContext.RemotePeer.Address.Returns(localAddr); - string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); - Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; - listenerContext.RemotePeer.Address.Returns(listenerAddr); +// string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); +// Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; +// listenerContext.RemotePeer.Address.Returns(listenerAddr); - // Act - Task listenTask = proto_responder.ListenAsync(downChannel, channelFactory, listenerContext); - Task dialTask = proto_initiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); +// // Act +// Task listenTask = proto_responder.ListenAsync(downChannel, channelFactory, listenerContext); +// Task dialTask = proto_initiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - int sent = 42; - ValueTask writeTask = upChannel.Reverse().WriteVarintAsync(sent); - int received = await listenerUpChannel.Reverse().ReadVarintAsync(); - await writeTask; +// int sent = 42; +// ValueTask writeTask = upChannel.Reverse().WriteVarintAsync(sent); +// int received = await listenerUpChannel.Reverse().ReadVarintAsync(); +// await writeTask; - await upChannel.CloseAsync(); - await listenerUpChannel.CloseAsync(); - await downChannel.CloseAsync(); +// await upChannel.CloseAsync(); +// await listenerUpChannel.CloseAsync(); +// await downChannel.CloseAsync(); - Assert.That(received, Is.EqualTo(sent)); - } +// Assert.That(received, Is.EqualTo(sent)); +// } - [Test] - public async Task Test_ConnectionEstablished_With_PreSelectedMuxer() - { - // Arrange - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - IPeerContext listenerContext = Substitute.For(); +// [Test] +// public async Task Test_ConnectionEstablished_With_PreSelectedMuxer() +// { +// // Arrange +// IChannel downChannel = new TestChannel(); +// IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); +// IChannelFactory channelFactory = Substitute.For(); +// IPeerContext peerContext = Substitute.For(); +// IPeerContext listenerContext = Substitute.For(); - IProtocol? proto1 = Substitute.For(); - proto1.Id.Returns("proto1"); +// IProtocol? proto1 = Substitute.For(); +// proto1.Id.Returns("proto1"); - IProtocol? proto2 = Substitute.For(); - proto2.Id.Returns("proto2"); +// IProtocol? proto2 = Substitute.For(); +// proto2.Id.Returns("proto2"); - channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); +// channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); - var i_multiplexerSettings = new MultiplexerSettings(); - var r_multiplexerSettings = new MultiplexerSettings(); - r_multiplexerSettings.Add(proto2); - r_multiplexerSettings.Add(proto1); - i_multiplexerSettings.Add(proto1); +// var i_multiplexerSettings = new MultiplexerSettings(); +// var r_multiplexerSettings = new MultiplexerSettings(); +// r_multiplexerSettings.Add(proto2); +// r_multiplexerSettings.Add(proto1); +// i_multiplexerSettings.Add(proto1); - NoiseProtocol proto_initiator = new(i_multiplexerSettings); - NoiseProtocol proto_responder = new(r_multiplexerSettings); +// NoiseProtocol proto_initiator = new(i_multiplexerSettings); +// NoiseProtocol proto_responder = new(r_multiplexerSettings); - peerContext.LocalPeer.Identity.Returns(new Identity()); - listenerContext.LocalPeer.Identity.Returns(new Identity()); - string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); - Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; - peerContext.RemotePeer.Address.Returns(localAddr); - - string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); - Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; - listenerContext.RemotePeer.Address.Returns(listenerAddr); +// peerContext.LocalPeer.Identity.Returns(new Identity()); +// listenerContext.LocalPeer.Identity.Returns(new Identity()); +// string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); +// Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; +// peerContext.RemotePeer.Address.Returns(localAddr); + +// string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); +// Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; +// listenerContext.RemotePeer.Address.Returns(listenerAddr); - // Act - Task listenTask = proto_responder.ListenAsync(downChannel, channelFactory, listenerContext); - Task dialTask = proto_initiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); +// // Act +// Task listenTask = proto_responder.ListenAsync(downChannel, channelFactory, listenerContext); +// Task dialTask = proto_initiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - await Task.Delay(TimeSpan.FromSeconds(2)); +// await Task.Delay(TimeSpan.FromSeconds(2)); - // Assert - Assert.That(peerContext.SpecificProtocolRequest.SubProtocol, Is.EqualTo(proto1)); +// // Assert +// Assert.That(peerContext.SpecificProtocolRequest.SubProtocol, Is.EqualTo(proto1)); - // Cleanup - await downChannel.CloseAsync(); - } -} +// // Cleanup +// await downChannel.CloseAsync(); +// } +//} diff --git a/src/libp2p/Libp2p.Protocols.Noise.Tests/Usings.cs b/src/libp2p/Libp2p.Protocols.Noise.Tests/Usings.cs index 07d11c14..29387723 100644 --- a/src/libp2p/Libp2p.Protocols.Noise.Tests/Usings.cs +++ b/src/libp2p/Libp2p.Protocols.Noise.Tests/Usings.cs @@ -1,9 +1,3 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -global using Nethermind.Libp2p.Core; -global using Nethermind.Libp2p.Core.TestsBase; -global using NSubstitute; -global using NUnit.Framework; -global using Multiformats.Address; -global using System.Threading.Tasks; diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index 6e01fd76..ffb67b33 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -64,12 +64,14 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) .Where(m => !string.IsNullOrEmpty(m)) .ToList(); IProtocol? commonMuxer = multiplexerSettings?.Multiplexers.FirstOrDefault(m => responderMuxers.Contains(m.Id)); + + UpgradeOptions? upgradeOptions = null; + if (commonMuxer is not null) { - context.SpecificProtocolRequest = new ChannelRequest + upgradeOptions = new UpgradeOptions { - SubProtocol = commonMuxer, - CompletionSource = context.SpecificProtocolRequest?.CompletionSource + SelectedProtocol = commonMuxer, }; } @@ -105,7 +107,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) _logger?.LogDebug("Established connection to {peer}", context.State.RemoteAddress); - IChannel upChannel = context.Upgrade(); + IChannel upChannel = context.Upgrade(upgradeOptions); await ExchangeData(transport, downChannel, upChannel); @@ -159,12 +161,13 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) List initiatorMuxers = msg2Decoded.Extensions.StreamMuxers.Where(m => !string.IsNullOrEmpty(m)).ToList(); IProtocol? commonMuxer = multiplexerSettings?.Multiplexers.FirstOrDefault(m => initiatorMuxers.Contains(m.Id)); + UpgradeOptions? upgradeOptions = null; + if (commonMuxer is not null) { - context.SpecificProtocolRequest = new ChannelRequest + upgradeOptions = new UpgradeOptions { - SubProtocol = commonMuxer, - CompletionSource = context.SpecificProtocolRequest?.CompletionSource + SelectedProtocol = commonMuxer, }; } @@ -175,7 +178,7 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) _logger?.LogDebug("Established connection to {peer}", context.State.RemoteAddress); - IChannel upChannel = context.Upgrade(); + IChannel upChannel = context.Upgrade(upgradeOptions); await ExchangeData(transport, downChannel, upChannel); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs index a4103fcf..d285eada 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -12,10 +12,7 @@ using System.Text; int totalCount = 7; -TestContextLoggerFactory fac = new(); -// There is common communication point -ChannelBus commonBus = new(fac); -ILocalPeer[] peers = new ILocalPeer[totalCount]; +IPeer[] peers = new IPeer[totalCount]; PeerStore[] peerStores = new PeerStore[totalCount]; PubsubRouter[] routers = new PubsubRouter[totalCount]; @@ -24,20 +21,21 @@ { // But we create a seprate setup for every peer ServiceProvider sp = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) - .AddSingleton(sp => fac) + .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) + .AddSingleton(sp => new TestContextLoggerFactory()) .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(sp => new Settings { LowestDegree = 1, Degree = 2, LazyDegree = 2, HighestDegree = 3 }) .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); IPeerFactory peerFactory = sp.GetService()!; - ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); + IPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = routers[i] = sp.GetService()!; PubsubPeerDiscoveryProtocol disc = new(router, peerStores[i] = sp.GetService()!, new PubsubPeerDiscoverySettings() { Interval = 300 }, peer); - await peer.ListenAsync(TestPeers.Multiaddr(i)); + await peer.StartListenAsync([TestPeers.Multiaddr(i)]); _ = router.RunAsync(peer, sp.GetService()); //_ = disc.DiscoverAsync(peer.Address); } @@ -49,11 +47,11 @@ routers[i].GetTopic("test"); } -Console.WriteLine($"Center: {peers[0].Address}"); +Console.WriteLine($"Center: {string.Join(",", peers[0].ListenAddresses)}"); for (int i = 1; i < peers.Length; i++) { - peerStores[i].Discover([peers[0].Address]); + peerStores[i].Discover(peers[0].ListenAddresses.ToArray()); } await Task.Delay(10000); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index f594fb02..f0465548 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -23,13 +23,10 @@ public async Task Test_Peer_is_in_fpeers() const string commonTopic = "topic1"; - ILocalPeer peer = Substitute.For(); - peer.Address.Returns(localPeerAddr); - peer.Identity.Returns(TestPeers.Identity(2)); - peer.DialAsync(discoveredPeerAddress, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddress)); IPeer peer = Substitute.For(); peer.ListenAddresses.Returns([localPeerAddr]); - peer.DialAsync(discoveredPeer, Arg.Any()).Returns(new TestRemotePeer(discoveredPeer)); + peer.Identity.Returns(TestPeers.Identity(2)); + peer.DialAsync(discoveredPeerAddress, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddress)); CancellationToken token = default; List sentRpcs = new(); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs index 19b82651..fb8d0209 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs @@ -1,56 +1,52 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Multiformats.Address; -using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Protocols.Pubsub.Dto; - namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; [TestFixture] public class GossipsubProtocolTests { - [Test] - public async Task Test_New_messages_are_sent_to_mesh_only() - { - PeerStore peerStore = new(); - PubsubRouter router = new(peerStore); - Settings settings = new() { HeartbeatInterval = int.MaxValue }; - IRoutingStateContainer state = router; - int peerCount = Settings.Default.HighestDegree + 1; - const string commonTopic = "topic1"; - - IPeer peer = new TestLocalPeer(); - TestDiscoveryProtocol discovery = new(); - CancellationToken token = default; - List sentRpcs = []; - - _ = router.RunAsync(peer, token: token); - router.GetTopic(commonTopic); - Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); - Assert.That(state.GossipsubPeers.Keys, Has.Member(commonTopic)); - - TaskCompletionSource tcs = new(); - - foreach (int index in Enumerable.Range(1, peerCount)) - { - Multiaddress discoveredPeer = TestPeers.Multiaddr(index); - PeerId peerId = TestPeers.PeerId(index); - - peerStore.Discover([discoveredPeer]); - router.OutboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, sentRpcs.Add); - router.InboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, tcs.Task, () => Task.CompletedTask); - await router.OnRpc(peerId, new Rpc().WithTopics([commonTopic], [])); - } - - await router.Heartbeat(); - - Assert.Multiple(() => - { - Assert.That(state.GossipsubPeers[commonTopic], Has.Count.EqualTo(peerCount)); - Assert.That(state.Mesh[commonTopic], Has.Count.EqualTo(Settings.Default.Degree)); - }); - - tcs.SetResult(); - } + //[Test] + //public async Task Test_New_messages_are_sent_to_mesh_only() + //{ + // PeerStore peerStore = new(); + // PubsubRouter router = new(peerStore); + // Settings settings = new() { HeartbeatInterval = int.MaxValue }; + // IRoutingStateContainer state = router; + // int peerCount = Settings.Default.HighestDegree + 1; + // const string commonTopic = "topic1"; + + // IPeer peer = new LocalPeerStub(); + // TestDiscoveryProtocol discovery = new(); + // CancellationToken token = default; + // List sentRpcs = []; + + // _ = router.RunAsync(peer, token: token); + // router.GetTopic(commonTopic); + // Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); + // Assert.That(state.GossipsubPeers.Keys, Has.Member(commonTopic)); + + // TaskCompletionSource tcs = new(); + + // foreach (int index in Enumerable.Range(1, peerCount)) + // { + // Multiaddress discoveredPeer = TestPeers.Multiaddr(index); + // PeerId peerId = TestPeers.PeerId(index); + + // peerStore.Discover([discoveredPeer]); + // router.OutboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, sentRpcs.Add); + // router.InboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, tcs.Task, () => Task.CompletedTask); + // await router.OnRpc(peerId, new Rpc().WithTopics([commonTopic], [])); + // } + + // await router.Heartbeat(); + + // Assert.Multiple(() => + // { + // Assert.That(state.GossipsubPeers[commonTopic], Has.Count.EqualTo(peerCount)); + // Assert.That(state.Mesh[commonTopic], Has.Count.EqualTo(Settings.Default.Degree)); + // }); + + // tcs.SetResult(); + //} } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs index 2522d82b..d72f350a 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs @@ -1,9 +1,6 @@ //// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited //// SPDX-License-Identifier: MIT -using Multiformats.Address; -using Nethermind.Libp2p.Core.Discovery; - //namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; [TestFixture] diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs index 865b4d24..e9c54091 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs @@ -120,7 +120,7 @@ public void Publish(string topicId, byte[] message) ulong seqNo = this.seqNo++; Span seqNoBytes = stackalloc byte[8]; BinaryPrimitives.WriteUInt64BigEndian(seqNoBytes, seqNo); - Rpc rpc = new Rpc().WithMessages(topicId, seqNo, localPeer!.Address.GetPeerId()!.Bytes, message, localPeer.Identity); + Rpc rpc = new Rpc().WithMessages(topicId, seqNo, localPeer!.Identity.PeerId.Bytes, message, localPeer.Identity); foreach (PeerId peerId in fPeers[topicId]) { diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index df694041..f80a2978 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -9,7 +9,6 @@ using Nethermind.Libp2p.Core.Dto; using Nethermind.Libp2p.Protocols.Identify.Dto; using Nethermind.Libp2p.Protocols.Pubsub.Dto; -using Org.BouncyCastle.Tls; using System.Collections.Concurrent; using System.Diagnostics; @@ -34,7 +33,7 @@ public partial class PubsubRouter(PeerStore store, ILoggerFactory? loggerFactory public override string ToString() { //{string.Join("|", peerState.Select(x => $"{x.Key}:{x.Value.SendRpc is not null}"))} - return $"Router#{routerId}: {localPeer?.Address.GetPeerId() ?? "null"}, " + + return $"Router#{routerId}: {localPeer?.Identity.PeerId ?? "null"}, " + $"peers: {peerState.Count(x => x.Value.SendRpc is not null)}/{peerState.Count}, " + $"mesh: {string.Join("|", mesh.Select(m => $"{m.Key}:{m.Value.Count}"))}, " + $"fanout: {string.Join("|", fanout.Select(m => $"{m.Key}:{m.Value.Count}"))}, " + @@ -138,7 +137,6 @@ public Action? SendRpc private TtlCache<(PeerId, MessageId)> dontWantMessages; private IPeer? localPeer; - private ManagedPeer peer; private readonly ILogger? logger = loggerFactory?.CreateLogger(); // all floodsub peers in topics @@ -171,7 +169,7 @@ static PubsubRouter() public async Task RunAsync(IPeer localPeer, Settings? settings = null, CancellationToken token = default) { - logger?.LogDebug($"Running pubsub for {localPeer.Address}"); + logger?.LogDebug($"Running pubsub for {string.Join(",", localPeer.ListenAddresses)}"); if (this.localPeer is not null) { @@ -183,7 +181,6 @@ public async Task RunAsync(IPeer localPeer, Settings? settings = null, Cancellat limboMessageCache = new(this.settings.MessageCacheTtl); dontWantMessages = new(this.settings.MessageCacheTtl); - LocalPeerId = localPeer.Identity.PeerId; logger?.LogInformation("Started"); store.OnNewPeer += (addrs) => @@ -265,7 +262,7 @@ public Task Heartbeat() { foreach (KeyValuePair> mesh in mesh) { - logger?.LogDebug($"MESH({localPeer!.Address.GetPeerId()}) {mesh.Key}: {mesh.Value.Count} ({mesh.Value})"); + logger?.LogDebug($"MESH({localPeer!.Identity.PeerId}) {mesh.Key}: {mesh.Value.Count} ({mesh.Value})"); if (mesh.Value.Count < settings.LowestDegree) { PeerId[] peersToGraft = gPeers[mesh.Key] diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs index e9f98a90..30709d50 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs @@ -21,29 +21,31 @@ public async Task Test_PeersConnect() ChannelBus commonBus = new(); ServiceProvider sp1 = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) + .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) .AddSingleton(sp => new TestContextLoggerFactory()) .AddSingleton() .AddSingleton() + .AddSingleton(commonBus) .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); ServiceProvider sp2 = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) + .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) .AddSingleton(sp => new TestContextLoggerFactory()) .AddSingleton() .AddSingleton() + .AddSingleton(commonBus) .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); - ILocalPeer peerA = sp1.GetService()!.Create(TestPeers.Identity(1)); - await peerA.ListenAsync(TestPeers.Multiaddr(1)); - ILocalPeer peerB = sp2.GetService()!.Create(TestPeers.Identity(2)); - await peerB.ListenAsync(TestPeers.Multiaddr(2)); + IPeer peerA = sp1.GetService()!.Create(TestPeers.Identity(1)); + await peerA.StartListenAsync([TestPeers.Multiaddr(1)]); + IPeer peerB = sp2.GetService()!.Create(TestPeers.Identity(2)); + await peerB.StartListenAsync([TestPeers.Multiaddr(2)]); - IRemotePeer remotePeerB = await peerA.DialAsync(peerB.Address); + ISession remotePeerB = await peerA.DialAsync(peerB.ListenAddresses.ToArray()); await remotePeerB.DialAsync(); } @@ -54,7 +56,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() TestContextLoggerFactory fac = new(); // There is common communication point ChannelBus commonBus = new(fac); - ILocalPeer[] peers = new ILocalPeer[totalCount]; + IPeer[] peers = new IPeer[totalCount]; PeerStore[] peerStores = new PeerStore[totalCount]; PubsubRouter[] routers = new PubsubRouter[totalCount]; @@ -62,29 +64,29 @@ public async Task Test_ConnectionEstablished_AfterHandshake() { // But we create a seprate setup for every peer ServiceProvider sp = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(commonBus, sp).AddAppLayerProtocol()) .AddSingleton(sp => fac) .AddSingleton() .AddSingleton() + .AddSingleton(commonBus) .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); IPeerFactory peerFactory = sp.GetService()!; - ILocalPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); + IPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = routers[i] = sp.GetService()!; PeerStore peerStore = sp.GetService()!; PubsubPeerDiscoveryProtocol disc = new(router, peerStore, new PubsubPeerDiscoverySettings() { Interval = 300 }, peer); - await peer.ListenAsync(TestPeers.Multiaddr(i)); + await peer.StartListenAsync([TestPeers.Multiaddr(i)]); _ = router.RunAsync(peer); peerStores[i] = peerStore; - _ = disc.DiscoverAsync(peers[i].Address); + _ = disc.DiscoverAsync(peers[i].ListenAddresses); } await Task.Delay(1000); for (int i = 0; i < peers.Length; i++) { - peerStores[i].Discover([peers[(i + 1) % totalCount].Address]); + peerStores[i].Discover(peers[(i + 1) % totalCount].ListenAddresses.ToArray()); } await Task.Delay(30000); @@ -108,13 +110,13 @@ public async Task Test_ConnectionEstablished_AfterHandshak3e() // discover in circle for (int i = 0; i < setup.Peers.Count; i++) { - setup.PeerStores[i].Discover([setup.Peers[(i + 1) % setup.Peers.Count].Address]); + setup.PeerStores[i].Discover(setup.Peers[(i + 1) % setup.Peers.Count].ListenAddresses.ToArray()); } for (int i = 0; i < setup.Peers.Count; i++) { discoveries[i] = new(setup.Routers[i], setup.PeerStores[i], new PubsubPeerDiscoverySettings() { Interval = int.MaxValue }, setup.Peers[i]); - _ = discoveries[i].DiscoverAsync(setup.Peers[i].Address); + _ = discoveries[i].DiscoverAsync(setup.Peers[i].ListenAddresses); } await Task.Delay(100); diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs index 00e24b7f..f2a51c40 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs @@ -11,7 +11,7 @@ class PubsubTestSetup static TestContextLoggerFactory fac = new TestContextLoggerFactory(); public ChannelBus CommonBus { get; } = new(fac); - public Dictionary Peers { get; } = new(); + public Dictionary Peers { get; } = new(); public Dictionary PeerStores { get; } = new(); public Dictionary Routers { get; } = new(); @@ -29,19 +29,20 @@ public async Task AddAsync(int count) }; ServiceProvider sp = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(CommonBus, sp).AddAppLayerProtocol()) + .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) .AddSingleton((Func)(sp => fac)) .AddSingleton() .AddSingleton(settings) + .AddSingleton(CommonBus) .AddSingleton() .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); IPeerFactory peerFactory = sp.GetService()!; - ILocalPeer peer = Peers[i] = peerFactory.Create(TestPeers.Identity(i)); + IPeer peer = Peers[i] = peerFactory.Create(TestPeers.Identity(i)); PubsubRouter router = Routers[i] = sp.GetService()!; PeerStore peerStore = sp.GetService()!; - await peer.ListenAsync(TestPeers.Multiaddr(i)); + await peer.StartListenAsync([TestPeers.Multiaddr(i)]); _ = router.RunAsync(peer); PeerStores[i] = peerStore; } diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs index 6e0cc306..8a066f2f 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs @@ -12,17 +12,21 @@ public class PubsubPeerDiscoverySettings public bool ListenOnly { get; set; } } -public class PubsubPeerDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore peerStore, PubsubPeerDiscoverySettings settings, ILocalPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol +public class PubsubPeerDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore peerStore, PubsubPeerDiscoverySettings settings, IPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol { private readonly PubsubRouter _pubSubRouter = pubSubRouter; - private Multiaddress? _localPeerAddr; + private IReadOnlyList? _localPeerAddrs; + private PeerId? localPeerId; private ITopic[]? topics; private readonly PubsubPeerDiscoverySettings _settings = settings; private ILogger? logger = loggerFactory?.CreateLogger(); - public async Task DiscoverAsync(Multiaddress localPeerAddr, CancellationToken token = default) + public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, CancellationToken token = default) { - _localPeerAddr = localPeerAddr; + _localPeerAddrs = localPeerAddrs; + localPeerId = localPeerAddrs.First().GetPeerId(); + + topics = _settings.Topics.Select(topic => { ITopic subscription = _pubSubRouter.GetTopic(topic); @@ -60,7 +64,7 @@ internal void BroadcastPeerInfo() topic.Publish(new Peer { PublicKey = peer.Identity.PublicKey.ToByteString(), - Addrs = { ByteString.CopyFrom(peer.Address.ToBytes()) }, + Addrs = { peer.ListenAddresses.Select(a => ByteString.CopyFrom(a.ToBytes())) }, }); } } @@ -72,11 +76,11 @@ private void OnPeerMessage(byte[] msg) Peer peer = Peer.Parser.ParseFrom(msg); Multiaddress[] addrs = [.. peer.Addrs.Select(a => Multiaddress.Decode(a.ToByteArray()))]; PeerId? remotePeerId = addrs.FirstOrDefault()?.GetPeerId(); - if (remotePeerId is not null && remotePeerId != _localPeerAddr?.GetPeerId()!) + if (remotePeerId is not null && remotePeerId != localPeerId!) { peerStore.Discover(addrs); } - logger?.LogDebug($"{_localPeerAddr}: New peer discovered {peer}"); + logger?.LogDebug($"New peer discovered {peer}"); } catch (Exception ex) { diff --git a/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs index ad20478f..3672f3fe 100644 --- a/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs @@ -7,58 +7,58 @@ namespace Nethermind.Libp2p.Protocols.TLS.Tests; [Parallelizable(scope: ParallelScope.All)] public class TlsProtocolTests { - [Test] - [Ignore("Infinite loop")] - public async Task Test_ConnectionEstablished_AfterHandshake() - { - // Arrange - IChannel downChannel = new TestChannel(); - IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - IChannelFactory channelFactory = Substitute.For(); - IPeerContext peerContext = Substitute.For(); - IPeerContext listenerContext = Substitute.For(); - ILoggerFactory loggerFactory = Substitute.For(); + //[Test] + //[Ignore("Infinite loop")] + //public async Task Test_ConnectionEstablished_AfterHandshake() + //{ + // // Arrange + // IChannel downChannel = new TestChannel(); + // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + // IChannelFactory channelFactory = Substitute.For(); + // IPeerContext peerContext = Substitute.For(); + // IPeerContext listenerContext = Substitute.For(); + // ILoggerFactory loggerFactory = Substitute.For(); - TestChannel upChannel = new TestChannel(); - channelFactory.SubDial(Arg.Any(), Arg.Any()) - .Returns(upChannel); + // TestChannel upChannel = new TestChannel(); + // channelFactory.SubDial(Arg.Any(), Arg.Any()) + // .Returns(upChannel); - TestChannel listenerUpChannel = new TestChannel(); - channelFactory.SubListen(Arg.Any(), Arg.Any()) - .Returns(listenerUpChannel); + // TestChannel listenerUpChannel = new TestChannel(); + // channelFactory.SubListen(Arg.Any(), Arg.Any()) + // .Returns(listenerUpChannel); - peerContext.LocalPeer.Identity.Returns(new Identity()); - listenerContext.LocalPeer.Identity.Returns(new Identity()); + // peerContext.LocalPeer.Identity.Returns(new Identity()); + // listenerContext.LocalPeer.Identity.Returns(new Identity()); - string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); - Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; - peerContext.LocalPeer.Address.Returns(localAddr); - listenerContext.RemotePeer.Address.Returns(localAddr); + // string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); + // Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; + // peerContext.LocalPeer.Address.Returns(localAddr); + // listenerContext.RemotePeer.Address.Returns(localAddr); - string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); - Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; - peerContext.RemotePeer.Address.Returns(listenerAddr); - listenerContext.LocalPeer.Address.Returns(listenerAddr); + // string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); + // Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; + // peerContext.RemotePeer.Address.Returns(listenerAddr); + // listenerContext.LocalPeer.Address.Returns(listenerAddr); - var i_multiplexerSettings = new MultiplexerSettings(); - var r_multiplexerSettings = new MultiplexerSettings(); - TlsProtocol tlsProtocolListener = new TlsProtocol(i_multiplexerSettings, loggerFactory); - TlsProtocol tlsProtocolInitiator = new TlsProtocol(r_multiplexerSettings, loggerFactory); + // var i_multiplexerSettings = new MultiplexerSettings(); + // var r_multiplexerSettings = new MultiplexerSettings(); + // TlsProtocol tlsProtocolListener = new TlsProtocol(i_multiplexerSettings, loggerFactory); + // TlsProtocol tlsProtocolInitiator = new TlsProtocol(r_multiplexerSettings, loggerFactory); - // Act - Task listenTask = tlsProtocolListener.ListenAsync(downChannel, channelFactory, listenerContext); - Task dialTask = tlsProtocolInitiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + // // Act + // Task listenTask = tlsProtocolListener.ListenAsync(downChannel, channelFactory, listenerContext); + // Task dialTask = tlsProtocolInitiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - int sent = 42; - ValueTask writeTask = listenerUpChannel.Reverse().WriteVarintAsync(sent); - int received = await upChannel.Reverse().ReadVarintAsync(); - await writeTask; + // int sent = 42; + // ValueTask writeTask = listenerUpChannel.Reverse().WriteVarintAsync(sent); + // int received = await upChannel.Reverse().ReadVarintAsync(); + // await writeTask; - await upChannel.CloseAsync(); - await listenerUpChannel.CloseAsync(); - await downChannel.CloseAsync(); + // await upChannel.CloseAsync(); + // await listenerUpChannel.CloseAsync(); + // await downChannel.CloseAsync(); - // Assert - Assert.That(received, Is.EqualTo(sent)); - } + // // Assert + // Assert.That(received, Is.EqualTo(sent)); + //} } diff --git a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs index 5a261fa0..123db134 100644 --- a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs @@ -12,7 +12,7 @@ namespace Nethermind.Libp2p.Protocols; -public class TlsProtocol(MultiplexerSettings? multiplexerSettings = null, ILoggerFactory? loggerFactory = null) : IProtocol +public class TlsProtocol(MultiplexerSettings? multiplexerSettings = null, ILoggerFactory? loggerFactory = null) : IConnectionProtocol { private readonly ECDsa _sessionKey = ECDsa.Create(); private readonly ILogger? _logger = loggerFactory?.CreateLogger(); @@ -21,23 +21,19 @@ public class TlsProtocol(MultiplexerSettings? multiplexerSettings = null, ILogge public SslApplicationProtocol? LastNegotiatedApplicationProtocol { get; private set; } public string Id => "/tls/1.0.0"; - public async Task ListenAsync(IChannel downChannel, IChannelFactory? channelFactory, IPeerContext context) + public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { - _logger?.LogInformation("Starting ListenAsync: PeerId {LocalPeerId}", context.LocalPeer.Address.Get()); - if (channelFactory is null) - { - throw new ArgumentException("Protocol is not properly instantiated"); - } + _logger?.LogInformation("Starting ListenAsync: PeerId {LocalPeerId}", context.Peer.Identity.PeerId); Stream str = new ChannelStream(downChannel); - X509Certificate certificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.LocalPeer.Identity); - _logger?.LogDebug("Successfully created X509Certificate for PeerId {LocalPeerId}. Certificate Subject: {Subject}, Issuer: {Issuer}", context.LocalPeer.Address.Get(), certificate.Subject, certificate.Issuer); + X509Certificate certificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity); + _logger?.LogDebug("Successfully created X509Certificate for PeerId {LocalPeerId}. Certificate Subject: {Subject}, Issuer: {Issuer}", context.Peer.Identity.PeerId, certificate.Subject, certificate.Issuer); SslServerAuthenticationOptions serverAuthenticationOptions = new() { ApplicationProtocols = ApplicationProtocols.Value, - RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.RemotePeer.Address, certificate), + RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.State.RemoteAddress, certificate), ServerCertificate = certificate, ClientCertificateRequired = true, }; @@ -47,16 +43,16 @@ public async Task ListenAsync(IChannel downChannel, IChannelFactory? channelFact try { await sslStream.AuthenticateAsServerAsync(serverAuthenticationOptions); - _logger?.LogInformation("Server TLS Authentication successful. PeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.RemotePeer.Address.Get(), sslStream.NegotiatedApplicationProtocol.Protocol); + _logger?.LogInformation("Server TLS Authentication successful. PeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.State.RemotePeerId, sslStream.NegotiatedApplicationProtocol.Protocol); } catch (Exception ex) { - _logger?.LogError("Error during TLS authentication for PeerId {RemotePeerId}: {ErrorMessage}.", context.RemotePeer.Address.Get(), ex.Message); + _logger?.LogError("Error during TLS authentication for PeerId {RemotePeerId}: {ErrorMessage}.", context.State.RemotePeerId, ex.Message); _logger?.LogDebug("TLS Authentication Exception Details: {StackTrace}", ex.StackTrace); throw; } _logger?.LogDebug($"{Encoding.UTF8.GetString(sslStream.NegotiatedApplicationProtocol.Protocol.ToArray())} protocol negotiated"); - IChannel upChannel = channelFactory.SubListen(context); + IChannel upChannel = context.Upgrade(); await ExchangeData(sslStream, upChannel, _logger); _ = upChannel.CloseAsync(); } @@ -64,14 +60,12 @@ public async Task ListenAsync(IChannel downChannel, IChannelFactory? channelFact private static bool VerifyRemoteCertificate(Multiaddress remotePeerAddress, X509Certificate certificate) => CertificateHelper.ValidateCertificate(certificate as X509Certificate2, remotePeerAddress.Get().ToString()); - public async Task DialAsync(IChannel downChannel, IChannelFactory? channelFactory, IPeerContext context) + public async Task DialAsync(IChannel downChannel, IConnectionContext context) { - _logger?.LogInformation("Starting DialAsync: LocalPeerId {LocalPeerId}", context.LocalPeer.Address.Get()); - if (channelFactory is null) - { - throw new ArgumentException("Protocol is not properly instantiated"); - } - Multiaddress addr = context.LocalPeer.Address; + _logger?.LogInformation("Starting DialAsync: LocalPeerId {LocalPeerId}", context.Peer.Identity.PeerId); + + // TODO + Multiaddress addr = context.Peer.ListenAddresses.First(); bool isIP4 = addr.Has(); MultiaddressProtocol ipProtocol = isIP4 ? addr.Get() : addr.Get(); IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); @@ -86,29 +80,29 @@ public async Task DialAsync(IChannel downChannel, IChannelFactory? channelFactor TargetHost = ipAddress.ToString(), ApplicationProtocols = ApplicationProtocols.Value, EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13, - RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.RemotePeer.Address, certificate), - ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.LocalPeer.Identity) }, + RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.State.RemoteAddress, certificate), + ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity) }, }; - _logger?.LogTrace("SslClientAuthenticationOptions initialized for PeerId {RemotePeerId}.", context.RemotePeer.Address.Get()); + _logger?.LogTrace("SslClientAuthenticationOptions initialized for PeerId {RemotePeerId}.", context.State.RemotePeerId); Stream str = new ChannelStream(downChannel); SslStream sslStream = new(str, false, clientAuthenticationOptions.RemoteCertificateValidationCallback); _logger?.LogTrace("Sslstream initialized."); try { await sslStream.AuthenticateAsClientAsync(clientAuthenticationOptions); - _logger?.LogInformation("Client TLS Authentication successful. RemotePeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.RemotePeer.Address.Get(), sslStream.NegotiatedApplicationProtocol.Protocol); + _logger?.LogInformation("Client TLS Authentication successful. RemotePeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.State.RemotePeerId, sslStream.NegotiatedApplicationProtocol.Protocol); } catch (Exception ex) { - _logger?.LogError("Error during TLS client authentication for RemotePeerId {RemotePeerId}: {ErrorMessage}.", context.RemotePeer.Address.Get(), ex.Message); + _logger?.LogError("Error during TLS client authentication for RemotePeerId {RemotePeerId}: {ErrorMessage}.", context.State.RemotePeerId, ex.Message); _logger?.LogDebug("TLS Authentication Exception Details: {StackTrace}", ex.StackTrace); return; } - _logger?.LogDebug("Subdialing protocols: {Protocols}.", string.Join(", ", channelFactory.SubProtocols.Select(x => x.Id))); - IChannel upChannel = channelFactory.SubDial(context); - _logger?.LogDebug("SubDial completed for PeerId {RemotePeerId}.", context.RemotePeer.Address.Get()); + _logger?.LogDebug("Subdialing protocols: {Protocols}.", string.Join(", ", context.SubProtocols.Select(x => x.Id))); + IChannel upChannel = context.Upgrade(); + _logger?.LogDebug("SubDial completed for PeerId {RemotePeerId}.", context.State.RemotePeerId); await ExchangeData(sslStream, upChannel, _logger); - _logger?.LogDebug("Connection closed for PeerId {RemotePeerId}.", context.RemotePeer.Address.Get()); + _logger?.LogDebug("Connection closed for PeerId {RemotePeerId}.", context.State.RemotePeerId); _ = upChannel.CloseAsync(); } diff --git a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs index 9adf705c..cc3a5d1c 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs @@ -1,11 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Core.TestsBase; -using NSubstitute; using NUnit.Framework.Internal; -using System.Collections.Concurrent; namespace Nethermind.Libp2p.Protocols.Noise.Tests; diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 08c98acc..9d224dc2 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -28,7 +28,7 @@ public YamuxProtocol(MultiplexerSettings? multiplexerSettings = null, ILoggerFac protected override async Task ConnectAsync(IChannel channel, IConnectionContext context, bool isListener) { - _logger?.LogInformation(isListener ? "Listen" : "Dial"); + _logger?.LogInformation("Ctx({ctx}): {mode} {peer}", context.Id, isListener ? "Listen" : "Dial", context.State.RemoteAddress); TaskAwaiter downChannelAwaiter = channel.GetAwaiter(); Dictionary channels = []; @@ -36,12 +36,13 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext try { int streamIdCounter = isListener ? 2 : 1; - INewSessionContext session = context.UpgradeToSession(); + using INewSessionContext session = context.UpgradeToSession(); + _logger?.LogInformation("Ctx({ctx}): Session created for {peer}", context.Id, context.State.RemoteAddress); int pingCounter = 0; using Timer timer = new((s) => { - _ = WriteHeaderAsync(channel, new YamuxHeader { Type = YamuxHeaderType.Ping, Flags = YamuxHeaderFlags.Syn, Length = ++pingCounter }); + _ = WriteHeaderAsync(context.Id, channel, new YamuxHeader { Type = YamuxHeaderType.Ping, Flags = YamuxHeaderFlags.Syn, Length = ++pingCounter }); }, null, PingDelay, PingDelay); _ = Task.Run(() => @@ -51,14 +52,14 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext int streamId = streamIdCounter; Interlocked.Add(ref streamIdCounter, 2); - _logger?.LogDebug("Stream {stream id}: Dialing with protocol {proto}", streamId, request.SelectedProtocol?.Id); - channels[streamId] = CreateUpchannel(streamId, YamuxHeaderFlags.Syn, request); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Dialing with protocol {proto}", context.Id, streamId, request.SelectedProtocol?.Id); + channels[streamId] = CreateUpchannel(context.Id, streamId, YamuxHeaderFlags.Syn, request); } }); while (!downChannelAwaiter.IsCompleted) { - YamuxHeader header = await ReadHeaderAsync(channel); + YamuxHeader header = await ReadHeaderAsync(context.Id, channel); ReadOnlySequence data = default; if (header.Type > YamuxHeaderType.GoAway) @@ -71,7 +72,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { if ((header.Flags & YamuxHeaderFlags.Syn) == YamuxHeaderFlags.Syn) { - _ = WriteHeaderAsync(channel, + _ = WriteHeaderAsync(context.Id, channel, new YamuxHeader { Flags = YamuxHeaderFlags.Ack, @@ -79,14 +80,14 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext Length = header.Length, }); - _logger?.LogDebug("Ping received and acknowledged"); + _logger?.LogDebug("Ctx({ctx}): Ping received and acknowledged", context.Id); } continue; } if (header.Type == YamuxHeaderType.GoAway) { - _logger?.LogDebug("Closing all streams"); + _logger?.LogDebug("Ctx({ctx}): Closing all streams", context.Id); foreach (ChannelState channelState in channels.Values) { @@ -104,7 +105,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext if ((header.Flags & YamuxHeaderFlags.Syn) == YamuxHeaderFlags.Syn && !channels.ContainsKey(header.StreamID)) { - channels[header.StreamID] = CreateUpchannel(header.StreamID, YamuxHeaderFlags.Ack, new UpgradeOptions()); + channels[header.StreamID] = CreateUpchannel(context.Id, header.StreamID, YamuxHeaderFlags.Ack, new UpgradeOptions()); } if (!channels.ContainsKey(header.StreamID)) @@ -113,7 +114,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { await channel.ReadAsync(header.Length); } - _logger?.LogDebug("Stream {stream id}: Ignored for closed stream", header.StreamID); + _logger?.LogDebug("Ctx({ctx}): Stream {stream id}: Ignored for closed stream", context.Id, header.StreamID); continue; } @@ -121,10 +122,10 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { if (header.Length > channels[header.StreamID].LocalWindow.Available) { - _logger?.LogDebug("Stream {stream id}: Data length > windows size: {length} > {window size}", + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Data length > windows size: {length} > {window size}", context.Id, header.StreamID, header.Length, channels[header.StreamID].LocalWindow.Available); - await WriteGoAwayAsync(channel, SessionTerminationCode.ProtocolError); + await WriteGoAwayAsync(context.Id, channel, SessionTerminationCode.ProtocolError); return; } @@ -133,8 +134,8 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext bool spent = channels[header.StreamID].LocalWindow.SpendWindow((int)data.Length); if (!spent) { - _logger?.LogDebug("Stream {stream id}: Window spent out of budget", header.StreamID); - await WriteGoAwayAsync(channel, SessionTerminationCode.InternalError); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Window spent out of budget", context.Id, header.StreamID); + await WriteGoAwayAsync(context.Id, channel, SessionTerminationCode.InternalError); return; } @@ -147,7 +148,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext int extendedBy = channels[header.StreamID].LocalWindow.ExtendWindowIfNeeded(); if (extendedBy is not 0) { - _ = WriteHeaderAsync(channel, + _ = WriteHeaderAsync(context.Id, channel, new YamuxHeader { Type = YamuxHeaderType.WindowUpdate, @@ -166,7 +167,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext int extendedBy = channelState.LocalWindow.ExtendWindowIfNeeded(); if (extendedBy is not 0) { - _ = WriteHeaderAsync(channel, + _ = WriteHeaderAsync(context.Id, channel, new YamuxHeader { Type = YamuxHeaderType.WindowUpdate, @@ -183,7 +184,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { int oldSize = channels[header.StreamID].RemoteWindow.Available; int newSize = channels[header.StreamID].RemoteWindow.ExtendWindow(header.Length); - _logger?.LogDebug("Stream {stream id}: Window update requested: {old} => {new}", header.StreamID, oldSize, newSize); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Window update requested: {old} => {new}", context.Id, header.StreamID, oldSize, newSize); } if ((header.Flags & YamuxHeaderFlags.Fin) == YamuxHeaderFlags.Fin) @@ -194,19 +195,19 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext } _ = state.Channel?.WriteEofAsync(); - _logger?.LogDebug("Stream {stream id}: Finish receiving", header.StreamID); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Finish receiving", context.Id, header.StreamID); } if ((header.Flags & YamuxHeaderFlags.Rst) == YamuxHeaderFlags.Rst) { _ = channels[header.StreamID].Channel?.CloseAsync(); - _logger?.LogDebug("Stream {stream id}: Reset", header.StreamID); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Reset", context.Id, header.StreamID); } } - await WriteGoAwayAsync(channel, SessionTerminationCode.Ok); + await WriteGoAwayAsync(context.Id, channel, SessionTerminationCode.Ok); - ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, UpgradeOptions upgradeOptions) + ChannelState CreateUpchannel(string contextId, int streamId, YamuxHeaderFlags initiationFlag, UpgradeOptions upgradeOptions) { bool isListenerChannel = isListener ^ (streamId % 2 == 0); @@ -227,14 +228,14 @@ ChannelState CreateUpchannel(int streamId, YamuxHeaderFlags initiationFlag, Upgr upChannel.GetAwaiter().OnCompleted(() => { channels.Remove(streamId); - _logger?.LogDebug("Stream {stream id}: Closed", streamId); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Closed", contextId, streamId); }); Task.Run(async () => { try { - await WriteHeaderAsync(channel, + await WriteHeaderAsync(contextId, channel, new YamuxHeader { Flags = initiationFlag, @@ -244,22 +245,22 @@ await WriteHeaderAsync(channel, if (initiationFlag == YamuxHeaderFlags.Syn) { - _logger?.LogDebug("Stream {stream id}: New stream request sent", streamId); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: New stream request sent", contextId, streamId); } else { - _logger?.LogDebug("Stream {stream id}: New stream request acknowledged", streamId); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: New stream request acknowledged", contextId, streamId); } await foreach (var upData in upChannel.ReadAllAsync()) { - _logger?.LogDebug("Stream {stream id}: Receive from upchannel, length={length}", streamId, upData.Length); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Receive from upchannel, length={length}", contextId, streamId, upData.Length); for (int i = 0; i < upData.Length;) { int sendingSize = await state.RemoteWindow.SpendWindowOrWait((int)upData.Length - i); - await WriteHeaderAsync(channel, + await WriteHeaderAsync(contextId, channel, new YamuxHeader { Type = YamuxHeaderType.Data, @@ -270,22 +271,22 @@ await WriteHeaderAsync(channel, } } - await WriteHeaderAsync(channel, + await WriteHeaderAsync(contextId, channel, new YamuxHeader { Flags = YamuxHeaderFlags.Fin, Type = YamuxHeaderType.WindowUpdate, StreamID = streamId }); - _logger?.LogDebug("Stream {stream id}: Upchannel finished writing", streamId); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Upchannel finished writing", contextId, streamId); } catch (ChannelClosedException e) { - _logger?.LogDebug("Stream {stream id}: Closed due to transport disconnection", streamId); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Closed due to transport disconnection", contextId, streamId); } catch (Exception e) { - await WriteHeaderAsync(channel, + await WriteHeaderAsync(contextId, channel, new YamuxHeader { Flags = YamuxHeaderFlags.Rst, @@ -295,21 +296,22 @@ await WriteHeaderAsync(channel, _ = upChannel.CloseAsync(); channels.Remove(streamId); - _logger?.LogDebug("Stream {stream id}: Unexpected error, closing: {error}", streamId, e.Message); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Unexpected error, closing: {error}", contextId, streamId, e.Message); } }); return state; } } - catch (ChannelClosedException ex) + catch (ChannelClosedException) { - _logger?.LogDebug("Closed due to transport disconnection"); + _logger?.LogDebug("Ctx({ctx}): Closed due to transport disconnection", context.Id); } catch (Exception ex) { - await WriteGoAwayAsync(channel, SessionTerminationCode.InternalError); - _logger?.LogDebug("Closed with exception \"{exception}\" {stackTrace}", ex.Message, ex.StackTrace); + _logger?.LogDebug("Ctx({ctx}): Closed with exception \"{exception}\" {stackTrace}", context.Id, ex.Message, ex.StackTrace); + await WriteGoAwayAsync(context.Id, channel, SessionTerminationCode.InternalError); + await channel.CloseAsync(); } foreach (ChannelState upChannel in channels.Values) @@ -318,15 +320,15 @@ await WriteHeaderAsync(channel, } } - private async Task ReadHeaderAsync(IReader reader, CancellationToken token = default) + private async Task ReadHeaderAsync(string contextId, IReader reader, CancellationToken token = default) { byte[] headerData = (await reader.ReadAsync(HeaderLength, token: token).OrThrow()).ToArray(); YamuxHeader header = YamuxHeader.FromBytes(headerData); - _logger?.LogTrace("Stream {stream id}: Receive type={type} flags={flags} length={length}", header.StreamID, header.Type, header.Flags, header.Length); + _logger?.LogTrace("Ctx({ctx}), stream {stream id}: Receive type={type} flags={flags} length={length}", contextId, header.StreamID, header.Type, header.Flags, header.Length); return header; } - private async Task WriteHeaderAsync(IWriter writer, YamuxHeader header, ReadOnlySequence data = default) + private async Task WriteHeaderAsync(string contextId, IWriter writer, YamuxHeader header, ReadOnlySequence data = default) { byte[] headerBuffer = new byte[HeaderLength]; if (header.Type == YamuxHeaderType.Data) @@ -335,12 +337,12 @@ private async Task WriteHeaderAsync(IWriter writer, YamuxHeader header, ReadOnly } YamuxHeader.ToBytes(headerBuffer, ref header); - _logger?.LogTrace("Stream {stream id}: Send type={type} flags={flags} length={length}", header.StreamID, header.Type, header.Flags, header.Length); + _logger?.LogTrace("Ctx({ ctx}), stream {stream id}: Send type={type} flags={flags} length={length}", contextId, header.StreamID, header.Type, header.Flags, header.Length); await writer.WriteAsync(data.Length == 0 ? new ReadOnlySequence(headerBuffer) : data.Prepend(headerBuffer)).OrThrow(); } - private Task WriteGoAwayAsync(IWriter channel, SessionTerminationCode code) => - WriteHeaderAsync(channel, new YamuxHeader + private Task WriteGoAwayAsync(string contextId, IWriter channel, SessionTerminationCode code) => + WriteHeaderAsync(contextId, channel, new YamuxHeader { Type = YamuxHeaderType.GoAway, Length = (int)code, diff --git a/src/libp2p/Libp2p/Libp2p.csproj b/src/libp2p/Libp2p/Libp2p.csproj index e823c2f2..80c6613c 100644 --- a/src/libp2p/Libp2p/Libp2p.csproj +++ b/src/libp2p/Libp2p/Libp2p.csproj @@ -15,6 +15,7 @@ + diff --git a/src/libp2p/Libp2p/Libp2pLocalPeer.cs b/src/libp2p/Libp2p/Libp2pLocalPeer.cs index 5ae2723e..465f0cc1 100644 --- a/src/libp2p/Libp2p/Libp2pLocalPeer.cs +++ b/src/libp2p/Libp2p/Libp2pLocalPeer.cs @@ -1,13 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Nethermind.Libp2p.Core; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Nethermind.Libp2p; //internal class Libp2pLocalPeer: IPeer //{ diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 29bdb78a..5f0668cf 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Multiformats.Address; using Multiformats.Address.Protocols; diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index 8815b918..3f1c0452 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -7,7 +7,6 @@ namespace Nethermind.Libp2p.Stack; -[RequiresPreviewFeatures] public class Libp2pPeerFactoryBuilder(IServiceProvider? serviceProvider = default) : PeerFactoryBuilderBase(serviceProvider), ILibp2pPeerFactoryBuilder { @@ -46,10 +45,10 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) ProtocolRef[] commonSelector = [Get()]; Connect([tcp], [Get()], encryption, [Get()], muxers, commonSelector); - ProtocolRef quic = Get(); - Connect([quic], commonSelector); + //ProtocolRef quic = Get(); + //Connect([quic], commonSelector); - ProtocolRef[] relay = addRelay ? [Get(), Get()] : []; + ProtocolRef[] relay = addRelay ? [Get(), Get()] : []; ProtocolRef[] pubsub = addPubsub ? [ Get(), Get(), @@ -59,6 +58,7 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) ProtocolRef[] apps = [ Get(), + Get(), .. additionalProtocols, .. relay, .. pubsub, @@ -72,6 +72,7 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) Connect(relaySelector, apps.Where(a => !relay.Contains(a)).ToArray()); } - return [tcp, quic]; + //return [tcp, quic]; + return [tcp]; } } diff --git a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs index b43f0a52..8cd6984b 100644 --- a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs +++ b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs @@ -1,12 +1,6 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Microsoft.Extensions.Logging; -using Multiformats.Address; -using Multiformats.Address.Protocols; -using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Stack; - namespace Nethermind.Libp2p.Protocols; /// diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index 249f5837..8109cb72 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -11,7 +11,6 @@ namespace Nethermind.Libp2p.Stack; public static class ServiceProviderExtensions { - [RequiresPreviewFeatures] public static IServiceCollection AddLibp2p(this IServiceCollection services, Func? factorySetup = null) { return services diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index 860f52e3..ca871f4b 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -49,7 +49,7 @@ "/ip4/0.0.0.0/udp/{0}/quic-v1" : "/ip4/0.0.0.0/tcp/{0}"; - peer.OnConnection += async newSession => logger.LogInformation("A peer connected {remote}", newSession.RemoteAddress); + peer.OnConnected += async newSession => logger.LogInformation("A peer connected {remote}", newSession.RemoteAddress); await peer.StartListenAsync( [string.Format(addrTemplate, args.Length > 0 && args[0] == "-sp" ? args[1] : "0")], diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index dba8f674..f9791738 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -7,8 +7,6 @@ using System.Text; using System.Text.Json; using Nethermind.Libp2p.Protocols.Pubsub; -using Multiformats.Address.Protocols; -using Multiformats.Address; using Nethermind.Libp2p.Protocols; using System.Text.RegularExpressions; @@ -21,7 +19,7 @@ .AddSimpleConsole(l => { l.SingleLine = true; - l.TimestampFormat = "[HH:mm:ss.FFF]"; + l.TimestampFormat = "[HH:mm:ss.fff]"; }).AddFilter((_, type, lvl) => !omittedLogs.IsMatch(type!))) .BuildServiceProvider(); @@ -57,7 +55,7 @@ _ = peer.StartListenAsync([addr], ts.Token); string peerId = peer.Identity.PeerId.ToString(); -_ = serviceProvider.GetService()!.DiscoverAsync(peer.Address, token: ts.Token); +_ = serviceProvider.GetService()!.DiscoverAsync(peer.ListenAddresses, token: ts.Token); _ = router.RunAsync(peer, token: ts.Token); diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index 651cf069..4ac9f922 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -85,7 +85,7 @@ CancellationTokenSource listennTcs = new(); await localPeer.StartListenAsync([builder.MakeAddress(ip)], listennTcs.Token); - localPeer.OnConnection += (session) => { Log($"Connected {session.RemoteAddress}"); return Task.CompletedTask; }; + localPeer.OnConnected += (session) => { Log($"Connected {session.RemoteAddress}"); return Task.CompletedTask; }; Log($"Listening on {string.Join(", ", localPeer.ListenAddresses)}"); db.ListRightPush(new RedisKey("listenerAddr"), new RedisValue(localPeer.ListenAddresses.First().ToString())); await Task.Delay(testTimeoutSeconds * 1000); @@ -161,7 +161,7 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) ProtocolRef[] apps = [Get(), Get()]; Connect(selector, apps); - return transportStack; + return transportStack; } public string MakeAddress(string ip = "0.0.0.0", string port = "0") => transport switch From fe65e9a7988d11a9cae2646d56164d291a9350bf Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Tue, 3 Dec 2024 13:46:33 +0300 Subject: [PATCH 11/25] Fix --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 108 +++++++----------- .../Libp2p.Core.TestsBase/E2e/ChannelBus.cs | 2 +- .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 2 +- .../E2e/TestMuxerProtocol.cs | 30 ++--- .../Libp2p.Core.TestsBase/LocalPeerStub.cs | 2 +- .../TestContextLoggerFactory.cs | 45 +++----- src/libp2p/Libp2p.Core/Discovery/PeerStore.cs | 2 +- src/libp2p/Libp2p.Core/IChannel.cs | 2 +- src/libp2p/Libp2p.Core/Peer.cs | 22 ++-- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 7 +- .../ProtocolStackSettings.cs | 10 +- src/libp2p/Libp2p.Core/Stream.cs | 4 +- .../IdentifyProtocol.cs | 2 +- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 2 +- .../Program.cs | 12 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 2 +- .../PubsubRouter.Topics.cs | 2 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 38 +++--- .../MultistreamProtocolTests.cs | 4 +- .../PubSubTestSetup.cs | 4 +- .../PubsubPeerDiscoveryProtocol.cs | 4 +- .../Libp2p.Protocols.Tls.Tests/Using.cs | 6 - .../Libp2p.Protocols.Tls/TlsProtocol.cs | 2 +- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 2 +- src/samples/pubsub-chat/Program.cs | 2 +- src/samples/transport-interop/Program.cs | 4 +- 26 files changed, 148 insertions(+), 174 deletions(-) rename src/libp2p/{Libp2p => Libp2p.Core}/ProtocolStackSettings.cs (50%) diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index 95176303..756a53d6 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -7,29 +7,29 @@ namespace Nethermind.Libp2p.Core.Tests; public class ContextTests { - public static Channel tcp = new Channel(); + public static Channel tcp = new(); [Test] public async Task E2e() { - ProtocolRef tProto = new ProtocolRef(new TProto()); - ProtocolRef cProto = new ProtocolRef(new CProto()); - ProtocolRef sProto = new ProtocolRef(new SProto()); + ProtocolRef tProto = new(new TProto()); + ProtocolRef cProto = new(new CProto()); + ProtocolRef sProto = new(new SProto()); + ProtocolRef sProto2 = new(new SProto2()); ProtocolStackSettings protocolStackSettings = new() { Protocols = new Dictionary { { tProto, [ cProto] }, - { cProto, [sProto] }, - { sProto, [] }, + { cProto, [sProto, sProto2] }, }, TopProtocols = [tProto] }; - LocalPeer peer1 = new LocalPeer(new Identity(), protocolStackSettings); - LocalPeer peer2 = new LocalPeer(new Identity(), protocolStackSettings); + LocalPeer peer1 = new(new Identity(), protocolStackSettings); + LocalPeer peer2 = new(new Identity(), protocolStackSettings); await peer1.StartListenAsync([new Multiaddress()]); await peer2.StartListenAsync([new Multiaddress()]); @@ -90,7 +90,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr break; } - var sent = await topChan.WriteAsync(received.Data); + IOResult sent = await topChan.WriteAsync(received.Data); if (sent != IOResult.Ok) { @@ -121,7 +121,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress listenAddr, break; } - var sent = await ContextTests.tcp.WriteAsync(received.Data); + IOResult sent = await ContextTests.tcp.WriteAsync(received.Data); if (sent != IOResult.Ok) { @@ -138,64 +138,54 @@ class CProto : IConnectionProtocol public async Task DialAsync(IChannel downChannel, IConnectionContext context) { - try - { - using INewSessionContext session = context.UpgradeToSession(); - IChannel topChan = context.Upgrade(); - ReadResult received; - while (true) + using INewSessionContext session = context.UpgradeToSession(); + IChannel topChan = context.Upgrade(); + + ReadResult received; + while (true) + { + received = await topChan.ReadAsync(1, ReadBlockingMode.WaitAny); + if (received.Result != IOResult.Ok) { - received = await topChan.ReadAsync(1, ReadBlockingMode.WaitAny); - if (received.Result != IOResult.Ok) - { - break; - } + break; + } - var sent = await downChannel.WriteAsync(received.Data); + IOResult sent = await downChannel.WriteAsync(received.Data); - if (sent != IOResult.Ok) - { - break; - } + if (sent != IOResult.Ok) + { + break; } - await topChan.CloseAsync(); } - catch - { + await topChan.CloseAsync(); - } } public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { - try - { - using INewSessionContext session = context.UpgradeToSession(); - IChannel topChan = context.Upgrade(); - ReadResult received; - while (true) + using INewSessionContext session = context.UpgradeToSession(); + IChannel topChan = context.Upgrade(); + + ReadResult received; + while (true) + { + received = await downChannel.ReadAsync(1, ReadBlockingMode.WaitAny); + if (received.Result != IOResult.Ok) { - received = await downChannel.ReadAsync(1, ReadBlockingMode.WaitAny); - if (received.Result != IOResult.Ok) - { - break; - } + break; + } - var sent = await topChan.WriteAsync(received.Data); + IOResult sent = await topChan.WriteAsync(received.Data); - if (sent != IOResult.Ok) - { - break; - } + if (sent != IOResult.Ok) + { + break; } - await topChan.CloseAsync(); } - catch - { + await topChan.CloseAsync(); - } } } @@ -205,26 +195,12 @@ class SProto : ISessionProtocol public async Task DialAsync(IChannel downChannel, ISessionContext context) { - try - { - await downChannel.WriteLineAsync("Oh hi there"); - } - catch - { - - } + await downChannel.WriteLineAsync("Oh hi there"); } public async Task ListenAsync(IChannel downChannel, ISessionContext context) { - try - { - var line = await downChannel.ReadLineAsync(); - } - catch - { - - } + string line = await downChannel.ReadLineAsync(); } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs index 0cdb874e..ef083406 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/ChannelBus.cs @@ -29,7 +29,7 @@ public async IAsyncEnumerable GetIncomingRequests(PeerId serverId) logger?.LogDebug($"Listen {serverId}"); - await foreach (var item in col.Reader.ReadAllAsync()) + await foreach (ClientChannel item in col.Reader.ReadAllAsync()) { logger?.LogDebug($"New request from {item.Client} to {serverId}"); yield return item.Channel; diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index 569319c7..dfc68852 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -7,7 +7,7 @@ public class TestBuilder(IServiceProvider? serviceProvider = null) : PeerFactory { protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { - var root = Get(); + ProtocolRef root = Get(); Connect([root], [ diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index 293345d3..47af966c 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -34,7 +34,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr { context.ListenerReady(listenAddr); logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listen async"); - await foreach (var item in bus.GetIncomingRequests(context.Peer.Identity.PeerId)) + await foreach (IChannel item in bus.GetIncomingRequests(context.Peer.Identity.PeerId)) { using INewConnectionContext connection = context.CreateConnection(); logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener handles new con"); @@ -62,20 +62,20 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn logger?.LogDebug($"{context.Peer.Identity.PeerId}: Dialer handles remote {peer}"); } - using INewSessionContext session = connection.UpgradeToSession(); connection.State.RemoteAddress = $"/p2p/{peer}"; + using INewSessionContext session = connection.UpgradeToSession(); string logPrefix = $"{context.Peer.Identity.PeerId}<>{peer}"; - _ = Task.Run(async () => + _ = Task.Run(() => { - foreach (var item in session.DialRequests) + foreach (UpgradeOptions item in session.DialRequests) { uint chanId = Interlocked.Add(ref counter, 2); logger?.LogDebug($"{context.Peer.Identity.PeerId}({chanId}): Sub-request {item.SelectedProtocol} {item.CompletionSource is not null} from {connection.State.RemoteAddress.GetPeerId()}"); chans[chanId] = new MuxerChannel { Tcs = item.CompletionSource }; - var response = new MuxerPacket() + MuxerPacket response = new() { ChannelId = chanId, Type = MuxerPacketType.NewStreamRequest, @@ -87,7 +87,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn _ = downChannel.WriteSizeAndProtobufAsync(response); } logger?.LogDebug($"{context.Peer.Identity.PeerId}: SubDialRequests End"); - + return Task.CompletedTask; }); while (true) @@ -104,7 +104,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn { case MuxerPacketType.NewStreamRequest: IProtocol? selected = null; - foreach (var proto in packet.Protocols) + foreach (string? proto in packet.Protocols) { selected = session.SubProtocols.FirstOrDefault(x => x.Id == proto); if (selected is not null) break; @@ -112,7 +112,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn if (selected is not null) { logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Matched {selected}"); - var response = new MuxerPacket() + MuxerPacket response = new() { ChannelId = packet.ChannelId, Type = MuxerPacketType.NewStreamResponse, @@ -122,7 +122,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn } }; - var req = new UpgradeOptions { SelectedProtocol = selected, ModeOverride = UpgradeModeOverride.Dial }; + UpgradeOptions req = new() { SelectedProtocol = selected, ModeOverride = UpgradeModeOverride.Dial }; IChannel upChannel = session.Upgrade(req); chans[packet.ChannelId] = new MuxerChannel { UpChannel = upChannel }; @@ -136,7 +136,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn { logger?.LogDebug($"{logPrefix}({packet.ChannelId}): No match {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); - var response = new MuxerPacket() + MuxerPacket response = new() { ChannelId = packet.ChannelId, Type = MuxerPacketType.NewStreamResponse, @@ -150,7 +150,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn case MuxerPacketType.NewStreamResponse: if (packet.Protocols.Any()) { - var req = new UpgradeOptions { SelectedProtocol = session.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First()), ModeOverride = UpgradeModeOverride.Dial }; + UpgradeOptions req = new() { SelectedProtocol = session.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First()), ModeOverride = UpgradeModeOverride.Dial }; IChannel upChannel = session.Upgrade(req); chans[packet.ChannelId].UpChannel = upChannel; logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SelectedProtocol}"); @@ -190,12 +190,12 @@ private Task HandleUpchannelData(IChannel downChannel, Dictionary item in upChannel.ReadAllAsync()) { - var data = item.ToArray(); + byte[] data = item.ToArray(); logger?.LogDebug($"{logPrefix}({channelId}): Upchannel data {data.Length} {Hex.ToHexString(data, false)}"); - var packet = new MuxerPacket() + MuxerPacket packet = new() { ChannelId = channelId, Type = MuxerPacketType.Data, @@ -214,7 +214,7 @@ private Task HandleUpchannelData(IChannel downChannel, Dictionary new Multiaddress(); + public Multiaddress RemoteAddress => new(); public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { diff --git a/src/libp2p/Libp2p.Core.TestsBase/TestContextLoggerFactory.cs b/src/libp2p/Libp2p.Core.TestsBase/TestContextLoggerFactory.cs index cd337265..9c1520bd 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/TestContextLoggerFactory.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/TestContextLoggerFactory.cs @@ -13,39 +13,32 @@ class TestContextLogger(string categoryName) : ILogger, IDisposable { private readonly string _categoryName = categoryName; - public IDisposable? BeginScope(TState state) where TState : notnull - { - return this; - } + public IDisposable? BeginScope(TState state) where TState : notnull => this; - public void Dispose() - { - } + public void Dispose() { } + public bool IsEnabled(LogLevel logLevel) => true; - public bool IsEnabled(LogLevel logLevel) + private static string ToString(LogLevel level) => level switch { - return true; - } + LogLevel.Trace => "TRAC", + LogLevel.Debug => "DEBG", + LogLevel.Information => "INFO", + LogLevel.Warning => "WARN", + LogLevel.Error => "EROR", + LogLevel.Critical => "CRIT", + LogLevel.None => "NONE", + _ => throw new NotImplementedException() + }; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - TestContext.Out.WriteLine($"{logLevel} {_categoryName}:{eventId}: {(exception is null ? state?.ToString() : formatter(state, exception))}"); - Debug.WriteLine($"{logLevel} {_categoryName}:{eventId}: {(exception is null ? state?.ToString() : formatter(state, exception))}"); + string log = $"{ToString(logLevel)} {_categoryName}: {(exception is null ? state?.ToString() : formatter(state, exception))}"; + TestContext.Out.WriteLine(log); + Debug.WriteLine(log); } } - public void AddProvider(ILoggerProvider provider) - { - - } - - public ILogger CreateLogger(string categoryName) - { - return new TestContextLogger(categoryName); - } - - public void Dispose() - { - - } + public void AddProvider(ILoggerProvider provider) { } + public ILogger CreateLogger(string categoryName) => new TestContextLogger(categoryName); + public void Dispose() { } } diff --git a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs index 93267bb8..042b435d 100644 --- a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs +++ b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs @@ -44,7 +44,7 @@ public event Action? OnNewPeer } onNewPeer += value; - foreach (var item in store.Select(x => x.Value).ToArray()) + foreach (PeerInfo? item in store.Select(x => x.Value).ToArray()) { if (item.Addrs is not null) value.Invoke(item.Addrs.ToArray()); } diff --git a/src/libp2p/Libp2p.Core/IChannel.cs b/src/libp2p/Libp2p.Core/IChannel.cs index ce24c06d..6de73dcc 100644 --- a/src/libp2p/Libp2p.Core/IChannel.cs +++ b/src/libp2p/Libp2p.Core/IChannel.cs @@ -14,7 +14,7 @@ CancellationToken CancellationToken { get { - var token = new CancellationTokenSource(); + CancellationTokenSource token = new(); GetAwaiter().OnCompleted(token.Cancel); return token.Token; } diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 05e2c55f..196f9fd3 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -212,7 +212,7 @@ public async Task DialAsync(Multiaddress[] addrs, CancellationToken to cancellations[addr] = CancellationTokenSource.CreateLinkedTokenSource(token); } - Task timeoutTask = Task.Delay(15_000, token); + Task timeoutTask = Task.Delay(15000_000, token); Task wait = await TaskHelper.FirstSuccess([timeoutTask, .. addrs.Select(addr => DialAsync(addr, cancellations[addr].Token))]); if (wait == timeoutTask) @@ -261,19 +261,21 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token return session; } - internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) + internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) { if (protocolStackSettings.Protocols is null) { throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); } - if (!protocolStackSettings.Protocols.ContainsKey(protocol)) + if (!protocolStackSettings.Protocols.ContainsKey(parentProtocol)) { - throw new Libp2pSetupException($"{protocol} is not added"); + throw new Libp2pSetupException($"{parentProtocol} is not added"); } - ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : protocolStackSettings.Protocols[protocol].Single(); + ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : + options?.SelectedProtocol is not null ? protocolStackSettings.Protocols[parentProtocol].SingleOrDefault(x => x.Protocol == options.SelectedProtocol) ?? new ProtocolRef(options.SelectedProtocol) : + protocolStackSettings.Protocols[parentProtocol].Single(); Channel downChannel = new(); @@ -281,20 +283,20 @@ internal IChannel Upgrade(Session session, ProtocolRef protocol, IProtocol? upgr Task upgradeTask; - _logger?.LogInformation($"Upgrade {protocol} to {top}, listen={isListener}"); + _logger?.LogInformation($"Upgrade {parentProtocol} to {top}, listen={isListener}"); switch (top.Protocol) { case IConnectionProtocol tProto: { - var ctx = new ConnectionContext(this, session, top, isListener, options); + ConnectionContext ctx = new(this, session, top, isListener, options); upgradeTask = isListener ? tProto.ListenAsync(downChannel.Reverse, ctx) : tProto.DialAsync(downChannel.Reverse, ctx); break; } case ISessionProtocol sProto: { - var ctx = new SessionContext(this, session, top, isListener, options); + SessionContext ctx = new(this, session, top, isListener, options); upgradeTask = isListener ? sProto.ListenAsync(downChannel.Reverse, ctx) : sProto.DialAsync(downChannel.Reverse, ctx); break; } @@ -362,13 +364,13 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef { case IConnectionProtocol tProto: { - var ctx = new ConnectionContext(this, session, top, isListener, options); + ConnectionContext ctx = new(this, session, top, isListener, options); upgradeTask = isListener ? tProto.ListenAsync(parentChannel, ctx) : tProto.DialAsync(parentChannel, ctx); break; } case ISessionProtocol sProto: { - var ctx = new SessionContext(this, session, top, isListener, options); + SessionContext ctx = new(this, session, top, isListener, options); upgradeTask = isListener ? sProto.ListenAsync(parentChannel, ctx) : sProto.DialAsync(parentChannel, ctx); break; } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index ff2bf765..2d1f77e5 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -29,13 +29,14 @@ internal static TProtocol CreateProtocolInstance(IServiceProvider ser public class ProtocolRef(IProtocol protocol, bool isExposed = true) { - static int IdCounter = 0; - private readonly bool l; + static int RefIdCounter = 0; - public string RefId { get; } = Interlocked.Increment(ref IdCounter).ToString(); + public string RefId { get; } = Interlocked.Increment(ref RefIdCounter).ToString(); public IProtocol Protocol => protocol; public bool IsExposed => isExposed; + public string Id => Protocol.Id; + public override string ToString() { return $"ref#{RefId}({Protocol.Id})"; diff --git a/src/libp2p/Libp2p/ProtocolStackSettings.cs b/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs similarity index 50% rename from src/libp2p/Libp2p/ProtocolStackSettings.cs rename to src/libp2p/Libp2p.Core/ProtocolStackSettings.cs index 1a2eb1ac..a56bd69a 100644 --- a/src/libp2p/Libp2p/ProtocolStackSettings.cs +++ b/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs @@ -1,11 +1,15 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Nethermind.Libp2p.Core; -namespace Nethermind.Libp2p.Stack; +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Stack; + +namespace Nethermind.Libp2p.Core; -internal class ProtocolStackSettings : IProtocolStackSettings +public class ProtocolStackSettings : IProtocolStackSettings { public ProtocolRef[]? TopProtocols { get; set; } public Dictionary? Protocols { get; set; } diff --git a/src/libp2p/Libp2p.Core/Stream.cs b/src/libp2p/Libp2p.Core/Stream.cs index 4b1d1720..7f3e36e5 100644 --- a/src/libp2p/Libp2p.Core/Stream.cs +++ b/src/libp2p/Libp2p.Core/Stream.cs @@ -38,7 +38,7 @@ public override int Read(Span buffer) { if (buffer is { Length: 0 } && _canRead) return 0; - var result = _chan.ReadAsync(buffer.Length, ReadBlockingMode.WaitAny).Result; + ReadResult result = _chan.ReadAsync(buffer.Length, ReadBlockingMode.WaitAny).Result; if (result.Result != IOResult.Ok) { _canRead = false; @@ -72,7 +72,7 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, { if (buffer is { Length: 0 } && _canRead) return 0; - var result = await _chan.ReadAsync(buffer.Length, ReadBlockingMode.WaitAny); + ReadResult result = await _chan.ReadAsync(buffer.Length, ReadBlockingMode.WaitAny); if (result.Result != IOResult.Ok) { _canRead = false; diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 5a93b502..ce81d15e 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -131,7 +131,7 @@ private static ByteString CreateSignedEnvelope(Identity identity, Multiaddress[] Seq = seq }; - foreach (var address in addresses) + foreach (Multiaddress address in addresses) { paylaod.Addresses.Add(new AddressInfo { diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index 170a01b4..cd162634 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -189,7 +189,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, public static Multiaddress ToMultiaddress(EndPoint ep, ProtocolType protocolType) { - Multiaddress multiaddress = new Multiaddress(); + Multiaddress multiaddress = new(); IPEndPoint iPEndPoint = (IPEndPoint)ep; if (iPEndPoint != null) { diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs index d285eada..8ecaf3b6 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs @@ -9,9 +9,10 @@ using Nethermind.Libp2p.Core.TestsBase.E2e; using Nethermind.Libp2p.Protocols; using Nethermind.Libp2p.Protocols.Pubsub; +using Nethermind.Libp2p.Stack; using System.Text; -int totalCount = 7; +int totalCount = 2; IPeer[] peers = new IPeer[totalCount]; PeerStore[] peerStores = new PeerStore[totalCount]; PubsubRouter[] routers = new PubsubRouter[totalCount]; @@ -24,6 +25,7 @@ .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) .AddSingleton(sp => new TestContextLoggerFactory()) .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton(sp => new Settings { LowestDegree = 1, Degree = 2, LazyDegree = 2, HighestDegree = 3 }) @@ -73,16 +75,16 @@ await Task.Delay(5000); -var testTopic = routers[1].GetTopic("test"); -var testTopicEnd = routers[totalCount - 1].GetTopic("test"); +ITopic testTopic = routers[1].GetTopic("test"); +ITopic testTopicEnd = routers[totalCount - 1].GetTopic("test"); testTopicEnd.OnMessage += (s) => Console.WriteLine(Encoding.UTF8.GetString(s)); testTopic.Publish(Encoding.UTF8.GetBytes("test")); -for (int i = 0; i < 20; i++) +for (int i = 0; i < 8; i++) { Console.WriteLine(i * 100); - await Task.Delay(100); + await Task.Delay(1000000); } Console.WriteLine("Routers"); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index a1a2f44b..01964f4e 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -33,7 +33,7 @@ public async Task DialAsync(IChannel channel, ISessionContext context) TaskCompletionSource dialTcs = new(); CancellationToken token = router.OutboundConnection(context.State.RemoteAddress, Id, dialTcs.Task, (rpc) => { - var t = channel.WriteSizeAndProtobufAsync(rpc); + ValueTask t = channel.WriteSizeAndProtobufAsync(rpc); t.AsTask().ContinueWith((t) => { if (!t.IsCompletedSuccessfully) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs index e9c54091..8ef082b8 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs @@ -49,7 +49,7 @@ public void Subscribe(string topicId) } Rpc topicUpdate = new Rpc().WithTopics([topicId], []); - foreach (var peer in peerState) + foreach (KeyValuePair peer in peerState) { peer.Value.Send(topicUpdate); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index f80a2978..f77d10e6 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -10,7 +10,6 @@ using Nethermind.Libp2p.Protocols.Identify.Dto; using Nethermind.Libp2p.Protocols.Pubsub.Dto; using System.Collections.Concurrent; -using System.Diagnostics; namespace Nethermind.Libp2p.Protocols.Pubsub; @@ -34,7 +33,7 @@ public override string ToString() { //{string.Join("|", peerState.Select(x => $"{x.Key}:{x.Value.SendRpc is not null}"))} return $"Router#{routerId}: {localPeer?.Identity.PeerId ?? "null"}, " + - $"peers: {peerState.Count(x => x.Value.SendRpc is not null)}/{peerState.Count}, " + + $"peers: {peerState.Count(x => x.Value.SendRpc is not null)}/{peerState.Count} ({string.Join(",", peerState.Keys)}), " + $"mesh: {string.Join("|", mesh.Select(m => $"{m.Key}:{m.Value.Count}"))}, " + $"fanout: {string.Join("|", fanout.Select(m => $"{m.Key}:{m.Value.Count}"))}, " + $"fPeers: {string.Join("|", fPeers.Select(m => $"{m.Key}:{m.Value.Count}"))}, " + @@ -47,9 +46,10 @@ public override string ToString() class PubsubPeer { - public PubsubPeer(PeerId peerId, string protocolId) + public PubsubPeer(PeerId peerId, string protocolId, ILogger? logger) { PeerId = peerId; + _logger = logger; Protocol = protocolId switch { GossipsubProtocolVersionV10 => PubsubProtocol.GossipsubV10, @@ -89,11 +89,13 @@ public void Send(Rpc rpc) public Dictionary Backoff { get; internal set; } public ConcurrentQueue SendRpcQueue { get; } private Action? _sendRpc; + private readonly ILogger? _logger; + public Action? SendRpc { get => _sendRpc; set { - Debug.WriteLine($"Set SENDRPC for {this.PeerId}: {value}"); + _logger?.LogDebug($"Set SENDRPC for {PeerId}: {value}"); _sendRpc = value; if (_sendRpc is not null) lock (SendRpcQueue) @@ -262,7 +264,7 @@ public Task Heartbeat() { foreach (KeyValuePair> mesh in mesh) { - logger?.LogDebug($"MESH({localPeer!.Identity.PeerId}) {mesh.Key}: {mesh.Value.Count} ({mesh.Value})"); + logger?.LogDebug($"MESH({localPeer!.Identity.PeerId}) {mesh.Key}: {mesh.Value.Count} ({string.Join(",", mesh.Value)})"); if (mesh.Value.Count < settings.LowestDegree) { PeerId[] peersToGraft = gPeers[mesh.Key] @@ -283,7 +285,7 @@ public Task Heartbeat() foreach (PeerId? peerId in peerstoPrune) { mesh.Value.Remove(peerId); - var prune = new ControlPrune { TopicID = mesh.Key, Backoff = 60 }; + ControlPrune prune = new() { TopicID = mesh.Key, Backoff = 60 }; prune.Peers.AddRange(mesh.Value.ToArray().Select(pid => (PeerId: pid, Record: store.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo { PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), @@ -352,7 +354,7 @@ internal CancellationToken OutboundConnection(Multiaddress addr, string protocol return Canceled; } - PubsubPeer peer = peerState.GetOrAdd(peerId, (id) => new PubsubPeer(peerId, protocolId) { Address = addr, SendRpc = sendRpc, InititatedBy = ConnectionInitiation.Local }); + PubsubPeer peer = peerState.GetOrAdd(peerId, (id) => new PubsubPeer(peerId, protocolId, logger) { Address = addr, SendRpc = sendRpc, InititatedBy = ConnectionInitiation.Local }); lock (peer) { @@ -373,19 +375,19 @@ internal CancellationToken OutboundConnection(Multiaddress addr, string protocol { peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); peerState.TryRemove(peerId, out _); - foreach (var topicPeers in fPeers) + foreach (KeyValuePair> topicPeers in fPeers) { topicPeers.Value.Remove(peerId); } - foreach (var topicPeers in gPeers) + foreach (KeyValuePair> topicPeers in gPeers) { topicPeers.Value.Remove(peerId); } - foreach (var topicPeers in fanout) + foreach (KeyValuePair> topicPeers in fanout) { topicPeers.Value.Remove(peerId); } - foreach (var topicPeers in mesh) + foreach (KeyValuePair> topicPeers in mesh) { topicPeers.Value.Remove(peerId); } @@ -416,7 +418,7 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI } PubsubPeer? newPeer = null; - PubsubPeer existingPeer = peerState.GetOrAdd(peerId, (id) => newPeer = new PubsubPeer(peerId, protocolId) { Address = addr, InititatedBy = ConnectionInitiation.Remote }); + PubsubPeer existingPeer = peerState.GetOrAdd(peerId, (id) => newPeer = new PubsubPeer(peerId, protocolId, logger) { Address = addr, InititatedBy = ConnectionInitiation.Remote }); if (newPeer is not null) { logger?.LogDebug("Inbound, let's dial {peerId} via remotely initiated connection", peerId); @@ -424,19 +426,19 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI { peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); peerState.TryRemove(peerId, out _); - foreach (var topicPeers in fPeers) + foreach (KeyValuePair> topicPeers in fPeers) { topicPeers.Value.Remove(peerId); } - foreach (var topicPeers in gPeers) + foreach (KeyValuePair> topicPeers in gPeers) { topicPeers.Value.Remove(peerId); } - foreach (var topicPeers in fanout) + foreach (KeyValuePair> topicPeers in fanout) { topicPeers.Value.Remove(peerId); } - foreach (var topicPeers in mesh) + foreach (KeyValuePair> topicPeers in mesh) { topicPeers.Value.Remove(peerId); } @@ -464,7 +466,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) { foreach (Rpc.Types.SubOpts? sub in rpc.Subscriptions) { - var state = peerState.GetValueOrDefault(peerId); + PubsubPeer? state = peerState.GetValueOrDefault(peerId); if (state is null) { return; @@ -624,7 +626,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) .Ensure(r => r.Control.Prune) .Add(new ControlPrune { TopicID = prune.TopicID }); - foreach (var peer in prune.Peers) + foreach (PeerInfo? peer in prune.Peers) { // TODO verify payload type, signature, etc // TODO check if it's working diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs index 30709d50..6cab9db2 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs @@ -91,7 +91,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake() await Task.Delay(30000); - foreach (var router in routers) + foreach (PubsubRouter router in routers) { Assert.That(((IRoutingStateContainer)router).ConnectedPeers.Count, Is.EqualTo(totalCount - 1)); } @@ -130,7 +130,7 @@ public async Task Test_ConnectionEstablished_AfterHandshak3e() discoveries[i].BroadcastPeerInfo(); } - foreach (var router in setup.Routers.Values) + foreach (PubsubRouter router in setup.Routers.Values) { Assert.That(((IRoutingStateContainer)router).ConnectedPeers.Count, Is.EqualTo(totalCount - 1)); } diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs index f2a51c40..44df38ce 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs @@ -8,7 +8,7 @@ namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; class PubsubTestSetup { - static TestContextLoggerFactory fac = new TestContextLoggerFactory(); + static TestContextLoggerFactory fac = new(); public ChannelBus CommonBus { get; } = new(fac); public Dictionary Peers { get; } = new(); @@ -23,7 +23,7 @@ public async Task AddAsync(int count) for (int i = initialCount; i < initialCount + count; i++) { // But we create a seprate setup for every peer - Settings settings = new Settings + Settings settings = new() { HeartbeatInterval = int.MaxValue, }; diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs index 8a066f2f..d3067d98 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs @@ -36,7 +36,7 @@ public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, Canc token.Register(() => { - foreach (var topic in topics) + foreach (ITopic topic in topics) { topic.Unsubscribe(); } @@ -59,7 +59,7 @@ internal void BroadcastPeerInfo() throw new NullReferenceException($"{nameof(topics)} should be previously set in ${nameof(DiscoverAsync)}"); } - foreach (var topic in topics) + foreach (ITopic topic in topics) { topic.Publish(new Peer { diff --git a/src/libp2p/Libp2p.Protocols.Tls.Tests/Using.cs b/src/libp2p/Libp2p.Protocols.Tls.Tests/Using.cs index 2048bd47..bb339bea 100644 --- a/src/libp2p/Libp2p.Protocols.Tls.Tests/Using.cs +++ b/src/libp2p/Libp2p.Protocols.Tls.Tests/Using.cs @@ -1,11 +1,5 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -global using Nethermind.Libp2p.Core; -global using Nethermind.Libp2p.Core.TestsBase; -global using NSubstitute; global using NUnit.Framework; -global using Multiformats.Address; -global using System.Threading.Tasks; -global using Microsoft.Extensions.Logging; diff --git a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs index 123db134..444f9fb8 100644 --- a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs @@ -17,7 +17,7 @@ public class TlsProtocol(MultiplexerSettings? multiplexerSettings = null, ILogge private readonly ECDsa _sessionKey = ECDsa.Create(); private readonly ILogger? _logger = loggerFactory?.CreateLogger(); - public Lazy> ApplicationProtocols = new Lazy>(() => multiplexerSettings?.Multiplexers.Select(proto => new SslApplicationProtocol(proto.Id)).ToList() ?? []); + public Lazy> ApplicationProtocols = new(() => multiplexerSettings?.Multiplexers.Select(proto => new SslApplicationProtocol(proto.Id)).ToList() ?? []); public SslApplicationProtocol? LastNegotiatedApplicationProtocol { get; private set; } public string Id => "/tls/1.0.0"; diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 9d224dc2..56be74aa 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -252,7 +252,7 @@ await WriteHeaderAsync(contextId, channel, _logger?.LogDebug("Ctx({ctx}), stream {stream id}: New stream request acknowledged", contextId, streamId); } - await foreach (var upData in upChannel.ReadAllAsync()) + await foreach (ReadOnlySequence upData in upChannel.ReadAllAsync()) { _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Receive from upchannel, length={length}", contextId, streamId, upData.Length); diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index f9791738..649eabf8 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -10,7 +10,7 @@ using Nethermind.Libp2p.Protocols; using System.Text.RegularExpressions; -Regex omittedLogs = new Regex(".*(MDnsDiscoveryProtocol|IpTcpProtocol).*"); +Regex omittedLogs = new(".*(MDnsDiscoveryProtocol|IpTcpProtocol).*"); ServiceProvider serviceProvider = new ServiceCollection() .AddLibp2p(builder => builder.WithPubsub()) diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index 4ac9f922..2b006a0f 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -24,7 +24,7 @@ int testTimeoutSeconds = int.Parse(Environment.GetEnvironmentVariable("test_timeout_seconds") ?? "180"); - TestPlansPeerFactoryBuilder builder = new TestPlansPeerFactoryBuilder(transport, muxer, security); + TestPlansPeerFactoryBuilder builder = new(transport, muxer, security); IPeerFactory peerFactory = builder.Build(); Log($"Connecting to redis at {redisAddr}..."); @@ -62,7 +62,7 @@ { if (ip == "0.0.0.0") { - var d = NetworkInterface.GetAllNetworkInterfaces()! + List d = NetworkInterface.GetAllNetworkInterfaces()! .Where(i => i.Name == "eth0" || (i.OperationalStatus == OperationalStatus.Up && i.NetworkInterfaceType == NetworkInterfaceType.Ethernet)).ToList(); From 903a777aec4261150a4db8596e6ac1ad8184e91f Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Fri, 6 Dec 2024 14:38:56 +0300 Subject: [PATCH 12/25] Fix pubsub; reuse session --- .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 34 +- .../E2e/TestLocalPeer.cs | 38 -- .../E2e/TestMuxerProtocol.cs | 126 ++++--- .../E2e/TestPeerFactory.cs | 18 - .../Libp2p.Core.TestsBase.csproj | 1 + src/libp2p/Libp2p.Core/Discovery/PeerStore.cs | 40 ++- .../Dto/PeerRecord.cs | 22 +- .../Dto/PeerRecord.proto | 2 +- src/libp2p/Libp2p.Core/Dto/SigningHelper.cs | 67 ++++ .../Libp2p.Core/Exceptions/Libp2pException.cs | 24 +- .../Libp2p.Core/Extensions/TaskHelper.cs | 6 + src/libp2p/Libp2p.Core/Libp2p.Core.csproj | 3 + src/libp2p/Libp2p.Core/Peer.cs | 90 +++-- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 44 ++- .../IdentifyProtocol.cs | 72 +--- .../Libp2p.Protocols.Identify.csproj | 7 - .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 23 +- .../Libp2p.Protocols.Ping/PingProtocol.cs | 11 + .../Libp2p.Protocols.Pubsub.E2eTests.csproj} | 0 .../Program.cs | 23 ++ .../PubSubTestSetup.cs | 141 ++++++++ .../Program.cs | 95 ----- .../{Settings.cs => PubSubSettings.cs} | 7 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 33 +- .../PubsubRouter.Topics.cs | 2 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 338 +++++++++--------- .../Libp2p.Protocols.Pubsub/RpcExtensions.cs | 4 +- .../E2eTests.cs | 33 ++ ...Protocols.PubsubPeerDiscovery.Tests.csproj | 1 + .../MultistreamProtocolTests.cs | 138 ------- .../PubSubTestSetup.cs | 58 --- .../PubsubTestSetupExtensions.cs | 16 + src/libp2p/Libp2p.sln | 2 +- src/libp2p/Libp2p/Libp2pPeerFactory.cs | 6 +- 34 files changed, 782 insertions(+), 743 deletions(-) delete mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs delete mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs rename src/libp2p/{Libp2p.Protocols.Identify => Libp2p.Core}/Dto/PeerRecord.cs (94%) rename src/libp2p/{Libp2p.Protocols.Identify => Libp2p.Core}/Dto/PeerRecord.proto (89%) create mode 100644 src/libp2p/Libp2p.Core/Dto/SigningHelper.cs rename src/libp2p/{Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj => Libp2p.Protocols.Pubsub.E2eTests/Libp2p.Protocols.Pubsub.E2eTests.csproj} (100%) create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs delete mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs rename src/libp2p/Libp2p.Protocols.Pubsub/{Settings.cs => PubSubSettings.cs} (92%) create mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs delete mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs delete mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index dfc68852..67675aea 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -1,9 +1,14 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Protocols; +using Nethermind.Libp2p.Stack; +using System.Collections.Concurrent; + namespace Nethermind.Libp2p.Core.TestsBase.E2e; -public class TestBuilder(IServiceProvider? serviceProvider = null) : PeerFactoryBuilderBase(serviceProvider) +public class TestBuilder(IServiceProvider? serviceProvider = null) : PeerFactoryBuilderBase(serviceProvider) { protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { @@ -12,9 +17,36 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) Connect([root], [ Get(), + Get(), .. additionalProtocols ]); return [root]; } } + +public class TestPeerFactory(IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings) +{ + ConcurrentDictionary peers = new(); + + public override IPeer Create(Identity? identity = default) + { + ArgumentNullException.ThrowIfNull(identity); + return peers.GetOrAdd(identity.PeerId, (p) => new TestLocalPeer(identity, protocolStackSettings, loggerFactory)); + } +} + +internal class TestLocalPeer(Identity id, IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : LocalPeer(id, protocolStackSettings, loggerFactory) +{ + protected override async Task ConnectedTo(ISession session, bool isDialer) + { + try + { + await session.DialAsync(); + } + catch + { + + } + } +} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs deleted file mode 100644 index 4ff6aa02..00000000 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestLocalPeer.cs +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Multiformats.Address; -using System.Collections.ObjectModel; - -namespace Nethermind.Libp2p.Core.TestsBase.E2e; - -internal class TestLocalPeer(Identity id) : IPeer -{ - public Identity Identity { get => id; set => throw new NotImplementedException(); } - public Multiaddress Address { get => $"/p2p/{id.PeerId}"; set => throw new NotImplementedException(); } - - public ObservableCollection ListenAddresses => throw new NotImplementedException(); - - public event Connected? OnConnected; - - public Task DialAsync(Multiaddress[] samePeerAddrs, CancellationToken token = default) - { - throw new NotImplementedException(); - } - - public Task DisconnectAsync() - { - throw new NotImplementedException(); - } - - - public Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) - { - throw new NotImplementedException(); - } - - Task IPeer.DialAsync(Multiaddress addr, CancellationToken token) - { - throw new NotImplementedException(); - } -} diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index 47af966c..0b7b49c3 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -3,6 +3,8 @@ using Microsoft.Extensions.Logging; using Multiformats.Address; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Dto; +using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Core.TestsBase.Dto; using Nethermind.Libp2p.Core.TestsBase.E2e; using Org.BouncyCastle.Utilities.Encoders; @@ -20,14 +22,14 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, { logger?.LogDebug($"{context.Peer.Identity.PeerId}: Dial async"); - await Task.Run(async () => - { - IChannel chan = bus.Dial(context.Peer.Identity.PeerId, remoteAddr.GetPeerId()!); - using INewConnectionContext connection = context.CreateConnection(); - connection.State.RemoteAddress = remoteAddr; + //await Task.Run(async () => + //{ + IChannel chan = bus.Dial(context.Peer.Identity.PeerId, remoteAddr.GetPeerId()!); + using INewConnectionContext connection = context.CreateConnection(); + connection.State.RemoteAddress = remoteAddr; - await HandleRemote(chan, connection, context); - }); + await HandleRemote(chan, connection, context); + //}); } public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) @@ -36,9 +38,23 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listen async"); await foreach (IChannel item in bus.GetIncomingRequests(context.Peer.Identity.PeerId)) { - using INewConnectionContext connection = context.CreateConnection(); - logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener handles new con"); - _ = HandleRemote(item, connection, context, true); + _ = Task.Run(async () => + { + INewConnectionContext connection = context.CreateConnection(); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener handles new con"); + try + { + await HandleRemote(item, connection, context, true); + } + catch (SessionExistsException) + { + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener rejected inititation of a redundant session"); + } + catch (Exception e) + { + logger?.LogError(e, $"{context.Peer.Identity.PeerId}: Listener exception"); + } + }, token); } } @@ -47,32 +63,36 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn uint counter = isListen ? 1u : 0u; Dictionary chans = []; - string peer = ""; + PublicKey? remotePublicKey; + PeerId? remotePeerId; if (isListen) { - peer = await downChannel.ReadLineAsync(); - await downChannel.WriteLineAsync(context.Peer.Identity.PeerId!.ToString()); - logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener handles remote {peer}"); + remotePublicKey = await downChannel.ReadPrefixedProtobufAsync(PublicKey.Parser); + remotePeerId = new PeerId(remotePublicKey); + await downChannel.WriteSizeAndProtobufAsync(context.Peer.Identity.PublicKey); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Listener handles remote {remotePeerId}"); } else { - await downChannel.WriteLineAsync(context.Peer.Identity.PeerId!.ToString()); - peer = await downChannel.ReadLineAsync(); - logger?.LogDebug($"{context.Peer.Identity.PeerId}: Dialer handles remote {peer}"); + await downChannel.WriteSizeAndProtobufAsync(context.Peer.Identity.PublicKey); + remotePublicKey = await downChannel.ReadPrefixedProtobufAsync(PublicKey.Parser); + remotePeerId = new PeerId(remotePublicKey); + logger?.LogDebug($"{context.Peer.Identity.PeerId}: Dialer handles remote {remotePeerId}"); } - connection.State.RemoteAddress = $"/p2p/{peer}"; - using INewSessionContext session = connection.UpgradeToSession(); + connection.State.RemotePublicKey = remotePublicKey; + connection.State.RemoteAddress = $"/p2p/{remotePeerId}"; + using INewSessionContext? session = connection.UpgradeToSession(); - string logPrefix = $"{context.Peer.Identity.PeerId}<>{peer}"; + string logPrefix = $"{context.Peer.Identity.PeerId}<>{remotePeerId}"; _ = Task.Run(() => { foreach (UpgradeOptions item in session.DialRequests) { uint chanId = Interlocked.Add(ref counter, 2); - logger?.LogDebug($"{context.Peer.Identity.PeerId}({chanId}): Sub-request {item.SelectedProtocol} {item.CompletionSource is not null} from {connection.State.RemoteAddress.GetPeerId()}"); + logger?.LogDebug($"{context.Peer.Identity.PeerId}({chanId}): Sub-request {item.SelectedProtocol} {item.CompletionSource is not null} to call {connection.State.RemoteAddress.GetPeerId()}"); chans[chanId] = new MuxerChannel { Tcs = item.CompletionSource }; MuxerPacket response = new() @@ -122,7 +142,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn } }; - UpgradeOptions req = new() { SelectedProtocol = selected, ModeOverride = UpgradeModeOverride.Dial }; + UpgradeOptions req = new() { SelectedProtocol = selected, ModeOverride = UpgradeModeOverride.Listen }; IChannel upChannel = session.Upgrade(req); chans[packet.ChannelId] = new MuxerChannel { UpChannel = upChannel }; @@ -162,23 +182,38 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn } break; case MuxerPacketType.Data: - logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Data to upchanel {packet.Data?.Length ?? 0} {Hex.ToHexString(packet.Data?.ToByteArray() ?? [])}"); - _ = chans[packet.ChannelId].UpChannel!.WriteAsync(new ReadOnlySequence(packet.Data.ToByteArray())); + if (packet.Data is null or []) + { + logger?.LogWarning($"{logPrefix}({packet.ChannelId}): Empty data received"); + break; + } + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Data to upchanel {packet.Data.Length} {Hex.ToHexString(packet.Data.ToByteArray())}"); + _ = chans.GetValueOrDefault(packet.ChannelId)?.UpChannel?.WriteAsync(new ReadOnlySequence(packet.Data.ToByteArray())); break; case MuxerPacketType.CloseWrite: logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Remote EOF"); - chans[packet.ChannelId].RemoteClosedWrites = true; - _ = chans[packet.ChannelId].UpChannel!.WriteEofAsync(); + lock (chans[packet.ChannelId]) + { + chans[packet.ChannelId].RemoteClosedWrites = true; + + _ = chans[packet.ChannelId].UpChannel?.WriteEofAsync(); + + if (chans[packet.ChannelId].LocalClosedWrites) + { + chans[packet.ChannelId].Tcs?.SetResult(); + _ = chans[packet.ChannelId].UpChannel?.CloseAsync(); + chans.Remove(packet.ChannelId); + } + } break; default: break; } } - catch + catch (Exception e) { - - + logger?.LogError(e, $"{logPrefix}: Muxer listener exception"); } } @@ -206,23 +241,31 @@ private Task HandleUpchannelData(IChannel downChannel, Dictionary Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + logger?.LogDebug($"{logPrefix}({channelId}): Upchannel write close"); - _ = downChannel.WriteSizeAndProtobufAsync(packet); + { + MuxerPacket packet = new() + { + ChannelId = channelId, + Type = MuxerPacketType.CloseWrite, + }; + + logger?.LogDebug($"{logPrefix}({packet.ChannelId}): > Packet {packet.Type} {string.Join(",", packet.Protocols)} {packet.Data?.Length ?? 0}"); + + _ = downChannel.WriteSizeAndProtobufAsync(packet); + } } } catch @@ -237,5 +280,6 @@ class MuxerChannel public IChannel? UpChannel { get; set; } public TaskCompletionSource? Tcs { get; set; } public bool RemoteClosedWrites { get; set; } + public bool LocalClosedWrites { get; set; } } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs deleted file mode 100644 index 3079f3ee..00000000 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestPeerFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Nethermind.Libp2p.Stack; -using System.Collections.Concurrent; - -namespace Nethermind.Libp2p.Core.TestsBase.E2e; - -internal class TestPeerFactory(IProtocolStackSettings protocolStackSettings) : PeerFactory(protocolStackSettings) -{ - ConcurrentDictionary peers = new(); - - public override IPeer Create(Identity? identity = default) - { - ArgumentNullException.ThrowIfNull(identity); - return peers.GetOrAdd(identity.PeerId, (p) => new TestLocalPeer(identity)); - } -} diff --git a/src/libp2p/Libp2p.Core.TestsBase/Libp2p.Core.TestsBase.csproj b/src/libp2p/Libp2p.Core.TestsBase/Libp2p.Core.TestsBase.csproj index 01c7ec59..bec831a2 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/Libp2p.Core.TestsBase.csproj +++ b/src/libp2p/Libp2p.Core.TestsBase/Libp2p.Core.TestsBase.csproj @@ -33,6 +33,7 @@ + diff --git a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs index 042b435d..0b136e43 100644 --- a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs +++ b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs @@ -3,13 +3,38 @@ using Google.Protobuf; using Multiformats.Address; +using Nethermind.Libp2p.Core.Dto; using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core.Discovery; public class PeerStore { - ConcurrentDictionary store = []; + private readonly ConcurrentDictionary _store = []; + + public void Discover(ByteString signedPeerRecord) + { + SignedEnvelope signedEnvelope = SignedEnvelope.Parser.ParseFrom(signedPeerRecord); + PublicKey publicKey = PublicKey.Parser.ParseFrom(signedEnvelope.PublicKey); + PeerId peerId = new Identity(publicKey).PeerId; + + if (!SigningHelper.VerifyPeerRecord(signedEnvelope, publicKey)) + { + return; + } + + Multiaddress[] addresses = PeerRecord.Parser.ParseFrom(signedEnvelope.Payload).Addresses + .Select(ai => Multiaddress.Decode(ai.Multiaddr.ToByteArray())) + .Where(a => a.GetPeerId() == peerId) + .ToArray(); + + if (addresses.Length == 0) + { + return; + } + + Discover(addresses); + } public void Discover(Multiaddress[] addrs) { @@ -23,8 +48,8 @@ public void Discover(Multiaddress[] addrs) if (peerId is not null) { PeerInfo? newOne = null; - PeerInfo peerInfo = store.GetOrAdd(peerId, (id) => newOne = new PeerInfo { Addrs = [.. addrs] }); - if (peerInfo != newOne && peerInfo.Addrs is not null && peerInfo.Addrs.Count == addrs.Length && addrs.All(peerInfo.Addrs.Contains)) + PeerInfo peerInfo = _store.GetOrAdd(peerId, (id) => newOne = new PeerInfo { Addrs = [.. addrs] }); + if (peerInfo != newOne && peerInfo.Addrs is not null && peerInfo.Addrs.Count == addrs.Length && addrs.All(a => peerInfo.Addrs.Any(a2 => a2.ToString() == a.ToString()))) { return; } @@ -44,7 +69,7 @@ public event Action? OnNewPeer } onNewPeer += value; - foreach (PeerInfo? item in store.Select(x => x.Value).ToArray()) + foreach (PeerInfo? item in _store.Select(x => x.Value).ToArray()) { if (item.Addrs is not null) value.Invoke(item.Addrs.ToArray()); } @@ -55,14 +80,11 @@ public event Action? OnNewPeer } } - public override string ToString() - { - return $"peerStore({store.Count}):{string.Join(",", store.Select(x => x.Key.ToString() ?? "null"))}"; - } + public override string ToString() => $"peerStore({_store.Count}):{string.Join(",", _store.Select(x => x.Key.ToString() ?? "null"))}"; public PeerInfo GetPeerInfo(PeerId peerId) { - return store.GetOrAdd(peerId, id => new PeerInfo()); + return _store.GetOrAdd(peerId, id => new PeerInfo()); } public class PeerInfo diff --git a/src/libp2p/Libp2p.Protocols.Identify/Dto/PeerRecord.cs b/src/libp2p/Libp2p.Core/Dto/PeerRecord.cs similarity index 94% rename from src/libp2p/Libp2p.Protocols.Identify/Dto/PeerRecord.cs rename to src/libp2p/Libp2p.Core/Dto/PeerRecord.cs index 2e2eef5f..b7099a92 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/Dto/PeerRecord.cs +++ b/src/libp2p/Libp2p.Core/Dto/PeerRecord.cs @@ -9,7 +9,7 @@ using pbc = global::Google.Protobuf.Collections; using pbr = global::Google.Protobuf.Reflection; using scg = global::System.Collections.Generic; -namespace Nethermind.Libp2p.Protocols.Identify.Dto { +namespace Nethermind.Libp2p.Core.Dto { /// Holder for reflection information generated from PeerRecord.proto public static partial class PeerRecordReflection { @@ -26,13 +26,13 @@ static PeerRecordReflection() { string.Concat( "ChBQZWVyUmVjb3JkLnByb3RvIiAKC0FkZHJlc3NJbmZvEhEKCW11bHRpYWRk", "chgBIAIoDCJLCgpQZWVyUmVjb3JkEg8KB3BlZXJfaWQYASACKAwSCwoDc2Vx", - "GAIgAigEEh8KCWFkZHJlc3NlcxgDIAMoCzIMLkFkZHJlc3NJbmZvQiuqAihO", - "ZXRoZXJtaW5kLkxpYnAycC5Qcm90b2NvbHMuSWRlbnRpZnkuRHRv")); + "GAIgAigEEh8KCWFkZHJlc3NlcxgDIAMoCzIMLkFkZHJlc3NJbmZvQh2qAhpO", + "ZXRoZXJtaW5kLkxpYnAycC5Db3JlLkR0bw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Protocols.Identify.Dto.AddressInfo), global::Nethermind.Libp2p.Protocols.Identify.Dto.AddressInfo.Parser, new[]{ "Multiaddr" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Protocols.Identify.Dto.PeerRecord), global::Nethermind.Libp2p.Protocols.Identify.Dto.PeerRecord.Parser, new[]{ "PeerId", "Seq", "Addresses" }, null, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Core.Dto.AddressInfo), global::Nethermind.Libp2p.Core.Dto.AddressInfo.Parser, new[]{ "Multiaddr" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Core.Dto.PeerRecord), global::Nethermind.Libp2p.Core.Dto.PeerRecord.Parser, new[]{ "PeerId", "Seq", "Addresses" }, null, null, null, null) })); } #endregion @@ -54,7 +54,7 @@ public sealed partial class AddressInfo : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Nethermind.Libp2p.Protocols.Identify.Dto.PeerRecordReflection.Descriptor.MessageTypes[0]; } + get { return global::Nethermind.Libp2p.Core.Dto.PeerRecordReflection.Descriptor.MessageTypes[0]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -267,7 +267,7 @@ public sealed partial class PeerRecord : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::Nethermind.Libp2p.Protocols.Identify.Dto.PeerRecordReflection.Descriptor.MessageTypes[1]; } + get { return global::Nethermind.Libp2p.Core.Dto.PeerRecordReflection.Descriptor.MessageTypes[1]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -361,15 +361,15 @@ public void ClearSeq() { /// Field number for the "addresses" field. public const int AddressesFieldNumber = 3; - private static readonly pb::FieldCodec _repeated_addresses_codec - = pb::FieldCodec.ForMessage(26, global::Nethermind.Libp2p.Protocols.Identify.Dto.AddressInfo.Parser); - private readonly pbc::RepeatedField addresses_ = new pbc::RepeatedField(); + private static readonly pb::FieldCodec _repeated_addresses_codec + = pb::FieldCodec.ForMessage(26, global::Nethermind.Libp2p.Core.Dto.AddressInfo.Parser); + private readonly pbc::RepeatedField addresses_ = new pbc::RepeatedField(); /// /// addresses is a list of public listen addresses for the peer. /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pbc::RepeatedField Addresses { + public pbc::RepeatedField Addresses { get { return addresses_; } } diff --git a/src/libp2p/Libp2p.Protocols.Identify/Dto/PeerRecord.proto b/src/libp2p/Libp2p.Core/Dto/PeerRecord.proto similarity index 89% rename from src/libp2p/Libp2p.Protocols.Identify/Dto/PeerRecord.proto rename to src/libp2p/Libp2p.Core/Dto/PeerRecord.proto index 186cd3b2..db1628e0 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/Dto/PeerRecord.proto +++ b/src/libp2p/Libp2p.Core/Dto/PeerRecord.proto @@ -1,6 +1,6 @@ syntax = "proto2"; -option csharp_namespace = "Nethermind.Libp2p.Protocols.Identify.Dto"; +option csharp_namespace = "Nethermind.Libp2p.Core.Dto"; message AddressInfo { required bytes multiaddr = 1; diff --git a/src/libp2p/Libp2p.Core/Dto/SigningHelper.cs b/src/libp2p/Libp2p.Core/Dto/SigningHelper.cs new file mode 100644 index 00000000..596b6714 --- /dev/null +++ b/src/libp2p/Libp2p.Core/Dto/SigningHelper.cs @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Google.Protobuf; +using Multiformats.Address; + +namespace Nethermind.Libp2p.Core.Dto; + +public static class SigningHelper +{ + private static readonly byte[] Libp2pPeerRecordAsArray = [((ushort)Enums.Libp2p.Libp2pPeerRecord >> 8) & 0xFF, (ushort)Enums.Libp2p.Libp2pPeerRecord & 0xFF]; + + public static bool VerifyPeerRecord(ByteString signedEnvelopeBytes, PublicKey publicKey) + { + SignedEnvelope signedEnvelope = SignedEnvelope.Parser.ParseFrom(signedEnvelopeBytes); + return VerifyPeerRecord(signedEnvelope, publicKey); + } + + public static bool VerifyPeerRecord(SignedEnvelope signedEnvelope, PublicKey publicKey) + { + Identity identity = new(publicKey); + + if (signedEnvelope.PayloadType?.Take(2).SequenceEqual(Libp2pPeerRecordAsArray) is not true) + { + return false; + } + + PeerRecord pr = PeerRecord.Parser.ParseFrom(signedEnvelope.Payload); + + if (identity.PeerId != new PeerId(pr.PeerId.ToByteArray())) + { + return false; + } + + SignedEnvelope envelopeWithoutSignature = signedEnvelope.Clone(); + envelopeWithoutSignature.ClearSignature(); + + return identity.VerifySignature(envelopeWithoutSignature.ToByteArray(), signedEnvelope.Signature.ToByteArray()); + } + + public static ByteString CreateSignedEnvelope(Identity identity, Multiaddress[] addresses, ulong seq) + { + PeerRecord paylaod = new() + { + PeerId = ByteString.CopyFrom(identity.PeerId.Bytes), + Seq = seq + }; + + foreach (Multiaddress address in addresses) + { + paylaod.Addresses.Add(new AddressInfo + { + Multiaddr = ByteString.CopyFrom(address.ToBytes()) + }); + } + + SignedEnvelope envelope = new() + { + PayloadType = ByteString.CopyFrom(Libp2pPeerRecordAsArray), + Payload = paylaod.ToByteString(), + PublicKey = identity.PublicKey.ToByteString(), + }; + + envelope.Signature = ByteString.CopyFrom(identity.Sign(envelope.ToByteArray())); + return envelope.ToByteString(); + } +} diff --git a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs index 3da5c397..a841eebb 100644 --- a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs +++ b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs @@ -15,15 +15,15 @@ public Libp2pException() : base() } } -public class ChannelClosedException : Libp2pException -{ - public ChannelClosedException() - { - - } -} - -public class Libp2pSetupException(string? message = null) : Libp2pException(message) -{ - -} +public class ChannelClosedException : Libp2pException; + +/// +/// Appears when libp2p is not set up properly in part of protocol tack, IoC, etc. +/// +/// +public class Libp2pSetupException(string? message = null) : Libp2pException(message); + +/// +/// Appears when there is already active session for the given peer +/// +public class SessionExistsException(PeerId remotePeerId) : Libp2pException($"Session is already established with {remotePeerId}"); diff --git a/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs index 39ce2f36..0797e0ea 100644 --- a/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs +++ b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Nethermind.Libp2p.Core.Exceptions; + namespace Nethermind.Libp2p.Core.Extensions; internal static class TaskHelper @@ -15,6 +17,10 @@ public static async Task FirstSuccess(params Task[] tasks) { tcs.TrySetResult(t); } + if (t.IsFaulted && t.Exception.InnerException is SessionExistsException) + { + tcs.TrySetResult(t); + } }))); Task result = await Task.WhenAny(tcs.Task, all); diff --git a/src/libp2p/Libp2p.Core/Libp2p.Core.csproj b/src/libp2p/Libp2p.Core/Libp2p.Core.csproj index 8868b8b7..ddb785e4 100644 --- a/src/libp2p/Libp2p.Core/Libp2p.Core.csproj +++ b/src/libp2p/Libp2p.Core/Libp2p.Core.csproj @@ -19,6 +19,9 @@ + + Never + diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 196f9fd3..c2dda35a 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -12,17 +12,31 @@ namespace Nethermind.Libp2p.Core; -public class LocalPeer(Identity identity, IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : IPeer +public class LocalPeer : IPeer { - private readonly ILogger? _logger = loggerFactory?.CreateLogger(); + protected readonly ILogger? _logger; + protected readonly IProtocolStackSettings _protocolStackSettings; - protected IProtocolStackSettings protocolStackSettings = protocolStackSettings; + Dictionary> listenerReadyTcs = new(); + private ObservableCollection sessions { get; } = []; + + + public LocalPeer(Identity identity, IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) + { + Identity = identity; + _protocolStackSettings = protocolStackSettings; + _logger = loggerFactory?.CreateLogger($"peer-{identity.PeerId}"); + } + + public override string ToString() + { + return $"peer({Identity.PeerId}): sessions {string.Join("|", sessions.Select(x => $"{x.State.RemotePeerId}"))}"; + } - public Identity Identity { get; } = identity; + public Identity Identity { get; } public ObservableCollection ListenAddresses { get; } = []; - private ObservableCollection sessions { get; } = []; protected virtual Task ConnectedTo(ISession peer, bool isDialer) => Task.CompletedTask; @@ -72,18 +86,18 @@ public Task DisconnectAsync() protected virtual ProtocolRef SelectProtocol(Multiaddress addr) { - if (protocolStackSettings.TopProtocols is null) + if (_protocolStackSettings.TopProtocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - if (protocolStackSettings.TopProtocols.Length is not 1) + if (_protocolStackSettings.TopProtocols.Length is not 1) { throw new Libp2pSetupException("Top protocol should be single one by default"); } - return protocolStackSettings.TopProtocols.Single(); + return _protocolStackSettings.TopProtocols.Single(); } protected virtual IEnumerable PrepareAddresses(Multiaddress[] addrs) @@ -101,7 +115,6 @@ protected virtual IEnumerable PrepareAddresses(Multiaddress[] addr } } - Dictionary> listenerReadyTcs = new(); public event Connected? OnConnected; @@ -154,19 +167,17 @@ public INewConnectionContext CreateConnection(ProtocolRef proto, Session? sessio public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, bool isListener) { - if (session.State.RemoteAddress?.GetPeerId() is null) - { + PeerId? remotePeerId = session.State.RemotePeerId ?? throw new Libp2pSetupException($"{nameof(session.State.RemoteAddress)} should be initialiazed before session creation"); - } lock (sessions) { - if (sessions.Any(s => !ReferenceEquals(session, s) && s.State.RemoteAddress.GetPeerId() == session.State.RemoteAddress?.GetPeerId())) + if (sessions.Any(s => !ReferenceEquals(session, s) && s.State.RemoteAddress.GetPeerId() == remotePeerId)) { _ = session.DisconnectAsync(); - throw new Libp2pException("Session is already established"); + throw new SessionExistsException(remotePeerId); } - + _logger?.LogDebug($"New session with {remotePeerId}"); sessions.Add(session); } @@ -176,6 +187,7 @@ public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, b if (t.IsFaulted) { _ = session.DisconnectAsync(); + _logger?.LogError(t.Exception.InnerException, $"Disconnecting due to exception"); return; } session.ConnectedTcs.SetResult(); @@ -186,33 +198,41 @@ public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, b internal IEnumerable GetProtocolsFor(ProtocolRef protocol) { - if (protocolStackSettings.Protocols is null) + if (_protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - if (!protocolStackSettings.Protocols.ContainsKey(protocol)) + if (!_protocolStackSettings.Protocols.ContainsKey(protocol)) { throw new Libp2pSetupException($"{protocol} is not added"); } - return protocolStackSettings.Protocols[protocol].Select(p => p.Protocol); + return _protocolStackSettings.Protocols[protocol].Select(p => p.Protocol); } internal IProtocol? GetProtocolInstance() { - return protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.Protocol.GetType() == typeof(TProtocol))?.Protocol; + return _protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.Protocol.GetType() == typeof(TProtocol))?.Protocol; } public async Task DialAsync(Multiaddress[] addrs, CancellationToken token) { + PeerId? remotePeerId = addrs.FirstOrDefault()?.GetPeerId(); + ISession? existingSession = sessions.FirstOrDefault(s => s.State.RemotePeerId == remotePeerId); + + if (existingSession is not null) + { + return existingSession; + } + Dictionary cancellations = new(); foreach (Multiaddress addr in addrs) { cancellations[addr] = CancellationTokenSource.CreateLinkedTokenSource(token); } - Task timeoutTask = Task.Delay(15000_000, token); + Task timeoutTask = Task.Delay(15_000, token); Task wait = await TaskHelper.FirstSuccess([timeoutTask, .. addrs.Select(addr => DialAsync(addr, cancellations[addr].Token))]); if (wait == timeoutTask) @@ -263,19 +283,19 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) { - if (protocolStackSettings.Protocols is null) + if (_protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - if (!protocolStackSettings.Protocols.ContainsKey(parentProtocol)) + if (!_protocolStackSettings.Protocols.ContainsKey(parentProtocol)) { throw new Libp2pSetupException($"{parentProtocol} is not added"); } ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : - options?.SelectedProtocol is not null ? protocolStackSettings.Protocols[parentProtocol].SingleOrDefault(x => x.Protocol == options.SelectedProtocol) ?? new ProtocolRef(options.SelectedProtocol) : - protocolStackSettings.Protocols[parentProtocol].Single(); + options?.SelectedProtocol is not null ? _protocolStackSettings.Protocols[parentProtocol].SingleOrDefault(x => x.Protocol == options.SelectedProtocol) ?? new ProtocolRef(options.SelectedProtocol) : + _protocolStackSettings.Protocols[parentProtocol].Single(); Channel downChannel = new(); @@ -319,6 +339,7 @@ internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol { _logger?.LogError($"Upgrade task failed for {top} with {t.Exception}"); } + _ = downChannel.CloseAsync(); }); return downChannel; @@ -341,19 +362,19 @@ private static void MapToTaskCompletionSource(Task t, TaskCompletionSource tcs) internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) { - if (protocolStackSettings.Protocols is null) + if (_protocolStackSettings.Protocols is null) { - throw new Libp2pSetupException($"Protocols are not set in {nameof(protocolStackSettings)}"); + throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - if (upgradeProtocol is not null && !protocolStackSettings.Protocols[protocol].Any(p => p.Protocol == upgradeProtocol)) + if (upgradeProtocol is not null && !_protocolStackSettings.Protocols[protocol].Any(p => p.Protocol == upgradeProtocol)) { - protocolStackSettings.Protocols.Add(new ProtocolRef(upgradeProtocol, false), []); + _protocolStackSettings.Protocols.Add(new ProtocolRef(upgradeProtocol, false), []); } ProtocolRef top = upgradeProtocol is not null ? - protocolStackSettings.Protocols[protocol].FirstOrDefault(p => p.Protocol == upgradeProtocol, protocolStackSettings.Protocols.Keys.First(k => k.Protocol == upgradeProtocol)) : - protocolStackSettings.Protocols[protocol].Single(); + _protocolStackSettings.Protocols[protocol].FirstOrDefault(p => p.Protocol == upgradeProtocol, _protocolStackSettings.Protocols.Keys.First(k => k.Protocol == upgradeProtocol)) : + _protocolStackSettings.Protocols[protocol].Single(); isListener = options?.ModeOverride switch { UpgradeModeOverride.Dial => false, UpgradeModeOverride.Listen => true, _ => isListener }; @@ -393,10 +414,11 @@ await upgradeTask.ContinueWith(t => { _logger?.LogError($"Upgrade task failed with {t.Exception}"); } + _ = parentChannel.CloseAsync(); }); } - public Task DisconnectAsync() => Task.WhenAll(sessions.Select(s => s.DisconnectAsync())); + public Task DisconnectAsync() => Task.WhenAll(sessions.ToArray().Select(s => s.DisconnectAsync())); } public class NewSessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), INewSessionContext diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 2d1f77e5..f81d01ff 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -6,27 +6,6 @@ namespace Nethermind.Libp2p.Core; -public static class PeerFactoryBuilderBase -{ - private static readonly HashSet protocols = []; - - internal static TProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol - { - if (instance is not null) - { - protocols.Add(instance); - } - - IProtocol? existing = instance ?? protocols.OfType().FirstOrDefault(); - if (existing is null) - { - existing = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); - protocols.Add(existing); - } - return (TProtocol)existing; - } -} - public class ProtocolRef(IProtocol protocol, bool isExposed = true) { static int RefIdCounter = 0; @@ -48,6 +27,25 @@ public abstract class PeerFactoryBuilderBase : IPeerFact where TBuilder : PeerFactoryBuilderBase, IPeerFactoryBuilder where TPeerFactory : IPeerFactory { + private readonly HashSet protocolInstances = []; + + private TProtocol CreateProtocolInstance(IServiceProvider serviceProvider, TProtocol? instance = default) where TProtocol : IProtocol + { + if (instance is not null) + { + protocolInstances.Add(instance); + } + + IProtocol? existing = instance ?? protocolInstances.OfType().FirstOrDefault(); + if (existing is null) + { + existing = ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider); + protocolInstances.Add(existing); + } + return (TProtocol)existing; + } + + private readonly List _appLayerProtocols = new(); public IEnumerable AppLayerProtocols => _appLayerProtocols.Select(x => x.Protocol); @@ -60,7 +58,7 @@ protected PeerFactoryBuilderBase(IServiceProvider? serviceProvider = default) public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = default, bool isExposed = true) where TProtocol : IProtocol { - _appLayerProtocols.Add(new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider!, instance), isExposed)); + _appLayerProtocols.Add(new ProtocolRef(CreateProtocolInstance(ServiceProvider!, instance), isExposed)); return (TBuilder)this; } @@ -90,7 +88,7 @@ protected ProtocolRef[] Connect(ProtocolRef[] protocols, params ProtocolRef[][] protected ProtocolRef Get() where TProtocol : IProtocol { - return new ProtocolRef(PeerFactoryBuilderBase.CreateProtocolInstance(ServiceProvider)); + return new ProtocolRef(CreateProtocolInstance(ServiceProvider)); } public IPeerFactory Build() diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index ce81d15e..6c0a7a3d 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -5,15 +5,12 @@ using Microsoft.Extensions.Logging; using Multiformats.Address.Net; using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Core.Dto; using Nethermind.Libp2p.Protocols.Identify; using Nethermind.Libp2p.Stack; using System.Net.Sockets; -using Multiformats.Address; -using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Protocols.Identify.Dto; using Nethermind.Libp2p.Core.Exceptions; +using Nethermind.Libp2p.Core.Dto; namespace Nethermind.Libp2p.Protocols; @@ -26,11 +23,9 @@ public class IdentifyProtocol : ISessionProtocol private readonly string _protocolVersion; private readonly ILogger? _logger; - private readonly IPeerFactoryBuilder _peerFactoryBuilder; private readonly PeerStore? _peerStore; private readonly IProtocolStackSettings _protocolStackSettings; - private static readonly byte[] Libp2pPeerRecordAsArray = [((ushort)Core.Enums.Libp2p.Libp2pPeerRecord >> 8) & 0xFF, (ushort)Core.Enums.Libp2p.Libp2pPeerRecord & 0xFF]; public string Id => "/ipfs/id/1.0.0"; @@ -47,6 +42,8 @@ public IdentifyProtocol(IProtocolStackSettings protocolStackSettings, IdentifyPr public async Task DialAsync(IChannel channel, ISessionContext context) { + ArgumentNullException.ThrowIfNull(context.State.RemotePublicKey); + _logger?.LogInformation("Dial"); Identify.Dto.Identify identify = await channel.ReadPrefixedProtobufAsync(Identify.Dto.Identify.Parser); @@ -55,7 +52,7 @@ public async Task DialAsync(IChannel channel, ISessionContext context) if (_peerStore is not null && identify.SignedPeerRecord is not null) { - if (!VerifyPeerRecord(identify.SignedPeerRecord, context.State.RemotePublicKey)) + if (!SigningHelper.VerifyPeerRecord(identify.SignedPeerRecord, context.State.RemotePublicKey)) { throw new PeerConnectionException(); } @@ -87,7 +84,7 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) ListenAddrs = { context.Peer.ListenAddresses.Select(x => ByteString.CopyFrom(x.ToBytes())) }, ObservedAddr = ByteString.CopyFrom(context.State.RemoteAddress!.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto).ToBytes()), Protocols = { _protocolStackSettings.Protocols!.Select(r => r.Key.Protocol).OfType().Select(p => p.Id) }, - SignedPeerRecord = CreateSignedEnvelope(context.Peer.Identity, context.Peer.ListenAddresses.ToArray(), 1), + SignedPeerRecord = SigningHelper.CreateSignedEnvelope(context.Peer.Identity, context.Peer.ListenAddresses.ToArray(), 1), }; ByteString[] endpoints = context.Peer.ListenAddresses.Where(a => !a.ToEndPoint().Address.IsPrivate()).Select(a => a.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto)).Select(a => ByteString.CopyFrom(a.ToBytes())).ToArray(); @@ -99,63 +96,4 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) await channel.WriteSizeAndDataAsync(ar); _logger?.LogDebug("Sent peer info {identify}", identify); } - - private static bool VerifyPeerRecord(ByteString signedPeerRecordBytes, PublicKey remotePublicKey) - { - Identity identity = new(remotePublicKey); - SignedEnvelope envelope = SignedEnvelope.Parser.ParseFrom(signedPeerRecordBytes); - - if (envelope.PayloadType?.Take(2).SequenceEqual(Libp2pPeerRecordAsArray) is not true) - { - return false; - } - - PeerRecord pr = PeerRecord.Parser.ParseFrom(envelope.Payload); - - if (identity.PeerId != new PeerId(pr.PeerId.ToByteArray())) - { - return false; - } - - SignedEnvelope envelopeWithoutSignature = envelope.Clone(); - envelopeWithoutSignature.ClearSignature(); - - return identity.VerifySignature(envelopeWithoutSignature.ToByteArray(), envelope.Signature.ToByteArray()); - } - - private static ByteString CreateSignedEnvelope(Identity identity, Multiaddress[] addresses, ulong seq) - { - PeerRecord paylaod = new() - { - PeerId = ByteString.CopyFrom(identity.PeerId.Bytes), - Seq = seq - }; - - foreach (Multiaddress address in addresses) - { - paylaod.Addresses.Add(new AddressInfo - { - Multiaddr = ByteString.CopyFrom(address.ToBytes()) - }); - } - - SignedEnvelope envelope = new() - { - PayloadType = ByteString.CopyFrom(Libp2pPeerRecordAsArray), - Payload = paylaod.ToByteString(), - PublicKey = identity.PublicKey.ToByteString(), - }; - - envelope.Signature = ByteString.CopyFrom(identity.Sign(envelope.ToByteArray())); - return envelope.ToByteString(); - } - - private static Multiaddress ToEndpoint(Multiaddress addr) => new() - { - Protocols = - { - addr.Has() ? addr.Get() : addr.Get(), - addr.Has() ? addr.Get() : addr.Get() - } - }; } diff --git a/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj b/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj index 5e2c68ac..4f264a38 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj +++ b/src/libp2p/Libp2p.Protocols.Identify/Libp2p.Protocols.Identify.csproj @@ -13,13 +13,6 @@ - - - - - - Never - diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index ffb67b33..d426d8e6 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -7,11 +7,11 @@ using Nethermind.Libp2p.Core; using Noise; using System.Text; -using Org.BouncyCastle.Math.EC.Rfc8032; using Microsoft.Extensions.Logging; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Protocols.Noise.Dto; using PublicKey = Nethermind.Libp2p.Core.Dto.PublicKey; +using Nethermind.Libp2p.Core.Exceptions; namespace Nethermind.Libp2p.Protocols; @@ -40,6 +40,11 @@ public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILog public async Task DialAsync(IChannel downChannel, IConnectionContext context) { + if (context.State.RemoteAddress is null) + { + throw new Libp2pException(); + } + KeyPair? clientStatic = KeyPair.Generate(); using HandshakeState? handshakeState = _protocol.Create(true, s: clientStatic.PrivateKey); byte[] buffer = new byte[Protocol.MaxMessageLength]; @@ -82,8 +87,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) } byte[] msg = [.. Encoding.UTF8.GetBytes(PayloadSigPrefix), .. ByteString.CopyFrom(clientStatic.PublicKey)]; - byte[] sig = new byte[64]; - Ed25519.Sign([.. context.Peer.Identity.PrivateKey!.Data], 0, msg, 0, msg.Length, sig, 0); + byte[] sig = context.Peer.Identity.Sign(msg); NoiseHandshakePayload payload = new() { IdentityKey = context.Peer.Identity.PublicKey.ToByteString(), @@ -117,6 +121,11 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { + if (context.State.RemoteAddress is null) + { + throw new Libp2pException(); + } + KeyPair? serverStatic = KeyPair.Generate(); using HandshakeState? handshakeState = _protocol.Create(false, @@ -131,8 +140,8 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) byte[] msg = Encoding.UTF8.GetBytes(PayloadSigPrefix) .Concat(ByteString.CopyFrom(serverStatic.PublicKey)) .ToArray(); - byte[] sig = new byte[64]; - Ed25519.Sign(context.Peer.Identity.PrivateKey!.Data.ToArray(), 0, msg, 0, msg.Length, sig, 0); + byte[] sig = context.Peer.Identity.Sign(msg); + NoiseHandshakePayload payload = new() { IdentityKey = context.Peer.Identity.PublicKey.ToByteString(), @@ -171,9 +180,9 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) }; } - if (!(context.State.RemoteAddress?.Has() ?? false)) + if (context.State.RemotePeerId is null) { - context.State.RemoteAddress!.Add(new P2P(remotePeerId.ToString())); + context.State.RemoteAddress.Add(new P2P(remotePeerId.ToString())); } _logger?.LogDebug("Established connection to {peer}", context.State.RemoteAddress); diff --git a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs index b934778a..c677e688 100644 --- a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs @@ -4,6 +4,7 @@ using System.Buffers; using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Protocols.Ping; namespace Nethermind.Libp2p.Protocols; @@ -26,6 +27,11 @@ public PingProtocol(ILoggerFactory? loggerFactory = null) public async Task DialAsync(IChannel channel, ISessionContext context) { + if (context.State.RemoteAddress is null) + { + throw new Libp2pException(); + } + byte[] ping = new byte[PayloadLength]; _random.NextBytes(ping.AsSpan(0, PayloadLength)); ReadOnlySequence bytes = new(ping); @@ -50,6 +56,11 @@ public async Task DialAsync(IChannel channel, ISessionContext context) public async Task ListenAsync(IChannel channel, ISessionContext context) { + if (context.State.RemoteAddress is null) + { + throw new Libp2pException(); + } + _logger?.PingListenStarted(context.State.RemoteAddress); while (true) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Libp2p.Protocols.Pubsub.E2eTests.csproj similarity index 100% rename from src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Libp2p.Protocols.Pubsub.Profiler.csproj rename to src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Libp2p.Protocols.Pubsub.E2eTests.csproj diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs new file mode 100644 index 00000000..41dbbb28 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Libp2p.Protocols.Pubsub.E2eTests; +using Nethermind.Libp2p.Protocols.Pubsub; + + +int totalCount = 9; +PubsubTestSetup test = new(); + +await test.StartPeersAsync(totalCount); +test.StartPubsub(); +test.Subscribe("test"); + +foreach ((int index, PubsubRouter router) in test.Routers.Skip(1)) +{ + test.PeerStores[index].Discover(test.Peers[0].ListenAddresses.ToArray()); +} + +await test.WaitForFullMeshAsync("test"); + +test.PrintState(true); + diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs new file mode 100644 index 00000000..b7b6db0d --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs @@ -0,0 +1,141 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Core.TestsBase; +using Nethermind.Libp2p.Core.TestsBase.E2e; +using Nethermind.Libp2p.Protocols.Pubsub; +using Nethermind.Libp2p.Stack; +using System.Text; + +namespace Libp2p.Protocols.Pubsub.E2eTests; + +public class PubsubTestSetup +{ + protected static TestContextLoggerFactory loggerFactory = new(); + private int Counter = 0; + + public PubsubSettings DefaultSettings { get; set; } = new PubsubSettings { LowestDegree = 2, Degree = 3, LazyDegree = 3, HighestDegree = 4, HeartbeatInterval = 200 }; + protected ILogger testLogger { get; set; } = loggerFactory.CreateLogger("test-setup"); + + public ChannelBus CommonBus { get; } = new(loggerFactory); + public Dictionary Peers { get; } = new(); + public Dictionary PeerStores { get; } = new(); + public Dictionary Routers { get; } = new(); + public Dictionary ServiceProviders { get; } = new(); + + public async Task StartPeersAsync(int count, PubsubSettings? customPubsubSettings = null) + { + for (int i = Counter; i < Counter + count; i++) + { + // But we create a seprate setup for every peer + ServiceProvider sp = ServiceProviders[i] = new ServiceCollection() + .AddSingleton(sp => new TestBuilder(sp) + .AddAppLayerProtocol() + .AddAppLayerProtocol() + .AddAppLayerProtocol() + .AddAppLayerProtocol()) + .AddSingleton(sp => new TestContextLoggerFactory()) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(CommonBus) + .AddSingleton(sp => customPubsubSettings ?? DefaultSettings) + .AddSingleton(sp => sp.GetService()!.Build()) + .BuildServiceProvider(); + + PeerStores[i] = ServiceProviders[i].GetService()!; + Peers[i] = sp.GetService()!.Create(TestPeers.Identity(i)); + Routers[i] = sp.GetService()!; + + await Peers[i].StartListenAsync([TestPeers.Multiaddr(i)]); + } + } + + public void StartPubsub() + { + foreach ((int index, PubsubRouter router) in Routers) + { + _ = router.RunAsync(Peers[index]); + } + } + + /// + /// Manual heartbeat in case the period is set to infinite + /// + /// + public async Task Heartbeat() + { + foreach (PubsubRouter router in Routers.Values) + { + await router.Heartbeat(); + } + } + + private int stateCounter = 1; + + public void PrintState(bool outputToConsole = false) + { + StringBuilder reportBuilder = new(); + reportBuilder.AppendLine($"Test state#{stateCounter++}"); + + foreach ((int index, PubsubRouter router) in Routers) + { + reportBuilder.AppendLine(router.ToString()); + reportBuilder.AppendLine(Peers[index].ToString()); + reportBuilder.AppendLine(); + } + + string report = reportBuilder.ToString(); + + if (outputToConsole) + { + Console.WriteLine(report); + } + else + { + testLogger.LogInformation(report.ToString()); + } + } + + public void Subscribe(string topic) + { + foreach (PubsubRouter router in Routers.Values) + { + router.GetTopic(topic); + } + } + + public async Task WaitForFullMeshAsync(string topic, int timeoutMs = 15_000) + { + int requiredCount = int.Min(Routers.Count - 1, DefaultSettings.LowestDegree); + + CancellationTokenSource cts = new(); + Task delayTask = Task.Delay(timeoutMs).ContinueWith((t) => cts.Cancel()); + + while (true) + { + if (cts.IsCancellationRequested) + { + PrintState(); + throw new Exception("Timeout waiting for the network"); + } + PrintState(); + + cts.Token.ThrowIfCancellationRequested(); + await Task.Delay(100); + + bool stillWaiting = false; + + foreach (IRoutingStateContainer router in Routers.Values) + { + if (router.Mesh[topic].Count < requiredCount) + { + stillWaiting = true; + } + } + + if (!stillWaiting) break; + } + } +} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs deleted file mode 100644 index 8ecaf3b6..00000000 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Profiler/Program.cs +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Core.TestsBase; -using Nethermind.Libp2p.Core.TestsBase.E2e; -using Nethermind.Libp2p.Protocols; -using Nethermind.Libp2p.Protocols.Pubsub; -using Nethermind.Libp2p.Stack; -using System.Text; - -int totalCount = 2; -IPeer[] peers = new IPeer[totalCount]; -PeerStore[] peerStores = new PeerStore[totalCount]; -PubsubRouter[] routers = new PubsubRouter[totalCount]; - - -for (int i = 0; i < totalCount; i++) -{ - // But we create a seprate setup for every peer - ServiceProvider sp = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) - .AddSingleton(sp => new TestContextLoggerFactory()) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(sp => new Settings { LowestDegree = 1, Degree = 2, LazyDegree = 2, HighestDegree = 3 }) - .AddSingleton(sp => sp.GetService()!.Build()) - .BuildServiceProvider(); - - IPeerFactory peerFactory = sp.GetService()!; - IPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); - PubsubRouter router = routers[i] = sp.GetService()!; - PubsubPeerDiscoveryProtocol disc = new(router, peerStores[i] = sp.GetService()!, new PubsubPeerDiscoverySettings() { Interval = 300 }, peer); - - await peer.StartListenAsync([TestPeers.Multiaddr(i)]); - _ = router.RunAsync(peer, sp.GetService()); - //_ = disc.DiscoverAsync(peer.Address); -} - -Console.WriteLine($"Emulate peer exchange with one bootstrap peer"); - -for (int i = 0; i < routers.Length; i++) -{ - routers[i].GetTopic("test"); -} - -Console.WriteLine($"Center: {string.Join(",", peers[0].ListenAddresses)}"); - -for (int i = 1; i < peers.Length; i++) -{ - peerStores[i].Discover(peers[0].ListenAddresses.ToArray()); -} - -await Task.Delay(10000); - -Console.WriteLine("Routers"); - -for (int i = 0; i < routers.Length; i++) -{ - Console.WriteLine(routers[i].ToString()); -} - -Console.WriteLine("Stores"); - -for (int i = 0; i < peerStores.Length; i++) -{ - Console.WriteLine(peerStores[i].ToString()); -} - - -await Task.Delay(5000); - -ITopic testTopic = routers[1].GetTopic("test"); -ITopic testTopicEnd = routers[totalCount - 1].GetTopic("test"); -testTopicEnd.OnMessage += (s) => Console.WriteLine(Encoding.UTF8.GetString(s)); - -testTopic.Publish(Encoding.UTF8.GetBytes("test")); - -for (int i = 0; i < 8; i++) -{ - Console.WriteLine(i * 100); - await Task.Delay(1000000); -} - -Console.WriteLine("Routers"); - -for (int i = 0; i < routers.Length; i++) -{ - Console.WriteLine(routers[i].ToString()); -} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/Settings.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubSubSettings.cs similarity index 92% rename from src/libp2p/Libp2p.Protocols.Pubsub/Settings.cs rename to src/libp2p/Libp2p.Protocols.Pubsub/PubSubSettings.cs index 4ba2e3ae..3fc31cae 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/Settings.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubSubSettings.cs @@ -6,9 +6,9 @@ namespace Nethermind.Libp2p.Protocols.Pubsub; -public class Settings +public class PubsubSettings { - public static Settings Default { get; } = new(); + public static PubsubSettings Default { get; } = new(); public int ReconnectionAttempts { get; set; } = 10; public int ReconnectionPeriod { get; set; } = 15_000; @@ -17,6 +17,9 @@ public class Settings public int LowestDegree { get; set; } = 4; //Lower bound for outbound degree 4 public int HighestDegree { get; set; } = 12;//Upper bound for outbound degree 12 public int LazyDegree { get; set; } = 6;//(Optional) the outbound degree for gossip emission D + + public int MaxConnections { get; set; } + public int HeartbeatInterval { get; set; } = 1_000;//Time between heartbeats 1 second public int FanoutTtl { get; set; } = 60 * 1000;//Time-to-live for each topic's fanout state 60 seconds public int mcache_len { get; set; } = 5;//Number of history windows in message cache 5 diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 01964f4e..0c2f6a55 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -2,8 +2,8 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.Logging; -using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Protocols.Pubsub.Dto; namespace Nethermind.Libp2p.Protocols.Pubsub; @@ -27,7 +27,13 @@ public PubsubProtocol(string protocolId, PubsubRouter router, ILoggerFactory? lo public async Task DialAsync(IChannel channel, ISessionContext context) { - string peerId = context.State.RemoteAddress.Get().ToString()!; + PeerId? remotePeerId = context.State.RemotePeerId ?? throw new Libp2pException(); + + if (context.State.RemoteAddress is null) + { + throw new Libp2pException(); + } + _logger?.LogDebug($"Dialed({context.Id}) {context.State.RemoteAddress}"); TaskCompletionSource dialTcs = new(); @@ -38,10 +44,10 @@ public async Task DialAsync(IChannel channel, ISessionContext context) { if (!t.IsCompletedSuccessfully) { - _logger?.LogWarning($"Sending RPC failed message to {peerId}: {rpc}"); + _logger?.LogWarning($"Sending RPC failed message to {remotePeerId}: {rpc}"); } }); - _logger?.LogTrace($"Sent message to {peerId}: {rpc}"); + _logger?.LogTrace($"Sent message to {remotePeerId}: {rpc}"); }); await channel; @@ -52,8 +58,13 @@ public async Task DialAsync(IChannel channel, ISessionContext context) public async Task ListenAsync(IChannel channel, ISessionContext context) { + PeerId? remotePeerId = context.State.RemotePeerId ?? throw new Libp2pException(); + + if (context.State.RemoteAddress is null) + { + throw new Libp2pException(); + } - string peerId = context.State.RemoteAddress.Get().ToString()!; _logger?.LogDebug($"Listen({context.Id}) to {context.State.RemoteAddress}"); TaskCompletionSource listTcs = new(); @@ -70,23 +81,21 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) Rpc? rpc = await channel.ReadAnyPrefixedProtobufAsync(Rpc.Parser, token); if (rpc is null) { - _logger?.LogDebug($"Received a broken message or EOF from {peerId}"); + _logger?.LogDebug($"Received a broken message or EOF from {remotePeerId}"); break; } else { - _logger?.LogTrace($"Received message from {peerId}: {rpc}"); - _ = router.OnRpc(peerId, rpc); + _logger?.LogTrace($"Received message from {remotePeerId}: {rpc}"); + _ = router.OnRpc(remotePeerId, rpc); } } + listTcs.SetResult(); _logger?.LogDebug($"Finished({context.Id}) list {context.State.RemoteAddress}"); } - public override string ToString() - { - return Id; - } + public override string ToString() => Id; } public class FloodsubProtocol(PubsubRouter router, ILoggerFactory? loggerFactory = null) : PubsubProtocol(PubsubRouter.FloodsubProtocolVersion, router, loggerFactory); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs index 8ef082b8..7bbbf8c5 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs @@ -144,7 +144,7 @@ public void Publish(string topicId, byte[] message) HashSet? topicPeers = gPeers.GetValueOrDefault(topicId); if (topicPeers is { Count: > 0 }) { - foreach (PeerId peer in topicPeers.Take(settings.Degree)) + foreach (PeerId peer in topicPeers.Take(_settings.Degree)) { topicFanout.Add(peer); } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index f77d10e6..b48ea9dd 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -6,14 +6,12 @@ using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Core.Dto; -using Nethermind.Libp2p.Protocols.Identify.Dto; using Nethermind.Libp2p.Protocols.Pubsub.Dto; using System.Collections.Concurrent; namespace Nethermind.Libp2p.Protocols.Pubsub; -internal interface IRoutingStateContainer +public interface IRoutingStateContainer { ConcurrentDictionary> FloodsubPeers { get; } ConcurrentDictionary> GossipsubPeers { get; } @@ -24,7 +22,7 @@ internal interface IRoutingStateContainer Task Heartbeat(); } -public partial class PubsubRouter(PeerStore store, ILoggerFactory? loggerFactory = default) : IRoutingStateContainer +public partial class PubsubRouter : IRoutingStateContainer { static int routerCounter = 0; readonly int routerId = Interlocked.Increment(ref routerCounter); @@ -133,13 +131,13 @@ public Action? SendRpc public event Action? OnMessage; public Func? VerifyMessage = null; - private Settings settings; - private TtlCache messageCache; - private TtlCache limboMessageCache; - private TtlCache<(PeerId, MessageId)> dontWantMessages; + private readonly PubsubSettings _settings; + private readonly TtlCache _messageCache; + private readonly TtlCache _limboMessageCache; + private readonly TtlCache<(PeerId, MessageId)> _dontWantMessages; private IPeer? localPeer; - private readonly ILogger? logger = loggerFactory?.CreateLogger(); + private readonly ILogger? logger; // all floodsub peers in topics private readonly ConcurrentDictionary> fPeers = new(); @@ -158,6 +156,7 @@ public Action? SendRpc private readonly ConcurrentDictionary peerState = new(); private readonly ConcurrentBag reconnections = new(); + private readonly PeerStore _peerStore; private ulong seqNo = 1; private record Reconnection(Multiaddress[] Addresses, int Attempts); @@ -169,7 +168,18 @@ static PubsubRouter() Canceled = cts.Token; } - public async Task RunAsync(IPeer localPeer, Settings? settings = null, CancellationToken token = default) + public PubsubRouter(PeerStore store, PubsubSettings? settings = null, ILoggerFactory? loggerFactory = default) + { + logger = loggerFactory?.CreateLogger("pubsub-router"); + + _peerStore = store; + _settings = settings ?? PubsubSettings.Default; + _messageCache = new(_settings.MessageCacheTtl); + _limboMessageCache = new(_settings.MessageCacheTtl); + _dontWantMessages = new(_settings.MessageCacheTtl); + } + + public async Task RunAsync(IPeer localPeer, CancellationToken token = default) { logger?.LogDebug($"Running pubsub for {string.Join(",", localPeer.ListenAddresses)}"); @@ -178,14 +188,11 @@ public async Task RunAsync(IPeer localPeer, Settings? settings = null, Cancellat throw new InvalidOperationException("Router has been already started"); } this.localPeer = localPeer; - this.settings = settings ?? Settings.Default; - messageCache = new(this.settings.MessageCacheTtl); - limboMessageCache = new(this.settings.MessageCacheTtl); - dontWantMessages = new(this.settings.MessageCacheTtl); + logger?.LogInformation("Started"); - store.OnNewPeer += (addrs) => + _peerStore.OnNewPeer += (addrs) => { if (addrs.Any(a => a.GetPeerId()! == localPeer.Identity.PeerId)) { @@ -199,7 +206,7 @@ public async Task RunAsync(IPeer localPeer, Settings? settings = null, Cancellat if (!peerState.ContainsKey(session.RemoteAddress.Get().ToString())) { - await session.DialAsync(token); + await session.DialAsync(token); if (peerState.TryGetValue(session.RemoteAddress.GetPeerId()!, out PubsubPeer? state) && state.InititatedBy == ConnectionInitiation.Remote) { _ = session.DisconnectAsync(); @@ -208,7 +215,7 @@ public async Task RunAsync(IPeer localPeer, Settings? settings = null, Cancellat } catch { - reconnections.Add(new Reconnection(addrs, this.settings.ReconnectionAttempts)); + reconnections.Add(new Reconnection(addrs, this._settings.ReconnectionAttempts)); } }); }; @@ -217,7 +224,7 @@ public async Task RunAsync(IPeer localPeer, Settings? settings = null, Cancellat { while (!token.IsCancellationRequested) { - await Task.Delay(this.settings.HeartbeatInterval); + await Task.Delay(this._settings.HeartbeatInterval); await Heartbeat(); } }, token); @@ -227,14 +234,14 @@ public async Task RunAsync(IPeer localPeer, Settings? settings = null, Cancellat { while (!token.IsCancellationRequested) { - await Task.Delay(this.settings.ReconnectionPeriod); + await Task.Delay(this._settings.ReconnectionPeriod); await Reconnect(token); } }, token); await Task.Delay(Timeout.Infinite, token); - messageCache.Dispose(); - limboMessageCache.Dispose(); + _messageCache.Dispose(); + _limboMessageCache.Dispose(); } @@ -264,13 +271,11 @@ public Task Heartbeat() { foreach (KeyValuePair> mesh in mesh) { - logger?.LogDebug($"MESH({localPeer!.Identity.PeerId}) {mesh.Key}: {mesh.Value.Count} ({string.Join(",", mesh.Value)})"); - if (mesh.Value.Count < settings.LowestDegree) + if (mesh.Value.Count < _settings.LowestDegree) { PeerId[] peersToGraft = gPeers[mesh.Key] - .Where(p => !mesh.Value.Contains(p) - && (peerState.GetValueOrDefault(p)?.Backoff.TryGetValue(mesh.Key, out DateTime backoff) != true || - backoff < DateTime.Now)).Take(settings.Degree - mesh.Value.Count).ToArray(); + .Where(p => !mesh.Value.Contains(p) && (peerState.GetValueOrDefault(p)?.Backoff.TryGetValue(mesh.Key, out DateTime backoff) != true || backoff < DateTime.Now)) + .Take(_settings.Degree - mesh.Value.Count).ToArray(); foreach (PeerId peerId in peersToGraft) { mesh.Value.Add(peerId); @@ -279,18 +284,21 @@ public Task Heartbeat() .Add(new ControlGraft { TopicID = mesh.Key }); } } - else if (mesh.Value.Count > settings.HighestDegree) + else if (mesh.Value.Count > _settings.HighestDegree) { - PeerId[] peerstoPrune = mesh.Value.Take(mesh.Value.Count - settings.HighestDegree).ToArray(); + PeerId[] peerstoPrune = mesh.Value.Take(mesh.Value.Count - _settings.HighestDegree).ToArray(); foreach (PeerId? peerId in peerstoPrune) { mesh.Value.Remove(peerId); ControlPrune prune = new() { TopicID = mesh.Key, Backoff = 60 }; - prune.Peers.AddRange(mesh.Value.ToArray().Select(pid => (PeerId: pid, Record: store.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo - { - PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), - SignedPeerRecord = pid.Record, - })); + prune.Peers.AddRange(mesh.Value.ToArray() + .Select(pid => (PeerId: pid, Record: _peerStore.GetPeerInfo(pid)?.SignedPeerRecord)) + .Where(pid => pid.Record is not null) + .Select(pid => new PeerInfo + { + PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), + SignedPeerRecord = pid.Record, + })); peerMessages.GetOrAdd(peerId, _ => new Rpc()) .Ensure(r => r.Control.Prune) .Add(prune); @@ -300,14 +308,14 @@ public Task Heartbeat() foreach (string? fanoutTopic in fanout.Keys.ToArray()) { - if (fanoutLastPublished.GetOrAdd(fanoutTopic, _ => DateTime.Now).AddMilliseconds(settings.FanoutTtl) < DateTime.Now) + if (fanoutLastPublished.GetOrAdd(fanoutTopic, _ => DateTime.Now).AddMilliseconds(_settings.FanoutTtl) < DateTime.Now) { fanout.Remove(fanoutTopic, out _); fanoutLastPublished.Remove(fanoutTopic, out _); } else { - int peerCountToAdd = settings.Degree - fanout[fanoutTopic].Count; + int peerCountToAdd = _settings.Degree - fanout[fanoutTopic].Count; if (peerCountToAdd > 0) { foreach (PeerId? peerId in gPeers[fanoutTopic].Where(p => !fanout[fanoutTopic].Contains(p)).Take(peerCountToAdd)) @@ -318,7 +326,7 @@ public Task Heartbeat() } } - IEnumerable> msgs = messageCache.ToList().GroupBy(m => m.Topic); + IEnumerable> msgs = _messageCache.ToList().GroupBy(m => m.Topic); foreach (string? topic in gPeers.Keys.Concat(fanout.Keys).Distinct().ToArray()) { @@ -326,9 +334,9 @@ public Task Heartbeat() if (msgsInTopic is not null) { ControlIHave ihave = new() { TopicID = topic }; - ihave.MessageIDs.AddRange(msgsInTopic.Select(m => ByteString.CopyFrom(settings.GetMessageId(m).Bytes))); + ihave.MessageIDs.AddRange(msgsInTopic.Select(m => ByteString.CopyFrom(_settings.GetMessageId(m).Bytes))); - foreach (PeerId? peer in gPeers[topic].Where(p => !mesh[topic].Contains(p) && !fanout[topic].Contains(p)).Take(settings.LazyDegree)) + foreach (PeerId? peer in gPeers[topic].Where(p => !mesh[topic].Contains(p) && !fanout[topic].Contains(p)).Take(_settings.LazyDegree)) { peerMessages.GetOrAdd(peer, _ => new Rpc()) .Ensure(r => r.Control.Ihave).Add(ihave); @@ -366,63 +374,15 @@ internal CancellationToken OutboundConnection(Multiaddress addr, string protocol } else { + logger?.LogDebug("Outbound, rpc set for {peerId}, cancelling", peerId); return Canceled; } } - } - - dialTask.ContinueWith(t => - { - peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); - peerState.TryRemove(peerId, out _); - foreach (KeyValuePair> topicPeers in fPeers) - { - topicPeers.Value.Remove(peerId); - } - foreach (KeyValuePair> topicPeers in gPeers) - { - topicPeers.Value.Remove(peerId); - } - foreach (KeyValuePair> topicPeers in fanout) - { - topicPeers.Value.Remove(peerId); - } - foreach (KeyValuePair> topicPeers in mesh) - { - topicPeers.Value.Remove(peerId); - } - reconnections.Add(new Reconnection([addr], settings.ReconnectionAttempts)); - }); - - string[] topics = topicState.Keys.ToArray(); - - if (topics.Any()) - { - logger?.LogDebug("Topics sent to {peerId}: {topics}", peerId, string.Join(",", topics)); - - Rpc helloMessage = new Rpc().WithTopics(topics, []); - peer.Send(helloMessage); - } - - logger?.LogDebug("Outbound {peerId}", peerId); - return peer.TokenSource.Token; - } - internal CancellationToken InboundConnection(Multiaddress addr, string protocolId, Task listTask, Task dialTask, Func subDial) - { - PeerId? peerId = addr.GetPeerId(); - if (peerId is null || peerId == localPeer!.Identity.PeerId) - { - return Canceled; - } + logger?.LogDebug("Outbound, let's dial {peerId} via remotely initiated connection", peerId); - PubsubPeer? newPeer = null; - PubsubPeer existingPeer = peerState.GetOrAdd(peerId, (id) => newPeer = new PubsubPeer(peerId, protocolId, logger) { Address = addr, InititatedBy = ConnectionInitiation.Remote }); - if (newPeer is not null) - { - logger?.LogDebug("Inbound, let's dial {peerId} via remotely initiated connection", peerId); - listTask.ContinueWith(t => + dialTask.ContinueWith(t => { peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); peerState.TryRemove(peerId, out _); @@ -442,16 +402,71 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI { topicPeers.Value.Remove(peerId); } - reconnections.Add(new Reconnection([addr], settings.ReconnectionAttempts)); + reconnections.Add(new Reconnection([addr], _settings.ReconnectionAttempts)); }); - subDial(); + string[] topics = topicState.Keys.ToArray(); - return newPeer.TokenSource.Token; + if (topics.Any()) + { + logger?.LogDebug("Topics sent to {peerId}: {topics}", peerId, string.Join(",", topics)); + + Rpc helloMessage = new Rpc().WithTopics(topics, []); + peer.Send(helloMessage); + } + + logger?.LogDebug("Outbound {peerId}", peerId); + return peer.TokenSource.Token; } - else + } + + internal CancellationToken InboundConnection(Multiaddress addr, string protocolId, Task listTask, Task dialTask, Func subDial) + { + PeerId? peerId = addr.GetPeerId(); + + if (peerId is null || peerId == localPeer!.Identity.PeerId) { - return existingPeer.TokenSource.Token; + return Canceled; + } + + PubsubPeer? newPeer = null; + PubsubPeer existingPeer = peerState.GetOrAdd(peerId, (id) => newPeer = new PubsubPeer(peerId, protocolId, logger) { Address = addr, InititatedBy = ConnectionInitiation.Remote }); + lock (existingPeer) + { + + if (newPeer is not null) + { + logger?.LogDebug("Inbound, let's dial {peerId} via remotely initiated connection", peerId); + listTask.ContinueWith(t => + { + peerState.GetValueOrDefault(peerId)?.TokenSource.Cancel(); + peerState.TryRemove(peerId, out _); + foreach (KeyValuePair> topicPeers in fPeers) + { + topicPeers.Value.Remove(peerId); + } + foreach (KeyValuePair> topicPeers in gPeers) + { + topicPeers.Value.Remove(peerId); + } + foreach (KeyValuePair> topicPeers in fanout) + { + topicPeers.Value.Remove(peerId); + } + foreach (KeyValuePair> topicPeers in mesh) + { + topicPeers.Value.Remove(peerId); + } + reconnections.Add(new Reconnection([addr], _settings.ReconnectionAttempts)); + }); + + subDial(); + return newPeer.TokenSource.Token; + } + else + { + return existingPeer.TokenSource.Token; + } } } @@ -462,60 +477,15 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) ConcurrentDictionary peerMessages = new(); lock (this) { - if (rpc.Subscriptions.Any()) - { - foreach (Rpc.Types.SubOpts? sub in rpc.Subscriptions) - { - PubsubPeer? state = peerState.GetValueOrDefault(peerId); - if (state is null) - { - return; - } - if (sub.Subscribe) - { - if (state.IsGossipSub) - { - gPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); - } - else if (state.IsFloodSub) - { - fPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); - } - } - else - { - if (state.IsGossipSub) - { - gPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); - if (mesh.ContainsKey(sub.Topicid)) - { - mesh[sub.Topicid].Remove(peerId); - } - if (fanout.ContainsKey(sub.Topicid)) - { - fanout[sub.Topicid].Remove(peerId); - } - } - else if (state.IsFloodSub) - { - fPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); - } - } - } - } - if (rpc.Publish.Any()) { - if (rpc.Publish.Any()) - { - logger?.LogDebug($"Messages received: {rpc.Publish.Select(settings.GetMessageId).Count(messageId => limboMessageCache.Contains(messageId) || messageCache!.Contains(messageId))}/{rpc.Publish.Count}: {rpc.Publish.Count}"); - } + logger?.LogDebug($"Messages received: {rpc.Publish.Select(_settings.GetMessageId).Count(messageId => _limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId))}/{rpc.Publish.Count}: {rpc.Publish.Count}"); foreach (Message? message in rpc.Publish) { - MessageId messageId = settings.GetMessageId(message); + MessageId messageId = _settings.GetMessageId(message); - if (limboMessageCache.Contains(messageId) || messageCache!.Contains(messageId)) + if (_limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId)) { continue; } @@ -524,19 +494,19 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) { case MessageValidity.Rejected: case MessageValidity.Ignored: - limboMessageCache.Add(messageId, message); + _limboMessageCache.Add(messageId, message); continue; case MessageValidity.Trottled: continue; } - if (!message.VerifySignature(settings.DefaultSignaturePolicy)) + if (!message.VerifySignature(_settings.DefaultSignaturePolicy)) { - limboMessageCache!.Add(messageId, message); + _limboMessageCache!.Add(messageId, message); continue; } - messageCache.Add(messageId, message); + _messageCache.Add(messageId, message); PeerId author = new(message.From.ToArray()); OnMessage?.Invoke(message.Topic, message.Data.ToByteArray()); @@ -552,9 +522,9 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); } } - if (fPeers.TryGetValue(message.Topic, out topicPeers)) + if (mesh.TryGetValue(message.Topic, out topicPeers)) { - foreach (PeerId peer in mesh[message.Topic]) + foreach (PeerId peer in topicPeers) { if (peer == author || peer == peerId) { @@ -566,6 +536,48 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) } } + if (rpc.Subscriptions.Any()) + { + foreach (Rpc.Types.SubOpts? sub in rpc.Subscriptions) + { + PubsubPeer? state = peerState.GetValueOrDefault(peerId); + if (state is null) + { + return; + } + if (sub.Subscribe) + { + if (state.IsGossipSub) + { + gPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); + } + else if (state.IsFloodSub) + { + fPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); + } + } + else + { + if (state.IsGossipSub) + { + gPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); + if (mesh.ContainsKey(sub.Topicid)) + { + mesh[sub.Topicid].Remove(peerId); + } + if (fanout.ContainsKey(sub.Topicid)) + { + fanout[sub.Topicid].Remove(peerId); + } + } + else if (state.IsFloodSub) + { + fPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); + } + } + } + } + if (rpc.Control is not null) { if (rpc.Control.Graft.Any()) @@ -582,14 +594,14 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) { HashSet topicMesh = mesh[graft.TopicID]; - if (topicMesh.Count >= settings.HighestDegree) + if (topicMesh.Count >= _settings.HighestDegree) { ControlPrune prune = new() { TopicID = graft.TopicID }; if (peerState.TryGetValue(peerId, out PubsubPeer? state) && state.IsGossipSub && state.Protocol >= PubsubPeer.PubsubProtocol.GossipsubV11) { state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); - prune.Peers.AddRange(topicMesh.ToArray().Select(pid => (PeerId: pid, Record: store.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo + prune.Peers.AddRange(topicMesh.ToArray().Select(pid => (PeerId: pid, Record: _peerStore.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo { PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), SignedPeerRecord = pid.Record, @@ -602,10 +614,14 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) } else { - topicMesh.Add(peerId); - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Graft) - .Add(new ControlGraft { TopicID = graft.TopicID }); + if (!topicMesh.Contains(peerId)) + { + topicMesh.Add(peerId); + gPeers[graft.TopicID].Add(peerId); + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Graft) + .Add(new ControlGraft { TopicID = graft.TopicID }); + } } } } @@ -628,9 +644,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) foreach (PeerInfo? peer in prune.Peers) { - // TODO verify payload type, signature, etc - // TODO check if it's working - reconnections.Add(new Reconnection(PeerRecord.Parser.ParseFrom(SignedEnvelope.Parser.ParseFrom(peer.SignedPeerRecord).Payload).Addresses.Select(ai => Multiaddress.Decode(ai.Multiaddr.ToByteArray())).ToArray(), 5)); + _peerStore.Discover(peer.SignedPeerRecord); } } } @@ -644,7 +658,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) .Where(iw => topicState.ContainsKey(iw.TopicID))) { messageIds.AddRange(ihave.MessageIDs.Select(m => new MessageId(m.ToByteArray())) - .Where(mid => !messageCache.Contains(mid))); + .Where(mid => !_messageCache.Contains(mid))); } if (messageIds.Any()) @@ -666,7 +680,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) List messages = new(); foreach (MessageId? mId in messageIds) { - Message message = messageCache.Get(mId); + Message message = _messageCache.Get(mId); if (message != default) { messages.Add(message); @@ -681,9 +695,9 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) if (rpc.Control.Idontwant.Any()) { - foreach (MessageId messageId in rpc.Control.Iwant.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())).Take(settings.MaxIdontwantMessages)) + foreach (MessageId messageId in rpc.Control.Iwant.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())).Take(_settings.MaxIdontwantMessages)) { - dontWantMessages.Add((peerId, messageId)); + _dontWantMessages.Add((peerId, messageId)); } } } @@ -695,7 +709,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) } catch (Exception ex) { - logger?.LogError("Exception during rpc handling: {exception}", ex); + logger?.LogError(ex, "Exception while processing RPC"); } } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/RpcExtensions.cs b/src/libp2p/Libp2p.Protocols.Pubsub/RpcExtensions.cs index fbbd1003..c2a5a90d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/RpcExtensions.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/RpcExtensions.cs @@ -45,9 +45,9 @@ public static Rpc WithTopics(this Rpc rpc, IEnumerable addTopics, IEnume return rpc; } - public static bool VerifySignature(this Message message, Settings.SignaturePolicy signaturePolicy) + public static bool VerifySignature(this Message message, PubsubSettings.SignaturePolicy signaturePolicy) { - if (signaturePolicy is Settings.SignaturePolicy.StrictNoSign) + if (signaturePolicy is PubsubSettings.SignaturePolicy.StrictNoSign) { return message.Signature.IsEmpty; } diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs new file mode 100644 index 00000000..e1ee165c --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Libp2p.Protocols.Pubsub.E2eTests; +using Nethermind.Libp2p.Protocols.Pubsub; +using NUnit.Framework.Internal; + +namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; + +public class E2eTests +{ + [Test] + public async Task Test_NetworkEstablished() + { + int totalCount = 10; + PubsubTestSetup test = new(); + + await test.StartPeersAsync(totalCount); + test.StartPubsub(); + test.AddPubsubPeerDiscovery(); + test.Subscribe("test"); + + foreach ((int index, PubsubRouter router) in test.Routers.Skip(1)) + { + test.PeerStores[index].Discover(test.Peers[0].ListenAddresses.ToArray()); + } + + + await test.WaitForFullMeshAsync("test", 150_000); + + test.PrintState(); + } +} diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj index 2132529c..eac2a5e3 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj @@ -23,6 +23,7 @@ + diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs deleted file mode 100644 index 6cab9db2..00000000 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/MultistreamProtocolTests.cs +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Core.TestsBase.E2e; -using Nethermind.Libp2p.Protocols.Pubsub; -using NUnit.Framework.Internal; - -namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; - -[TestFixture, Ignore("No support of time mock yet")] -[Parallelizable(scope: ParallelScope.All)] -public class MultistreamProtocolTests -{ - [Test, CancelAfter(5000)] - public async Task Test_PeersConnect() - { - IPeerFactory peerFactory = new TestBuilder().Build(); - ChannelBus commonBus = new(); - - ServiceProvider sp1 = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) - .AddSingleton(sp => new TestContextLoggerFactory()) - .AddSingleton() - .AddSingleton() - .AddSingleton(commonBus) - .AddSingleton(sp => sp.GetService()!.Build()) - .BuildServiceProvider(); - - - ServiceProvider sp2 = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) - .AddSingleton(sp => new TestContextLoggerFactory()) - .AddSingleton() - .AddSingleton() - .AddSingleton(commonBus) - .AddSingleton(sp => sp.GetService()!.Build()) - .BuildServiceProvider(); - - - IPeer peerA = sp1.GetService()!.Create(TestPeers.Identity(1)); - await peerA.StartListenAsync([TestPeers.Multiaddr(1)]); - IPeer peerB = sp2.GetService()!.Create(TestPeers.Identity(2)); - await peerB.StartListenAsync([TestPeers.Multiaddr(2)]); - - ISession remotePeerB = await peerA.DialAsync(peerB.ListenAddresses.ToArray()); - await remotePeerB.DialAsync(); - } - - [Test] - public async Task Test_ConnectionEstablished_AfterHandshake() - { - int totalCount = 5; - TestContextLoggerFactory fac = new(); - // There is common communication point - ChannelBus commonBus = new(fac); - IPeer[] peers = new IPeer[totalCount]; - PeerStore[] peerStores = new PeerStore[totalCount]; - PubsubRouter[] routers = new PubsubRouter[totalCount]; - - for (int i = 0; i < totalCount; i++) - { - // But we create a seprate setup for every peer - ServiceProvider sp = new ServiceCollection() - .AddSingleton(sp => fac) - .AddSingleton() - .AddSingleton() - .AddSingleton(commonBus) - .AddSingleton(sp => sp.GetService()!.Build()) - .BuildServiceProvider(); - - IPeerFactory peerFactory = sp.GetService()!; - IPeer peer = peers[i] = peerFactory.Create(TestPeers.Identity(i)); - PubsubRouter router = routers[i] = sp.GetService()!; - PeerStore peerStore = sp.GetService()!; - PubsubPeerDiscoveryProtocol disc = new(router, peerStore, new PubsubPeerDiscoverySettings() { Interval = 300 }, peer); - await peer.StartListenAsync([TestPeers.Multiaddr(i)]); - _ = router.RunAsync(peer); - peerStores[i] = peerStore; - _ = disc.DiscoverAsync(peers[i].ListenAddresses); - } - - await Task.Delay(1000); - - for (int i = 0; i < peers.Length; i++) - { - peerStores[i].Discover(peers[(i + 1) % totalCount].ListenAddresses.ToArray()); - } - - await Task.Delay(30000); - - foreach (PubsubRouter router in routers) - { - Assert.That(((IRoutingStateContainer)router).ConnectedPeers.Count, Is.EqualTo(totalCount - 1)); - } - } - - [Test, CancelAfter(5000)] - public async Task Test_ConnectionEstablished_AfterHandshak3e() - { - int totalCount = 5; - - PubsubTestSetup setup = new(); - Dictionary discoveries = []; - - await setup.AddAsync(totalCount); - - // discover in circle - for (int i = 0; i < setup.Peers.Count; i++) - { - setup.PeerStores[i].Discover(setup.Peers[(i + 1) % setup.Peers.Count].ListenAddresses.ToArray()); - } - - for (int i = 0; i < setup.Peers.Count; i++) - { - discoveries[i] = new(setup.Routers[i], setup.PeerStores[i], new PubsubPeerDiscoverySettings() { Interval = int.MaxValue }, setup.Peers[i]); - _ = discoveries[i].DiscoverAsync(setup.Peers[i].ListenAddresses); - } - - await Task.Delay(100); - - await setup.Heartbeat(); - await setup.Heartbeat(); - await setup.Heartbeat(); - - for (int i = 0; i < setup.Peers.Count; i++) - { - discoveries[i].BroadcastPeerInfo(); - } - - foreach (PubsubRouter router in setup.Routers.Values) - { - Assert.That(((IRoutingStateContainer)router).ConnectedPeers.Count, Is.EqualTo(totalCount - 1)); - } - } -} diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs deleted file mode 100644 index 44df38ce..00000000 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubSubTestSetup.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Core.TestsBase.E2e; -using Nethermind.Libp2p.Protocols.Pubsub; - -namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; - -class PubsubTestSetup -{ - static TestContextLoggerFactory fac = new(); - - public ChannelBus CommonBus { get; } = new(fac); - public Dictionary Peers { get; } = new(); - public Dictionary PeerStores { get; } = new(); - public Dictionary Routers { get; } = new(); - - public async Task AddAsync(int count) - { - int initialCount = Peers.Count; - // There is common communication point - - for (int i = initialCount; i < initialCount + count; i++) - { - // But we create a seprate setup for every peer - Settings settings = new() - { - HeartbeatInterval = int.MaxValue, - }; - - ServiceProvider sp = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(sp).AddAppLayerProtocol()) - .AddSingleton((Func)(sp => fac)) - .AddSingleton() - .AddSingleton(settings) - .AddSingleton(CommonBus) - .AddSingleton() - .AddSingleton(sp => sp.GetService()!.Build()) - .BuildServiceProvider(); - - IPeerFactory peerFactory = sp.GetService()!; - IPeer peer = Peers[i] = peerFactory.Create(TestPeers.Identity(i)); - PubsubRouter router = Routers[i] = sp.GetService()!; - PeerStore peerStore = sp.GetService()!; - await peer.StartListenAsync([TestPeers.Multiaddr(i)]); - _ = router.RunAsync(peer); - PeerStores[i] = peerStore; - } - } - - internal async Task Heartbeat() - { - foreach (PubsubRouter router in Routers.Values) - { - await router.Heartbeat(); - } - } -} diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs new file mode 100644 index 00000000..1c1fe3e5 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs @@ -0,0 +1,16 @@ +using Libp2p.Protocols.Pubsub.E2eTests; +using Nethermind.Libp2p.Protocols.Pubsub; + +namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; + +public static class PubsubTestSetupExtensions +{ + public static void AddPubsubPeerDiscovery(this PubsubTestSetup self, bool start = true) + { + foreach ((int index, PubsubRouter router) in self.Routers) + { + PubsubPeerDiscoveryProtocol disc = new(router, self.PeerStores[index], new PubsubPeerDiscoverySettings() { Interval = 300 }, self.Peers[index]); + if (start) _ = disc.DiscoverAsync(self.Peers[index].ListenAddresses); + } + } +} diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index 3d311237..c5ece94d 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -72,7 +72,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubPeer EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubPeerDiscovery.Tests", "Libp2p.Protocols.PubsubPeerDiscovery.Tests\Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj", "{5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Pubsub.Profiler", "Libp2p.Protocols.Pubsub.Profiler\Libp2p.Protocols.Pubsub.Profiler.csproj", "{BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Pubsub.E2eTests", "Libp2p.Protocols.Pubsub.E2eTests\Libp2p.Protocols.Pubsub.E2eTests.csproj", "{BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Tls", "Libp2p.Protocols.Tls\Libp2p.Protocols.Tls.csproj", "{C3CDBAAE-C790-443A-A293-D6E2330160F7}" EndProject diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 5f0668cf..83a57370 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -23,17 +23,17 @@ protected override async Task ConnectedTo(ISession session, bool isDialer) protected override ProtocolRef SelectProtocol(Multiaddress addr) { - ArgumentNullException.ThrowIfNull(protocolStackSettings.TopProtocols); + ArgumentNullException.ThrowIfNull(_protocolStackSettings.TopProtocols); ProtocolRef? protocol; if (addr.Has()) { - protocol = protocolStackSettings.TopProtocols.FirstOrDefault(proto => proto.Protocol.Id == "quic-v1") ?? throw new ApplicationException("QUICv1 is not supported"); + protocol = _protocolStackSettings.TopProtocols.FirstOrDefault(proto => proto.Protocol.Id == "quic-v1") ?? throw new ApplicationException("QUICv1 is not supported"); } else if (addr.Has()) { - protocol = protocolStackSettings.TopProtocols!.FirstOrDefault(proto => proto.Protocol.Id == "ip-tcp") ?? throw new ApplicationException("TCP is not supported"); + protocol = _protocolStackSettings.TopProtocols!.FirstOrDefault(proto => proto.Protocol.Id == "ip-tcp") ?? throw new ApplicationException("TCP is not supported"); } else { From 9e6fac5062fb0397f21ad90be6032e0ec3604350 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Fri, 6 Dec 2024 19:29:52 +0300 Subject: [PATCH 13/25] Typed requests --- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 5 +- .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 7 +- .../E2e/TestMuxerProtocol.cs | 11 +- .../Libp2p.Core.TestsBase/LocalPeerStub.cs | 10 ++ src/libp2p/Libp2p.Core/Discovery/PeerStore.cs | 1 + src/libp2p/Libp2p.Core/IChannelFactory.cs | 3 +- src/libp2p/Libp2p.Core/IPeer.cs | 6 + src/libp2p/Libp2p.Core/IProtocol.cs | 14 +- src/libp2p/Libp2p.Core/Peer.cs | 122 +++++++++++++++++- src/libp2p/Libp2p.Core/PeerFactory.cs | 5 +- .../IdentifyProtocol.cs | 23 ++-- .../Program.cs | 23 ++-- .../PubSubTestSetup.cs | 19 +++ src/libp2p/Libp2p/Libp2pPeerFactory.cs | 7 +- src/samples/transport-interop/Program.cs | 3 +- 15 files changed, 215 insertions(+), 44 deletions(-) diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs index 756a53d6..97fc87a8 100644 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT using Multiformats.Address; +using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core.Tests; @@ -28,8 +29,8 @@ public async Task E2e() TopProtocols = [tProto] }; - LocalPeer peer1 = new(new Identity(), protocolStackSettings); - LocalPeer peer2 = new(new Identity(), protocolStackSettings); + LocalPeer peer1 = new(new Identity(), new PeerStore(), protocolStackSettings); + LocalPeer peer2 = new(new Identity(), new PeerStore(), protocolStackSettings); await peer1.StartListenAsync([new Multiaddress()]); await peer2.StartListenAsync([new Multiaddress()]); diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index 67675aea..96e96f87 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols; using Nethermind.Libp2p.Stack; using System.Collections.Concurrent; @@ -25,18 +26,18 @@ .. additionalProtocols } } -public class TestPeerFactory(IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings) +public class TestPeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, peerStore) { ConcurrentDictionary peers = new(); public override IPeer Create(Identity? identity = default) { ArgumentNullException.ThrowIfNull(identity); - return peers.GetOrAdd(identity.PeerId, (p) => new TestLocalPeer(identity, protocolStackSettings, loggerFactory)); + return peers.GetOrAdd(identity.PeerId, (p) => new TestLocalPeer(identity, protocolStackSettings, peerStore, loggerFactory)); } } -internal class TestLocalPeer(Identity id, IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : LocalPeer(id, protocolStackSettings, loggerFactory) +internal class TestLocalPeer(Identity id, IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : LocalPeer(id, peerStore, protocolStackSettings, loggerFactory) { protected override async Task ConnectedTo(ISession session, bool isDialer) { diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index 0b7b49c3..89e09c0e 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -94,7 +94,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn uint chanId = Interlocked.Add(ref counter, 2); logger?.LogDebug($"{context.Peer.Identity.PeerId}({chanId}): Sub-request {item.SelectedProtocol} {item.CompletionSource is not null} to call {connection.State.RemoteAddress.GetPeerId()}"); - chans[chanId] = new MuxerChannel { Tcs = item.CompletionSource }; + chans[chanId] = new MuxerChannel { Tcs = item.CompletionSource, Argument = item.Argument }; MuxerPacket response = new() { ChannelId = chanId, @@ -170,7 +170,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn case MuxerPacketType.NewStreamResponse: if (packet.Protocols.Any()) { - UpgradeOptions req = new() { SelectedProtocol = session.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First()), ModeOverride = UpgradeModeOverride.Dial }; + UpgradeOptions req = new() { SelectedProtocol = session.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First()), CompletionSource = chans[packet.ChannelId].Tcs, Argument = chans[packet.ChannelId].Argument, ModeOverride = UpgradeModeOverride.Dial }; IChannel upChannel = session.Upgrade(req); chans[packet.ChannelId].UpChannel = upChannel; logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SelectedProtocol}"); @@ -201,7 +201,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn if (chans[packet.ChannelId].LocalClosedWrites) { - chans[packet.ChannelId].Tcs?.SetResult(); + //chans[packet.ChannelId].Tcs?.SetResult(null); _ = chans[packet.ChannelId].UpChannel?.CloseAsync(); chans.Remove(packet.ChannelId); } @@ -248,7 +248,7 @@ private Task HandleUpchannelData(IChannel downChannel, Dictionary? Tcs { get; set; } public bool RemoteClosedWrites { get; set; } public bool LocalClosedWrites { get; set; } + public object? Argument { get; internal set; } } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs index 4dece69d..cb3bb9ed 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs @@ -31,6 +31,11 @@ public Task DialAsync(Multiaddress[] samePeerAddrs, CancellationToken return Task.FromResult(new TestRemotePeer(samePeerAddrs.First())); } + public Task DialAsync(PeerId peerId, CancellationToken token = default) + { + throw new NotImplementedException(); + } + public Task DisconnectAsync() { return Task.CompletedTask; @@ -60,6 +65,11 @@ public Task DialAsync(CancellationToken token = default) where TProto return Task.CompletedTask; } + public Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol + { + throw new NotImplementedException(); + } + public Task DisconnectAsync() { return Task.CompletedTask; diff --git a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs index 0b136e43..51681092 100644 --- a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs +++ b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs @@ -90,6 +90,7 @@ public PeerInfo GetPeerInfo(PeerId peerId) public class PeerInfo { public ByteString? SignedPeerRecord { get; set; } + public string[]? SupportedProtocols { get; set; } public HashSet? Addrs { get; set; } } } diff --git a/src/libp2p/Libp2p.Core/IChannelFactory.cs b/src/libp2p/Libp2p.Core/IChannelFactory.cs index a83d6acb..698cab80 100644 --- a/src/libp2p/Libp2p.Core/IChannelFactory.cs +++ b/src/libp2p/Libp2p.Core/IChannelFactory.cs @@ -18,7 +18,8 @@ public record UpgradeOptions { public IProtocol? SelectedProtocol { get; init; } public UpgradeModeOverride ModeOverride { get; init; } - public TaskCompletionSource? CompletionSource { get; init; } + public TaskCompletionSource? CompletionSource { get; init; } + public object? Argument { get; set; } } public enum UpgradeModeOverride diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index 7fa44d2d..22ac7743 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -13,6 +13,11 @@ public interface IPeer Task DialAsync(Multiaddress addr, CancellationToken token = default); Task DialAsync(Multiaddress[] samePeerAddrs, CancellationToken token = default); + /// + /// Find existing session or dial a peer if found in peer store + /// + Task DialAsync(PeerId peerId, CancellationToken token = default); + Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default); Task DisconnectAsync(); @@ -28,5 +33,6 @@ public interface ISession { Multiaddress RemoteAddress { get; } Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol; + Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol; Task DisconnectAsync(); } diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index 0a4cdff8..7b35c896 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -22,8 +22,20 @@ public interface IConnectionProtocol : IProtocol Task DialAsync(IChannel downChannel, IConnectionContext context); } -public interface ISessionProtocol : IProtocol + +public interface ISessionListenerProtocol : IProtocol { Task ListenAsync(IChannel downChannel, ISessionContext context); +} + +public interface ISessionProtocol : ISessionListenerProtocol +{ Task DialAsync(IChannel downChannel, ISessionContext context); } + +public class Void; + +public interface ISessionProtocol : ISessionListenerProtocol +{ + Task DialAsync(IChannel downChannel, ISessionContext context, TRequest request); +} diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index c2dda35a..44311834 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using Multiformats.Address; using Multiformats.Address.Protocols; +using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Core.Extensions; using Nethermind.Libp2p.Stack; @@ -15,15 +16,17 @@ namespace Nethermind.Libp2p.Core; public class LocalPeer : IPeer { protected readonly ILogger? _logger; + protected readonly PeerStore _peerStore; protected readonly IProtocolStackSettings _protocolStackSettings; Dictionary> listenerReadyTcs = new(); private ObservableCollection sessions { get; } = []; - public LocalPeer(Identity identity, IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) + public LocalPeer(Identity identity, PeerStore peerStore, IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) { Identity = identity; + _peerStore = peerStore; _protocolStackSettings = protocolStackSettings; _logger = loggerFactory?.CreateLogger($"peer-{identity.PeerId}"); } @@ -50,19 +53,30 @@ public class Session(LocalPeer peer) : ISession public async Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { - TaskCompletionSource tcs = new(); + TaskCompletionSource tcs = new(); SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance() }, token); await tcs.Task; + MarkAsConnected(); } public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = default) { - TaskCompletionSource tcs = new(); + TaskCompletionSource tcs = new(); SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = protocol }, token); await tcs.Task; MarkAsConnected(); } + public async Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol + { + TaskCompletionSource tcs = new(); + SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance(), Argument = request }, token); + await tcs.Task; + MarkAsConnected(); + return (TResponse)tcs.Task.Result; + } + + private CancellationTokenSource connectionTokenSource = new(); public Task DisconnectAsync() @@ -77,7 +91,7 @@ public Task DisconnectAsync() public TaskCompletionSource ConnectedTcs = new(); public Task Connected => ConnectedTcs.Task; - internal void MarkAsConnected() => ConnectedTcs.SetResult(); + internal void MarkAsConnected() => ConnectedTcs?.TrySetResult(); internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); @@ -232,7 +246,7 @@ public async Task DialAsync(Multiaddress[] addrs, CancellationToken to cancellations[addr] = CancellationTokenSource.CreateLinkedTokenSource(token); } - Task timeoutTask = Task.Delay(15_000, token); + Task timeoutTask = Task.Delay(1511111_000, token); Task wait = await TaskHelper.FirstSuccess([timeoutTask, .. addrs.Select(addr => DialAsync(addr, cancellations[addr].Token))]); if (wait == timeoutTask) @@ -281,6 +295,25 @@ public async Task DialAsync(Multiaddress addr, CancellationToken token return session; } + public Task DialAsync(PeerId peerId, CancellationToken token = default) + { + ISession? existingSession = sessions.FirstOrDefault(s => s.State.RemotePeerId == peerId); + + if (existingSession is not null) + { + return Task.FromResult(existingSession); + } + + PeerStore.PeerInfo existingPeerInfo = _peerStore.GetPeerInfo(peerId); + + if (existingPeerInfo?.Addrs is null) + { + throw new Libp2pException("Peer not found"); + } + + return DialAsync([.. existingPeerInfo.Addrs], token); + } + internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) { if (_protocolStackSettings.Protocols is null) @@ -321,6 +354,38 @@ internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol break; } default: + if (isListener && top.Protocol is ISessionListenerProtocol listenerProtocol) + { + SessionContext ctx = new(this, session, top, isListener, options); + upgradeTask = listenerProtocol.ListenAsync(downChannel.Reverse, ctx); + break; + } + + var genericInterface = top.Protocol.GetType().GetInterfaces() + .FirstOrDefault(i => + i.IsGenericType && + i.GetGenericTypeDefinition() == typeof(ISessionProtocol<,>)); + + if (genericInterface != null) + { + var genericArguments = genericInterface.GetGenericArguments(); + var requestType = genericArguments[0]; + var responseType = genericArguments[1]; + + if (options?.Argument is not null && !options.Argument.GetType().IsAssignableTo(requestType)) + { + throw new ArgumentException($"Invalid request. Argument is of {options.Argument.GetType()} type which is not assignable to {requestType.FullName}"); + } + + // Dynamically invoke DialAsync + var dialAsyncMethod = genericInterface.GetMethod("DialAsync"); + if (dialAsyncMethod != null) + { + SessionContext ctx = new(this, session, top, isListener, options); + upgradeTask = (Task)dialAsyncMethod.Invoke(top.Protocol, [downChannel.Reverse, ctx, options?.Argument])!; + break; + } + } throw new Libp2pSetupException($"Protocol {top.Protocol} does not implement proper protocol interface"); } @@ -345,6 +410,21 @@ internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol return downChannel; } + private static void MapToTaskCompletionSource(Task t, TaskCompletionSource tcs) + { + if (t.IsCompletedSuccessfully) + { + tcs.SetResult(t.GetType().GenericTypeArguments.Any() ? t.GetType().GetProperty("Result")!.GetValue(t) : null); + return; + } + if (t.IsCanceled) + { + tcs.SetCanceled(); + return; + } + tcs.SetException(t.Exception!); + } + private static void MapToTaskCompletionSource(Task t, TaskCompletionSource tcs) { if (t.IsCompletedSuccessfully) @@ -396,6 +476,38 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef break; } default: + if (isListener && top.Protocol is ISessionListenerProtocol listenerProtocol) + { + SessionContext ctx = new(this, session, top, isListener, options); + upgradeTask = listenerProtocol.ListenAsync(parentChannel, ctx); + break; + } + + var genericInterface = top.Protocol.GetType().GetInterfaces() + .FirstOrDefault(i => + i.IsGenericType && + i.GetGenericTypeDefinition() == typeof(ISessionProtocol<,>)); + + if (genericInterface != null) + { + var genericArguments = genericInterface.GetGenericArguments(); + var requestType = genericArguments[0]; + var responseType = genericArguments[1]; + + if (options?.Argument is not null && !options.Argument.GetType().IsInstanceOfType(requestType)) + { + throw new ArgumentException($"Invalid request. Argument is of {options.Argument.GetType()} type which is not assignable to {requestType.FullName}"); + } + + // Dynamically invoke DialAsync + var dialAsyncMethod = genericInterface.GetMethod("DialAsync"); + if (dialAsyncMethod != null) + { + SessionContext ctx = new(this, session, top, isListener, options); + upgradeTask = (Task)dialAsyncMethod.Invoke(top.Protocol, [parentChannel, ctx, options?.Argument])!; + break; + } + } throw new Libp2pSetupException($"Protocol {top.Protocol} does not implement proper protocol interface"); } diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 01285f92..56844d3d 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -2,16 +2,17 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; -public class PeerFactory(IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : IPeerFactory +public class PeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : IPeerFactory { protected IProtocolStackSettings protocolStackSettings = protocolStackSettings; public virtual IPeer Create(Identity? identity = default) { - return new LocalPeer(identity ?? new Identity(), protocolStackSettings, loggerFactory); + return new LocalPeer(identity ?? new Identity(), peerStore, protocolStackSettings, loggerFactory); } } diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 6c0a7a3d..55f0f4ce 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -9,7 +9,6 @@ using Nethermind.Libp2p.Stack; using System.Net.Sockets; using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Core.Dto; namespace Nethermind.Libp2p.Protocols; @@ -43,6 +42,7 @@ public IdentifyProtocol(IProtocolStackSettings protocolStackSettings, IdentifyPr public async Task DialAsync(IChannel channel, ISessionContext context) { ArgumentNullException.ThrowIfNull(context.State.RemotePublicKey); + ArgumentNullException.ThrowIfNull(context.State.RemotePeerId); _logger?.LogInformation("Dial"); @@ -50,20 +50,21 @@ public async Task DialAsync(IChannel channel, ISessionContext context) _logger?.LogInformation("Received peer info: {identify}", identify); - if (_peerStore is not null && identify.SignedPeerRecord is not null) + if (_peerStore is not null) { - if (!SigningHelper.VerifyPeerRecord(identify.SignedPeerRecord, context.State.RemotePublicKey)) - { - throw new PeerConnectionException(); - } + _peerStore.GetPeerInfo(context.State.RemotePeerId).SupportedProtocols = identify.Protocols.ToArray(); - if (context.State.RemotePeerId is null) + if (identify.SignedPeerRecord is not null) { - throw new Libp2pException("No remote peer id is set"); - } - _peerStore.GetPeerInfo(context.State.RemotePeerId).SignedPeerRecord = identify.SignedPeerRecord; + if (!SigningHelper.VerifyPeerRecord(identify.SignedPeerRecord, context.State.RemotePublicKey)) + { + throw new PeerConnectionException(); + } - _logger?.LogInformation("Confirmed peer record: {peerId}", context.State.RemotePeerId); + _peerStore.GetPeerInfo(context.State.RemotePeerId).SignedPeerRecord = identify.SignedPeerRecord; + + _logger?.LogInformation("Confirmed peer record: {peerId}", context.State.RemotePeerId); + } } if (context.State.RemotePublicKey.ToByteString() != identify.PublicKey) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs index 41dbbb28..49f11bfe 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs @@ -2,22 +2,25 @@ // SPDX-License-Identifier: MIT using Libp2p.Protocols.Pubsub.E2eTests; -using Nethermind.Libp2p.Protocols.Pubsub; +using Nethermind.Libp2p.Core; -int totalCount = 9; +int totalCount = 2; PubsubTestSetup test = new(); await test.StartPeersAsync(totalCount); -test.StartPubsub(); -test.Subscribe("test"); +//test.StartPubsub(); +//test.Subscribe("test"); -foreach ((int index, PubsubRouter router) in test.Routers.Skip(1)) -{ - test.PeerStores[index].Discover(test.Peers[0].ListenAddresses.ToArray()); -} +//foreach ((int index, PubsubRouter router) in test.Routers.Skip(1)) +//{ +// test.PeerStores[index].Discover(test.Peers[0].ListenAddresses.ToArray()); +//} -await test.WaitForFullMeshAsync("test"); +//await test.WaitForFullMeshAsync("test"); -test.PrintState(true); +//test.PrintState(true); + +ISession session = await test.Peers[0].DialAsync(test.Peers[1].ListenAddresses.ToArray()); +Console.WriteLine(await session.DialAsync(1)); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs index b7b6db0d..c47592b2 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs @@ -10,6 +10,24 @@ namespace Libp2p.Protocols.Pubsub.E2eTests; + +public class TestRequestResponseProtocol : ISessionProtocol +{ + public string Id => "1"; + + public async Task DialAsync(IChannel downChannel, ISessionContext context, int request) + { + await downChannel.WriteVarintAsync(request); + return await downChannel.ReadVarintAsync(); + } + + public async Task ListenAsync(IChannel downChannel, ISessionContext context) + { + var request = await downChannel.ReadVarintAsync(); + await downChannel.WriteVarintAsync(request + 1); + } +} + public class PubsubTestSetup { protected static TestContextLoggerFactory loggerFactory = new(); @@ -33,6 +51,7 @@ public async Task StartPeersAsync(int count, PubsubSettings? customPubsubSetting .AddSingleton(sp => new TestBuilder(sp) .AddAppLayerProtocol() .AddAppLayerProtocol() + .AddAppLayerProtocol() .AddAppLayerProtocol() .AddAppLayerProtocol()) .AddSingleton(sp => new TestContextLoggerFactory()) diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 83a57370..392203fe 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -5,16 +5,17 @@ using Multiformats.Address; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols; namespace Nethermind.Libp2p.Stack; -public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, loggerFactory) +public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, peerStore, loggerFactory) { - public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, identity ?? new Identity(), loggerFactory); + public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, peerStore, identity ?? new Identity(), loggerFactory); } -class Libp2pPeer(IProtocolStackSettings protocolStackSettings, Identity identity, ILoggerFactory? loggerFactory = null) : LocalPeer(identity, protocolStackSettings, loggerFactory) +class Libp2pPeer(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, Identity identity, ILoggerFactory? loggerFactory = null) : LocalPeer(identity, peerStore, protocolStackSettings, loggerFactory) { protected override async Task ConnectedTo(ISession session, bool isDialer) { diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index 2b006a0f..10007b2a 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Multiformats.Address; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols; using StackExchange.Redis; @@ -46,7 +47,7 @@ Log($"Dialing {listenerAddr}..."); Stopwatch handshakeStartInstant = Stopwatch.StartNew(); - ISession remotePeer = await localPeer.DialAsync(listenerAddr); + ISession remotePeer = await localPeer.DialAsync((Multiaddress)listenerAddr); Stopwatch pingIstant = Stopwatch.StartNew(); await remotePeer.DialAsync(); From 6997b37c74ff92643d49458d998ab39425db022c Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Wed, 11 Dec 2024 12:52:15 +0300 Subject: [PATCH 14/25] Fix tests --- .github/workflows/test.yml | 6 +- src/libp2p/Directory.Packages.props | 4 +- src/libp2p/Libp2p.Core.Tests/ContextTests.cs | 221 ------------------ .../Libp2p.Core.Tests/TaskHelperTests.cs | 12 +- .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 9 +- .../E2e/TestMuxerProtocol.cs | 6 +- .../E2e/TestMuxerTests.cs | 19 +- .../Libp2p.Core.TestsBase/E2e/TestSuite.cs | 12 - .../Libp2p.Core/Exceptions/Libp2pException.cs | 2 +- .../Libp2p.Core/Extensions/TaskHelper.cs | 2 +- src/libp2p/Libp2p.Core/IPeer.cs | 2 +- src/libp2p/Libp2p.Core/IProtocol.cs | 3 + src/libp2p/Libp2p.Core/IWriter.cs | 8 +- src/libp2p/Libp2p.Core/Peer.cs | 84 ++++--- src/libp2p/Libp2p.Core/PeerFactory.cs | 5 +- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 2 +- src/libp2p/Libp2p.Core/Utils/IpHelper.cs | 11 + src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs | 100 ++++++++ .../IncrementNumberTestProtocol.cs | 20 ++ .../Libp2p.E2eTests/Libp2p.E2eTests.csproj | 31 +++ .../Libp2p.E2eTests/RequestResponseTests.cs | 24 ++ .../IdentifyProtocol.cs | 7 +- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 48 ++-- .../MDnsDiscoveryProtocol.cs | 2 +- .../Libp2p.Protocols.Noise.Tests.csproj | 2 +- .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 20 +- .../Libp2p.Protocols.Pubsub.E2eTests.csproj | 20 +- .../Program.cs | 26 --- .../PubSubTestSetup.cs | 160 ------------- .../PubsubE2eTestSetup.cs | 91 ++++++++ .../FloodsubProtocolTests.cs | 7 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 3 +- .../PubsubRouter.Topics.cs | 4 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 20 +- .../Libp2p.Protocols.Pubsub/TtlCache.cs | 2 +- ...ocols.PubsubPeerDiscovery.E2eTests.csproj} | 11 +- .../NetworkDiscoveryTests.cs | 29 +++ .../PubsubDiscoveryE2eTestSetup.cs | 36 +++ .../E2eTests.cs | 33 --- .../PubsubTestSetupExtensions.cs | 16 -- .../Usings.cs | 6 - .../PubsubPeerDiscoveryProtocol.cs | 10 +- .../PubsubPeerDiscoverySettings.cs | 12 + .../Libp2p.Protocols.Quic.Tests.csproj | 1 - .../ProtocolTests.cs | 19 ++ .../Libp2p.Protocols.Quic.csproj | 1 + .../Libp2p.Protocols.Quic/QuicProtocol.cs | 29 ++- .../Libp2p.Protocols.Tls/TlsProtocol.cs | 2 +- .../Libp2p.Protocols.Yamux/YamuxProtocol.cs | 42 ++-- src/libp2p/Libp2p.sln | 20 +- src/libp2p/Libp2p/Libp2pPeerFactory.cs | 29 +-- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 33 +-- .../perf-benchmarks/PerfBenchmarks.csproj | 1 - src/samples/pubsub-chat/Program.cs | 4 +- src/samples/pubsub-chat/PubsubChat.csproj | 1 - .../transport-interop/TransportInterop.csproj | 1 - .../transport-interop/packages.lock.json | 6 +- 57 files changed, 627 insertions(+), 710 deletions(-) delete mode 100644 src/libp2p/Libp2p.Core.Tests/ContextTests.cs delete mode 100644 src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs create mode 100644 src/libp2p/Libp2p.Core/Utils/IpHelper.cs create mode 100644 src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs create mode 100644 src/libp2p/Libp2p.E2eTests/IncrementNumberTestProtocol.cs create mode 100644 src/libp2p/Libp2p.E2eTests/Libp2p.E2eTests.csproj create mode 100644 src/libp2p/Libp2p.E2eTests/RequestResponseTests.cs delete mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs delete mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubsubE2eTestSetup.cs rename src/libp2p/{Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj => Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests.csproj} (81%) create mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/NetworkDiscoveryTests.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs delete mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs delete mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs delete mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Usings.cs create mode 100644 src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoverySettings.cs create mode 100644 src/libp2p/Libp2p.Protocols.Quic.Tests/ProtocolTests.cs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5352e82e..413ad491 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,11 @@ jobs: TEST_OPTS: -c ${{ env.BUILD_CONFIG }} --no-restore run: | dotnet test Libp2p.Core.Tests ${{ env.PACK_OPTS }} - #dotnet test Libp2p.Protocols.Multistream.Tests ${{ env.PACK_OPTS }} + dotnet test Libp2p.Protocols.Multistream.Tests ${{ env.PACK_OPTS }} dotnet test Libp2p.Protocols.Noise.Tests ${{ env.PACK_OPTS }} dotnet test Libp2p.Protocols.Pubsub.Tests ${{ env.PACK_OPTS }} dotnet test Libp2p.Protocols.Quic.Tests ${{ env.PACK_OPTS }} + dotnet test Libp2p.Protocols.Yamux.Tests ${{ env.PACK_OPTS }} + dotnet test Libp2p.E2eTests ${{ env.PACK_OPTS }} + dotnet test Libp2p.Protocols.Pubsub.E2eTests ${{ env.PACK_OPTS }} + dotnet test Libp2p.Protocols.PubsubPeerDiscovery.E2eTests ${{ env.PACK_OPTS }} diff --git a/src/libp2p/Directory.Packages.props b/src/libp2p/Directory.Packages.props index 9e92e867..bdb7f4a2 100644 --- a/src/libp2p/Directory.Packages.props +++ b/src/libp2p/Directory.Packages.props @@ -23,7 +23,7 @@ - + @@ -34,4 +34,4 @@ - \ No newline at end of file + diff --git a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs b/src/libp2p/Libp2p.Core.Tests/ContextTests.cs deleted file mode 100644 index 97fc87a8..00000000 --- a/src/libp2p/Libp2p.Core.Tests/ContextTests.cs +++ /dev/null @@ -1,221 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Multiformats.Address; -using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Stack; - -namespace Nethermind.Libp2p.Core.Tests; -public class ContextTests -{ - public static Channel tcp = new(); - - - [Test] - public async Task E2e() - { - ProtocolRef tProto = new(new TProto()); - ProtocolRef cProto = new(new CProto()); - ProtocolRef sProto = new(new SProto()); - ProtocolRef sProto2 = new(new SProto2()); - - ProtocolStackSettings protocolStackSettings = new() - { - Protocols = new Dictionary - { - { tProto, [ cProto] }, - { cProto, [sProto, sProto2] }, - }, - TopProtocols = [tProto] - }; - - LocalPeer peer1 = new(new Identity(), new PeerStore(), protocolStackSettings); - LocalPeer peer2 = new(new Identity(), new PeerStore(), protocolStackSettings); - - await peer1.StartListenAsync([new Multiaddress()]); - await peer2.StartListenAsync([new Multiaddress()]); - - ISession session = await peer2.DialAsync(new Multiaddress()); - - //await session.DialAsync(); - //await session.DialAsync(); - - //ITransportContext tContext = peer.CreateContext(tProto); - - //ITransportConnectionContext tcContext = tContext.CreateConnection(); - //tcContext.SubDial(); - - //IConnectionContext cContext = peer.CreateContext(cProto); - - //cContext.SubDial(); - //ISession connectionSessionContext = cContext.UpgradeToSession(); - - //ISessionContext sContext = peer.CreateContext(sProto); - - //sContext.SubDial(); - //sContext.DialAsync(); - - //sContext.Disconnect(); - await Task.Delay(1000_0000); - } -} - -class ProtocolStackSettings : IProtocolStackSettings -{ - public Dictionary? Protocols { get; set; } = []; - public ProtocolRef[]? TopProtocols { get; set; } = []; -} - -class TProto : ITransportProtocol -{ - public string Id => nameof(TProto); - - public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) - { - try - { - context.ListenerReady(Multiaddress.Decode("/ip4/127.0.0.1/tcp/4096")); - using INewConnectionContext connectionCtx = context.CreateConnection(); - connectionCtx.State.RemoteAddress = Multiaddress.Decode("/ip4/127.0.0.1/tcp/1000"); - - IChannel topChan = connectionCtx.Upgrade(); - connectionCtx.Token.Register(() => topChan.CloseAsync()); - - - ReadResult received; - while (true) - { - received = await ContextTests.tcp.ReadAsync(1, ReadBlockingMode.WaitAny); - if (received.Result != IOResult.Ok) - { - break; - } - - IOResult sent = await topChan.WriteAsync(received.Data); - - if (sent != IOResult.Ok) - { - break; - } - } - await topChan.CloseAsync(); - } - catch - { - - } - } - - public async Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) - { - INewConnectionContext connectionContext = context.CreateConnection(); - IChannel topChan = connectionContext.Upgrade(); - connectionContext.Token.Register(() => topChan.CloseAsync()); - - - ReadResult received; - while (true) - { - received = await topChan.ReadAsync(1, ReadBlockingMode.WaitAny); - if (received.Result != IOResult.Ok) - { - break; - } - - IOResult sent = await ContextTests.tcp.WriteAsync(received.Data); - - if (sent != IOResult.Ok) - { - break; - } - } - await topChan.CloseAsync(); - } -} - -class CProto : IConnectionProtocol -{ - public string Id => throw new NotImplementedException(); - - public async Task DialAsync(IChannel downChannel, IConnectionContext context) - { - - using INewSessionContext session = context.UpgradeToSession(); - IChannel topChan = context.Upgrade(); - - ReadResult received; - while (true) - { - received = await topChan.ReadAsync(1, ReadBlockingMode.WaitAny); - if (received.Result != IOResult.Ok) - { - break; - } - - IOResult sent = await downChannel.WriteAsync(received.Data); - - if (sent != IOResult.Ok) - { - break; - } - } - await topChan.CloseAsync(); - - } - - public async Task ListenAsync(IChannel downChannel, IConnectionContext context) - { - - using INewSessionContext session = context.UpgradeToSession(); - IChannel topChan = context.Upgrade(); - - ReadResult received; - while (true) - { - received = await downChannel.ReadAsync(1, ReadBlockingMode.WaitAny); - if (received.Result != IOResult.Ok) - { - break; - } - - IOResult sent = await topChan.WriteAsync(received.Data); - - if (sent != IOResult.Ok) - { - break; - } - } - await topChan.CloseAsync(); - - } -} - -class SProto : ISessionProtocol -{ - public string Id => throw new NotImplementedException(); - - public async Task DialAsync(IChannel downChannel, ISessionContext context) - { - await downChannel.WriteLineAsync("Oh hi there"); - } - - public async Task ListenAsync(IChannel downChannel, ISessionContext context) - { - string line = await downChannel.ReadLineAsync(); - } -} - -class SProto2 : ISessionProtocol -{ - public string Id => throw new NotImplementedException(); - - public Task DialAsync(IChannel downChannel, ISessionContext context) - { - throw new NotImplementedException(); - } - - public Task ListenAsync(IChannel downChannel, ISessionContext context) - { - throw new NotImplementedException(); - } -} diff --git a/src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs b/src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs index a76a25c7..772740ff 100644 --- a/src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs +++ b/src/libp2p/Libp2p.Core.Tests/TaskHelperTests.cs @@ -20,8 +20,15 @@ public async Task Test_AllExceptions_RaiseAggregateException() tcs2.SetException(new Exception()); tcs3.SetException(new Exception()); - - Task r = await t; + await t.ContinueWith((t) => + { + Assert.Multiple(() => + { + Assert.That(t.IsFaulted, Is.True); + Assert.That(t.Exception?.InnerException, Is.TypeOf()); + Assert.That((t.Exception?.InnerException as AggregateException)?.InnerExceptions, Has.Count.EqualTo(3)); + }); + }); } [Test] @@ -40,5 +47,6 @@ public async Task Test_SingleSuccess_ReturnsCompletedTask() Task result = await t; Assert.That(result, Is.EqualTo(tcs3.Task)); + Assert.That((result as Task)!.Result, Is.EqualTo(true)); } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index 96e96f87..5a499280 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -41,13 +41,6 @@ internal class TestLocalPeer(Identity id, IProtocolStackSettings protocolStackSe { protected override async Task ConnectedTo(ISession session, bool isDialer) { - try - { - await session.DialAsync(); - } - catch - { - - } + await session.DialAsync(); } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs index 89e09c0e..10fd051d 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerProtocol.cs @@ -17,6 +17,8 @@ class TestMuxerProtocol(ChannelBus bus, ILoggerFactory? loggerFactory = null) : private readonly ILogger? logger = loggerFactory?.CreateLogger(id); public string Id => id; + public static Multiaddress[] GetDefaultAddresses(PeerId peerId) => [$"/p2p/{peerId}"]; + public static bool IsAddressMatch(Multiaddress addr) => true; public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token) { @@ -144,7 +146,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn UpgradeOptions req = new() { SelectedProtocol = selected, ModeOverride = UpgradeModeOverride.Listen }; - IChannel upChannel = session.Upgrade(req); + IChannel upChannel = session.Upgrade(selected, req); chans[packet.ChannelId] = new MuxerChannel { UpChannel = upChannel }; _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); @@ -171,7 +173,7 @@ private async Task HandleRemote(IChannel downChannel, INewConnectionContext conn if (packet.Protocols.Any()) { UpgradeOptions req = new() { SelectedProtocol = session.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First()), CompletionSource = chans[packet.ChannelId].Tcs, Argument = chans[packet.ChannelId].Argument, ModeOverride = UpgradeModeOverride.Dial }; - IChannel upChannel = session.Upgrade(req); + IChannel upChannel = session.Upgrade(session.SubProtocols.FirstOrDefault(x => x.Id == packet.Protocols.First())!, req); chans[packet.ChannelId].UpChannel = upChannel; logger?.LogDebug($"{logPrefix}({packet.ChannelId}): Start upchanel with {req.SelectedProtocol}"); _ = HandleUpchannelData(downChannel, chans, packet.ChannelId, upChannel, logPrefix); diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs index 653153e7..2e2ae85e 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Stack; using NUnit.Framework; namespace Nethermind.Libp2p.Core.TestsBase.E2e; @@ -11,21 +12,21 @@ internal class TestMuxerTests [Test] public async Task Test_ConnectionEstablished_AfterHandshake() { - ServiceProvider sp = new ServiceCollection() + ChannelBus channelBus = new(); + ServiceProvider MakeServiceProvider() => new ServiceCollection() .AddSingleton(sp => new TestBuilder(sp)) + .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton(channelBus) .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); - IPeerFactory peerFactory = sp.GetService()!; + IPeer peerA = MakeServiceProvider().GetRequiredService().Create(TestPeers.Identity(1)); + await peerA.StartListenAsync(); + IPeer peerB = MakeServiceProvider().GetRequiredService().Create(TestPeers.Identity(2)); + await peerB.StartListenAsync(); - IPeer peerA = peerFactory.Create(TestPeers.Identity(1)); - await peerA.StartListenAsync([TestPeers.Multiaddr(1)]); - IPeer peerB = peerFactory.Create(TestPeers.Identity(2)); - await peerB.StartListenAsync([TestPeers.Multiaddr(2)]); - - ISession remotePeerB = await peerA.DialAsync(TestPeers.Multiaddr(1)); + ISession remotePeerB = await peerA.DialAsync(TestPeers.Multiaddr(2)); await remotePeerB.DialAsync(); } } diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs deleted file mode 100644 index a355e05b..00000000 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestSuite.cs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core.TestsBase.E2e; - -public class TestSuite -{ - public static IPeerFactory CreateLibp2p(params Type[] appProcols) - { - return new TestBuilder().Build(); - } -} diff --git a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs index a841eebb..92cd6625 100644 --- a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs +++ b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs @@ -15,7 +15,7 @@ public Libp2pException() : base() } } -public class ChannelClosedException : Libp2pException; +public class ChannelClosedException() : Libp2pException("Channel closed"); /// /// Appears when libp2p is not set up properly in part of protocol tack, IoC, etc. diff --git a/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs index 0797e0ea..249a68f9 100644 --- a/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs +++ b/src/libp2p/Libp2p.Core/Extensions/TaskHelper.cs @@ -26,7 +26,7 @@ public static async Task FirstSuccess(params Task[] tasks) Task result = await Task.WhenAny(tcs.Task, all); if (result == all) { - throw new AggregateException(tasks.Select(t => t.Exception).Where(ex => ex is not null)!); + throw new AggregateException(tasks.Select(t => t.Exception?.InnerException).Where(ex => ex is not null)!); } return tcs.Task.Result; } diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/IPeer.cs index 22ac7743..64530716 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/IPeer.cs @@ -18,7 +18,7 @@ public interface IPeer /// Task DialAsync(PeerId peerId, CancellationToken token = default); - Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default); + Task StartListenAsync(Multiaddress[]? addrs = default, CancellationToken token = default); Task DisconnectAsync(); diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index 7b35c896..7e3a5a3e 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -12,6 +12,9 @@ public interface IProtocol public interface ITransportProtocol : IProtocol { + static abstract Multiaddress[] GetDefaultAddresses(PeerId peerId); + static abstract bool IsAddressMatch(Multiaddress addr); + Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token); Task DialAsync(ITransportContext context, Multiaddress remoteAddr, CancellationToken token); } diff --git a/src/libp2p/Libp2p.Core/IWriter.cs b/src/libp2p/Libp2p.Core/IWriter.cs index 3948e798..7e652811 100644 --- a/src/libp2p/Libp2p.Core/IWriter.cs +++ b/src/libp2p/Libp2p.Core/IWriter.cs @@ -47,8 +47,12 @@ ValueTask WriteSizeAndDataAsync(byte[] data) async ValueTask WriteSizeAndProtobufAsync(T grpcMessage) where T : IMessage { - byte[] serializedMessage = grpcMessage.ToByteArray(); - await WriteSizeAndDataAsync(serializedMessage); + int length = grpcMessage.CalculateSize(); + byte[] buf = new byte[VarInt.GetSizeInBytes(length) + length]; + int offset = 0; + VarInt.Encode(length, buf, ref offset); + grpcMessage.WriteTo(buf.AsSpan(offset)); + await WriteAsync(new ReadOnlySequence(buf)); } ValueTask WriteAsync(ReadOnlySequence bytes, CancellationToken token = default); diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 44311834..c44016a0 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -19,7 +19,7 @@ public class LocalPeer : IPeer protected readonly PeerStore _peerStore; protected readonly IProtocolStackSettings _protocolStackSettings; - Dictionary> listenerReadyTcs = new(); + Dictionary> listenerReadyTcs = []; private ObservableCollection sessions { get; } = []; @@ -33,7 +33,7 @@ public LocalPeer(Identity identity, PeerStore peerStore, IProtocolStackSettings public override string ToString() { - return $"peer({Identity.PeerId}): sessions {string.Join("|", sessions.Select(x => $"{x.State.RemotePeerId}"))}"; + return $"peer({Identity.PeerId}): addresses {string.Join(",", ListenAddresses)} sessions {string.Join("|", sessions.Select(x => $"{x.State.RemotePeerId}"))}"; } public Identity Identity { get; } @@ -100,18 +100,21 @@ public Task DisconnectAsync() protected virtual ProtocolRef SelectProtocol(Multiaddress addr) { - if (_protocolStackSettings.TopProtocols is null) + if (_protocolStackSettings.TopProtocols is null or []) { throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - if (_protocolStackSettings.TopProtocols.Length is not 1) + return _protocolStackSettings.TopProtocols.First(p => (bool)p.Protocol.GetType().GetMethod(nameof(ITransportProtocol.IsAddressMatch))!.Invoke(null, [addr])!); + } + protected virtual Multiaddress[] GetDefaultAddresses() + { + if (_protocolStackSettings.TopProtocols is null or []) { - throw new Libp2pSetupException("Top protocol should be single one by default"); - + throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - return _protocolStackSettings.TopProtocols.Single(); + return _protocolStackSettings.TopProtocols.SelectMany(p => (Multiaddress[])p.Protocol.GetType().GetMethod(nameof(ITransportProtocol.GetDefaultAddresses))!.Invoke(null, [Identity.PeerId])!).ToArray(); } protected virtual IEnumerable PrepareAddresses(Multiaddress[] addrs) @@ -132,8 +135,10 @@ protected virtual IEnumerable PrepareAddresses(Multiaddress[] addr public event Connected? OnConnected; - public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationToken token = default) + public virtual async Task StartListenAsync(Multiaddress[]? addrs = default, CancellationToken token = default) { + addrs ??= GetDefaultAddresses(); + List listenTasks = new(addrs.Length); foreach (Multiaddress addr in PrepareAddresses(addrs)) @@ -158,11 +163,29 @@ public virtual async Task StartListenAsync(Multiaddress[] addrs, CancellationTok ListenAddresses.Remove(tcs.Task.Result); }); - listenTasks.Add(tcs.Task.WaitAsync(TimeSpan.FromMilliseconds(5000))); - ListenAddresses.Add(tcs.Task.Result); + listenTasks.Add(tcs.Task.WaitAsync(TimeSpan.FromMilliseconds(5000)).ContinueWith(t => + { + if (t.IsFaulted) + { + _logger?.LogDebug($"Failed to start listener for an address"); + return null; + } + + return t.Result; + })); } await Task.WhenAll(listenTasks); + + foreach (Task startTask in listenTasks) + { + Multiaddress? addr = (startTask as Task)?.Result; + + if (addr is not null) + { + ListenAddresses.Add(addr); + } + } } public void ListenerReady(object sender, Multiaddress addr) @@ -204,7 +227,7 @@ public INewSessionContext UpgradeToSession(Session session, ProtocolRef proto, b _logger?.LogError(t.Exception.InnerException, $"Disconnecting due to exception"); return; } - session.ConnectedTcs.SetResult(); + session.ConnectedTcs.TrySetResult(); OnConnected?.Invoke(session); }); return new NewSessionContext(this, session, proto, isListener, null); @@ -240,13 +263,13 @@ public async Task DialAsync(Multiaddress[] addrs, CancellationToken to return existingSession; } - Dictionary cancellations = new(); + Dictionary cancellations = []; foreach (Multiaddress addr in addrs) { cancellations[addr] = CancellationTokenSource.CreateLinkedTokenSource(token); } - Task timeoutTask = Task.Delay(1511111_000, token); + Task timeoutTask = Task.Delay(15_000, token); Task wait = await TaskHelper.FirstSuccess([timeoutTask, .. addrs.Select(addr => DialAsync(addr, cancellations[addr].Token))]); if (wait == timeoutTask) @@ -326,8 +349,7 @@ internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol throw new Libp2pSetupException($"{parentProtocol} is not added"); } - ProtocolRef top = upgradeProtocol is not null ? new ProtocolRef(upgradeProtocol) : - options?.SelectedProtocol is not null ? _protocolStackSettings.Protocols[parentProtocol].SingleOrDefault(x => x.Protocol == options.SelectedProtocol) ?? new ProtocolRef(options.SelectedProtocol) : + ProtocolRef top = upgradeProtocol is not null ? _protocolStackSettings.Protocols[parentProtocol].SingleOrDefault(x => x.Protocol == options.SelectedProtocol) ?? new ProtocolRef(upgradeProtocol) : _protocolStackSettings.Protocols[parentProtocol].Single(); Channel downChannel = new(); @@ -361,24 +383,22 @@ internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol break; } - var genericInterface = top.Protocol.GetType().GetInterfaces() + Type? genericInterface = top.Protocol.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ISessionProtocol<,>)); if (genericInterface != null) { - var genericArguments = genericInterface.GetGenericArguments(); - var requestType = genericArguments[0]; - var responseType = genericArguments[1]; + Type[] genericArguments = genericInterface.GetGenericArguments(); + Type requestType = genericArguments[0]; if (options?.Argument is not null && !options.Argument.GetType().IsAssignableTo(requestType)) { throw new ArgumentException($"Invalid request. Argument is of {options.Argument.GetType()} type which is not assignable to {requestType.FullName}"); } - // Dynamically invoke DialAsync - var dialAsyncMethod = genericInterface.GetMethod("DialAsync"); + System.Reflection.MethodInfo? dialAsyncMethod = genericInterface.GetMethod("DialAsync"); if (dialAsyncMethod != null) { SessionContext ctx = new(this, session, top, isListener, options); @@ -405,6 +425,7 @@ internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol _logger?.LogError($"Upgrade task failed for {top} with {t.Exception}"); } _ = downChannel.CloseAsync(); + _logger?.LogInformation($"Finished {parentProtocol} to {top}, listen={isListener}"); }); return downChannel; @@ -483,24 +504,24 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef break; } - var genericInterface = top.Protocol.GetType().GetInterfaces() + Type? genericInterface = top.Protocol.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ISessionProtocol<,>)); if (genericInterface != null) { - var genericArguments = genericInterface.GetGenericArguments(); - var requestType = genericArguments[0]; - var responseType = genericArguments[1]; + Type[] genericArguments = genericInterface.GetGenericArguments(); + Type requestType = genericArguments[0]; + Type responseType = genericArguments[1]; - if (options?.Argument is not null && !options.Argument.GetType().IsInstanceOfType(requestType)) + if (options?.Argument is not null && !options.Argument.GetType().IsAssignableTo(requestType)) { throw new ArgumentException($"Invalid request. Argument is of {options.Argument.GetType()} type which is not assignable to {requestType.FullName}"); } // Dynamically invoke DialAsync - var dialAsyncMethod = genericInterface.GetMethod("DialAsync"); + System.Reflection.MethodInfo? dialAsyncMethod = genericInterface.GetMethod("DialAsync"); if (dialAsyncMethod != null) { SessionContext ctx = new(this, session, top, isListener, options); @@ -527,6 +548,7 @@ await upgradeTask.ContinueWith(t => _logger?.LogError($"Upgrade task failed with {t.Exception}"); } _ = parentChannel.CloseAsync(); + _logger?.LogInformation($"Finished#2 {protocol} to {top}, listen={isListener}"); }); } @@ -605,14 +627,14 @@ public IChannel Upgrade(UpgradeOptions? upgradeOptions = null) return localPeer.Upgrade(session, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); } - public Task Upgrade(IChannel parentChannel, UpgradeOptions? upgradeOptions = null) + public IChannel Upgrade(IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) { - return localPeer.Upgrade(session, parentChannel, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); + return localPeer.Upgrade(session, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); } - public IChannel Upgrade(IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) + public Task Upgrade(IChannel parentChannel, UpgradeOptions? upgradeOptions = null) { - return localPeer.Upgrade(session, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); + return localPeer.Upgrade(session, parentChannel, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); } public Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 56844d3d..bc554ba7 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -11,8 +11,11 @@ public class PeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore { protected IProtocolStackSettings protocolStackSettings = protocolStackSettings; + protected PeerStore PeerStore { get; } = peerStore; + protected ILoggerFactory? LoggerFactory { get; } = loggerFactory; + public virtual IPeer Create(Identity? identity = default) { - return new LocalPeer(identity ?? new Identity(), peerStore, protocolStackSettings, loggerFactory); + return new LocalPeer(identity ?? new Identity(), PeerStore, protocolStackSettings, LoggerFactory); } } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index f81d01ff..86800156 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -46,7 +46,7 @@ private TProtocol CreateProtocolInstance(IServiceProvider serviceProv } - private readonly List _appLayerProtocols = new(); + private readonly List _appLayerProtocols = []; public IEnumerable AppLayerProtocols => _appLayerProtocols.Select(x => x.Protocol); internal readonly IServiceProvider ServiceProvider; diff --git a/src/libp2p/Libp2p.Core/Utils/IpHelper.cs b/src/libp2p/Libp2p.Core/Utils/IpHelper.cs new file mode 100644 index 00000000..3921878b --- /dev/null +++ b/src/libp2p/Libp2p.Core/Utils/IpHelper.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using System.Net; +using System.Net.NetworkInformation; + +namespace Nethermind.Libp2p.Core.Utils; +public class IpHelper +{ + public static IEnumerable GetListenerAddresses() => NetworkInterface.GetAllNetworkInterfaces().SelectMany(i => i.GetIPProperties().UnicastAddresses.Select(a => a.Address)); +} diff --git a/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs b/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs new file mode 100644 index 00000000..3c8e571c --- /dev/null +++ b/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs @@ -0,0 +1,100 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Core.TestsBase; +using Nethermind.Libp2p.Stack; +using System.Text; + +namespace Libp2p.E2eTests; + +public class E2eTestSetup : IDisposable +{ + private readonly CancellationTokenSource _commonTokenSource = new(); + public void Dispose() + { + _commonTokenSource.Cancel(); + _commonTokenSource.Dispose(); + } + + protected CancellationToken Token => _commonTokenSource.Token; + + protected static TestContextLoggerFactory loggerFactory = new(); + private int _peerCounter = 0; + + protected ILogger TestLogger { get; set; } = loggerFactory.CreateLogger("test-setup"); + + public Dictionary Peers { get; } = []; + public Dictionary PeerStores { get; } = []; + public Dictionary ServiceProviders { get; } = []; + + protected virtual IPeerFactoryBuilder ConfigureLibp2p(ILibp2pPeerFactoryBuilder builder) + { + return builder.AddAppLayerProtocol(); + } + + protected virtual IServiceCollection ConfigureServices(IServiceCollection col) + { + return col; + } + + protected virtual void AddToPrintState(StringBuilder sb, int index) + { + } + + protected virtual void AddAt(int index) + { + + } + + public async Task AddPeersAsync(int count) + { + int totalCount = _peerCounter + count; + + for (; _peerCounter < totalCount; _peerCounter++) + { + // But we create a seprate setup for every peer + ServiceProvider sp = ServiceProviders[_peerCounter] = + ConfigureServices( + new ServiceCollection() + .AddLibp2p(ConfigureLibp2p) + .AddSingleton(sp => new TestContextLoggerFactory()) + ) + .BuildServiceProvider(); + + PeerStores[_peerCounter] = ServiceProviders[_peerCounter].GetService()!; + Peers[_peerCounter] = sp.GetService()!.Create(TestPeers.Identity(_peerCounter)); + + await Peers[_peerCounter].StartListenAsync(token: Token); + + AddAt(_peerCounter); + } + } + + + private int stateCounter = 1; + + public void PrintState(bool outputToConsole = false) + { + StringBuilder reportBuilder = new(); + reportBuilder.AppendLine($"Test state#{stateCounter++}"); + + foreach ((int index, IPeer peer) in Peers) + { + AddToPrintState(reportBuilder, index); + reportBuilder.AppendLine(peer.ToString()); + reportBuilder.AppendLine(); + } + + string report = reportBuilder.ToString(); + + if (outputToConsole) + { + Console.WriteLine(report); + } + else + { + TestLogger.LogInformation(report.ToString()); + } + } +} diff --git a/src/libp2p/Libp2p.E2eTests/IncrementNumberTestProtocol.cs b/src/libp2p/Libp2p.E2eTests/IncrementNumberTestProtocol.cs new file mode 100644 index 00000000..af81e1e5 --- /dev/null +++ b/src/libp2p/Libp2p.E2eTests/IncrementNumberTestProtocol.cs @@ -0,0 +1,20 @@ +using Nethermind.Libp2p.Core; + +namespace Libp2p.E2eTests; + +public class IncrementNumberTestProtocol : ISessionProtocol +{ + public string Id => "1"; + + public async Task DialAsync(IChannel downChannel, ISessionContext context, int request) + { + await downChannel.WriteVarintAsync(request); + return await downChannel.ReadVarintAsync(); + } + + public async Task ListenAsync(IChannel downChannel, ISessionContext context) + { + int request = await downChannel.ReadVarintAsync(); + await downChannel.WriteVarintAsync(request + 1); + } +} diff --git a/src/libp2p/Libp2p.E2eTests/Libp2p.E2eTests.csproj b/src/libp2p/Libp2p.E2eTests/Libp2p.E2eTests.csproj new file mode 100644 index 00000000..9c85b1e2 --- /dev/null +++ b/src/libp2p/Libp2p.E2eTests/Libp2p.E2eTests.csproj @@ -0,0 +1,31 @@ + + + + enable + enable + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/src/libp2p/Libp2p.E2eTests/RequestResponseTests.cs b/src/libp2p/Libp2p.E2eTests/RequestResponseTests.cs new file mode 100644 index 00000000..3d38f9b7 --- /dev/null +++ b/src/libp2p/Libp2p.E2eTests/RequestResponseTests.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; +using NUnit.Framework; + +namespace Libp2p.E2eTests; + +public class RequestResponseTests +{ + [Test] + public async Task Test_RequestReponse() + { + E2eTestSetup test = new(); + int request = 1; + + await test.AddPeersAsync(2); + ISession session = await test.Peers[0].DialAsync(test.Peers[1].ListenAddresses.ToArray()); + int response = await session.DialAsync(1); + + Assert.That(response, Is.EqualTo(request + 1)); + } + +} diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 55f0f4ce..353dccbd 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -85,16 +85,13 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) ListenAddrs = { context.Peer.ListenAddresses.Select(x => ByteString.CopyFrom(x.ToBytes())) }, ObservedAddr = ByteString.CopyFrom(context.State.RemoteAddress!.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto).ToBytes()), Protocols = { _protocolStackSettings.Protocols!.Select(r => r.Key.Protocol).OfType().Select(p => p.Id) }, - SignedPeerRecord = SigningHelper.CreateSignedEnvelope(context.Peer.Identity, context.Peer.ListenAddresses.ToArray(), 1), + SignedPeerRecord = SigningHelper.CreateSignedEnvelope(context.Peer.Identity, [.. context.Peer.ListenAddresses], 1), }; ByteString[] endpoints = context.Peer.ListenAddresses.Where(a => !a.ToEndPoint().Address.IsPrivate()).Select(a => a.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto)).Select(a => ByteString.CopyFrom(a.ToBytes())).ToArray(); identify.ListenAddrs.AddRange(endpoints); - byte[] ar = new byte[identify.CalculateSize()]; - identify.WriteTo(ar); - - await channel.WriteSizeAndDataAsync(ar); + await channel.WriteSizeAndProtobufAsync(identify); _logger?.LogDebug("Sent peer info {identify}", identify); } } diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index cd162634..25de344f 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -10,6 +10,7 @@ using Multiformats.Address.Protocols; using Multiformats.Address.Net; using Nethermind.Libp2p.Core.Exceptions; +using Nethermind.Libp2p.Core.Utils; namespace Nethermind.Libp2p.Protocols; @@ -18,6 +19,9 @@ public class IpTcpProtocol(ILoggerFactory? loggerFactory = null) : ITransportPro private readonly ILogger? _logger = loggerFactory?.CreateLogger(); public string Id => "ip-tcp"; + public static Multiaddress[] GetDefaultAddresses(PeerId peerId) => IpHelper.GetListenerAddresses() + .Select(a => Multiaddress.Decode($"/{(a.AddressFamily is AddressFamily.InterNetwork ? "ip4" : "ip6")}/{a}/tcp/0/p2p/{peerId}")).Where(x => x.Has()).Take(1).ToArray(); + public static bool IsAddressMatch(Multiaddress addr) => addr.Has(); public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) { @@ -28,6 +32,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr listener.Bind(endpoint); listener.Listen(); + if (endpoint.Port is 0) { IPEndPoint localIpEndpoint = (IPEndPoint)listener.LocalEndPoint!; @@ -48,6 +53,7 @@ await Task.Run(async () => INewConnectionContext connectionCtx = context.CreateConnection(); connectionCtx.Token.Register(client.Close); + connectionCtx.State.RemoteAddress = client.RemoteEndPoint.ToMultiaddress(ProtocolType.Tcp); IChannel upChannel = connectionCtx.Upgrade(); @@ -91,7 +97,7 @@ await Task.Run(async () => } } } - catch (SocketException) + catch (SocketException e) { _logger?.LogInformation($"Disconnected due to a socket exception"); await upChannel.CloseAsync(); @@ -131,8 +137,8 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, } INewConnectionContext connectionCtx = context.CreateConnection(); - connectionCtx.State.RemoteAddress = ToMultiaddress(client.RemoteEndPoint, ProtocolType.Tcp); - connectionCtx.State.LocalAddress = ToMultiaddress(client.LocalEndPoint, ProtocolType.Tcp); + connectionCtx.State.RemoteAddress = client.RemoteEndPoint.ToMultiaddress(ProtocolType.Tcp); + connectionCtx.State.LocalAddress = client.LocalEndPoint.ToMultiaddress(ProtocolType.Tcp); connectionCtx.Token.Register(client.Close); token.Register(client.Close); @@ -141,11 +147,11 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, Task receiveTask = Task.Run(async () => { - byte[] buf = new byte[client.ReceiveBufferSize]; try { for (; client.Connected;) { + byte[] buf = new byte[client.ReceiveBufferSize]; int dataLength = await client.ReceiveAsync(buf, SocketFlags.None); _logger?.LogDebug("Ctx{0}: receive, length={1}", connectionCtx.Id, dataLength); @@ -178,42 +184,14 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, catch (SocketException) { _ = upChannel.CloseAsync(); + return; } + + client.Close(); }); await Task.WhenAll(receiveTask, sendTask).ContinueWith(t => connectionCtx.Dispose()); _ = upChannel.CloseAsync(); } - - - public static Multiaddress ToMultiaddress(EndPoint ep, ProtocolType protocolType) - { - Multiaddress multiaddress = new(); - IPEndPoint iPEndPoint = (IPEndPoint)ep; - if (iPEndPoint != null) - { - if (iPEndPoint.AddressFamily == AddressFamily.InterNetwork) - { - multiaddress.Add(iPEndPoint.Address.MapToIPv4()); - } - - if (iPEndPoint.AddressFamily == AddressFamily.InterNetworkV6) - { - multiaddress.Add(iPEndPoint.Address.MapToIPv6()); - } - - if (protocolType == ProtocolType.Tcp) - { - multiaddress.Add(iPEndPoint.Port); - } - - if (protocolType == ProtocolType.Udp) - { - multiaddress.Add(iPEndPoint.Port); - } - } - - return multiaddress; - } } diff --git a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs index f4f0a948..2b3d256e 100644 --- a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs @@ -78,7 +78,7 @@ public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, Canc _logger?.LogTrace("Inst disc {0}, nmsg: {1}", e.ServiceInstanceName, e.Message); if (records.Length != 0 && !peers.Contains(records[0]) && localPeerId != records[0].Get().ToString()) { - List peerAddresses = new(); + List peerAddresses = []; foreach (Multiaddress peer in records) { peers.Add(peer); diff --git a/src/libp2p/Libp2p.Protocols.Noise.Tests/Libp2p.Protocols.Noise.Tests.csproj b/src/libp2p/Libp2p.Protocols.Noise.Tests/Libp2p.Protocols.Noise.Tests.csproj index 860c1a07..e4deaead 100644 --- a/src/libp2p/Libp2p.Protocols.Noise.Tests/Libp2p.Protocols.Noise.Tests.csproj +++ b/src/libp2p/Libp2p.Protocols.Noise.Tests/Libp2p.Protocols.Noise.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index d426d8e6..99eddb0a 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -26,11 +26,11 @@ public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILog ); private readonly ILogger? _logger = loggerFactory?.CreateLogger(); - private readonly NoiseExtensions _extensions = new() + private NoiseExtensions _extensions => new() { StreamMuxers = { - multiplexerSettings is null ? ["na"] : !multiplexerSettings.Multiplexers.Any() ? ["na"] : [.. multiplexerSettings.Multiplexers.Select(proto => proto.Id)] + multiplexerSettings is null || !multiplexerSettings.Multiplexers.Any() ? ["na"] : [.. multiplexerSettings.Multiplexers.Select(proto => proto.Id)] } }; @@ -63,8 +63,11 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) (int BytesRead, byte[] HandshakeHash, Transport Transport) msg1 = handshakeState.ReadMessage(received.ToArray(), buffer); NoiseHandshakePayload? msg1Decoded = NoiseHandshakePayload.Parser.ParseFrom(buffer.AsSpan(0, msg1.BytesRead)); + PublicKey? msg1KeyDecoded = PublicKey.Parser.ParseFrom(msg1Decoded.IdentityKey); - //var key = new byte[] { 0x1 }.Concat(clientStatic.PublicKey).ToArray(); + context.State.RemotePublicKey = msg1KeyDecoded; + // TODO: verify signature + List responderMuxers = msg1Decoded.Extensions.StreamMuxers .Where(m => !string.IsNullOrEmpty(m)) .ToList(); @@ -163,10 +166,10 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) handshakeState.ReadMessage(hs2Bytes.ToArray(), buffer); NoiseHandshakePayload? msg2Decoded = NoiseHandshakePayload.Parser.ParseFrom(buffer.AsSpan(0, msg2.BytesRead)); PublicKey? msg2KeyDecoded = PublicKey.Parser.ParseFrom(msg2Decoded.IdentityKey); - Transport? transport = msg2.Transport; - - PeerId remotePeerId = new(msg2KeyDecoded); + context.State.RemotePublicKey = msg2KeyDecoded; + // TODO: verify signature + Transport? transport = msg2.Transport; List initiatorMuxers = msg2Decoded.Extensions.StreamMuxers.Where(m => !string.IsNullOrEmpty(m)).ToList(); IProtocol? commonMuxer = multiplexerSettings?.Multiplexers.FirstOrDefault(m => initiatorMuxers.Contains(m.Id)); @@ -180,8 +183,9 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) }; } - if (context.State.RemotePeerId is null) + if (!context.State.RemoteAddress.Has()) { + PeerId remotePeerId = new(msg2KeyDecoded); context.State.RemoteAddress.Add(new P2P(remotePeerId.ToString())); } @@ -249,7 +253,7 @@ private static Task ExchangeData(Transport transport, IChannel downChannel, ICha } }); - return Task.WhenAny(t, t2).ContinueWith((t) => + return Task.WhenAll(t, t2).ContinueWith((t) => { }); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Libp2p.Protocols.Pubsub.E2eTests.csproj b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Libp2p.Protocols.Pubsub.E2eTests.csproj index b3a22fa1..990a0d3f 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Libp2p.Protocols.Pubsub.E2eTests.csproj +++ b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Libp2p.Protocols.Pubsub.E2eTests.csproj @@ -1,16 +1,32 @@ - Exe - net8.0 enable enable + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs deleted file mode 100644 index 49f11bfe..00000000 --- a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Libp2p.Protocols.Pubsub.E2eTests; -using Nethermind.Libp2p.Core; - - -int totalCount = 2; -PubsubTestSetup test = new(); - -await test.StartPeersAsync(totalCount); -//test.StartPubsub(); -//test.Subscribe("test"); - -//foreach ((int index, PubsubRouter router) in test.Routers.Skip(1)) -//{ -// test.PeerStores[index].Discover(test.Peers[0].ListenAddresses.ToArray()); -//} - -//await test.WaitForFullMeshAsync("test"); - -//test.PrintState(true); - - -ISession session = await test.Peers[0].DialAsync(test.Peers[1].ListenAddresses.ToArray()); -Console.WriteLine(await session.DialAsync(1)); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs deleted file mode 100644 index c47592b2..00000000 --- a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubSubTestSetup.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Core.TestsBase; -using Nethermind.Libp2p.Core.TestsBase.E2e; -using Nethermind.Libp2p.Protocols.Pubsub; -using Nethermind.Libp2p.Stack; -using System.Text; - -namespace Libp2p.Protocols.Pubsub.E2eTests; - - -public class TestRequestResponseProtocol : ISessionProtocol -{ - public string Id => "1"; - - public async Task DialAsync(IChannel downChannel, ISessionContext context, int request) - { - await downChannel.WriteVarintAsync(request); - return await downChannel.ReadVarintAsync(); - } - - public async Task ListenAsync(IChannel downChannel, ISessionContext context) - { - var request = await downChannel.ReadVarintAsync(); - await downChannel.WriteVarintAsync(request + 1); - } -} - -public class PubsubTestSetup -{ - protected static TestContextLoggerFactory loggerFactory = new(); - private int Counter = 0; - - public PubsubSettings DefaultSettings { get; set; } = new PubsubSettings { LowestDegree = 2, Degree = 3, LazyDegree = 3, HighestDegree = 4, HeartbeatInterval = 200 }; - protected ILogger testLogger { get; set; } = loggerFactory.CreateLogger("test-setup"); - - public ChannelBus CommonBus { get; } = new(loggerFactory); - public Dictionary Peers { get; } = new(); - public Dictionary PeerStores { get; } = new(); - public Dictionary Routers { get; } = new(); - public Dictionary ServiceProviders { get; } = new(); - - public async Task StartPeersAsync(int count, PubsubSettings? customPubsubSettings = null) - { - for (int i = Counter; i < Counter + count; i++) - { - // But we create a seprate setup for every peer - ServiceProvider sp = ServiceProviders[i] = new ServiceCollection() - .AddSingleton(sp => new TestBuilder(sp) - .AddAppLayerProtocol() - .AddAppLayerProtocol() - .AddAppLayerProtocol() - .AddAppLayerProtocol() - .AddAppLayerProtocol()) - .AddSingleton(sp => new TestContextLoggerFactory()) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton(CommonBus) - .AddSingleton(sp => customPubsubSettings ?? DefaultSettings) - .AddSingleton(sp => sp.GetService()!.Build()) - .BuildServiceProvider(); - - PeerStores[i] = ServiceProviders[i].GetService()!; - Peers[i] = sp.GetService()!.Create(TestPeers.Identity(i)); - Routers[i] = sp.GetService()!; - - await Peers[i].StartListenAsync([TestPeers.Multiaddr(i)]); - } - } - - public void StartPubsub() - { - foreach ((int index, PubsubRouter router) in Routers) - { - _ = router.RunAsync(Peers[index]); - } - } - - /// - /// Manual heartbeat in case the period is set to infinite - /// - /// - public async Task Heartbeat() - { - foreach (PubsubRouter router in Routers.Values) - { - await router.Heartbeat(); - } - } - - private int stateCounter = 1; - - public void PrintState(bool outputToConsole = false) - { - StringBuilder reportBuilder = new(); - reportBuilder.AppendLine($"Test state#{stateCounter++}"); - - foreach ((int index, PubsubRouter router) in Routers) - { - reportBuilder.AppendLine(router.ToString()); - reportBuilder.AppendLine(Peers[index].ToString()); - reportBuilder.AppendLine(); - } - - string report = reportBuilder.ToString(); - - if (outputToConsole) - { - Console.WriteLine(report); - } - else - { - testLogger.LogInformation(report.ToString()); - } - } - - public void Subscribe(string topic) - { - foreach (PubsubRouter router in Routers.Values) - { - router.GetTopic(topic); - } - } - - public async Task WaitForFullMeshAsync(string topic, int timeoutMs = 15_000) - { - int requiredCount = int.Min(Routers.Count - 1, DefaultSettings.LowestDegree); - - CancellationTokenSource cts = new(); - Task delayTask = Task.Delay(timeoutMs).ContinueWith((t) => cts.Cancel()); - - while (true) - { - if (cts.IsCancellationRequested) - { - PrintState(); - throw new Exception("Timeout waiting for the network"); - } - PrintState(); - - cts.Token.ThrowIfCancellationRequested(); - await Task.Delay(100); - - bool stillWaiting = false; - - foreach (IRoutingStateContainer router in Routers.Values) - { - if (router.Mesh[topic].Count < requiredCount) - { - stillWaiting = true; - } - } - - if (!stillWaiting) break; - } - } -} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubsubE2eTestSetup.cs b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubsubE2eTestSetup.cs new file mode 100644 index 00000000..990f78c5 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub.E2eTests/PubsubE2eTestSetup.cs @@ -0,0 +1,91 @@ +using Libp2p.E2eTests; +using Microsoft.Extensions.DependencyInjection; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Protocols.Pubsub; +using System.Text; + +namespace Libp2p.Protocols.Pubsub.E2eTests; + +public class PubsubE2eTestSetup : E2eTestSetup +{ + public PubsubSettings DefaultSettings { get; set; } = new PubsubSettings { LowestDegree = 2, Degree = 3, LazyDegree = 3, HighestDegree = 4, HeartbeatInterval = 200 }; + public Dictionary Routers { get; } = []; + + + protected override IPeerFactoryBuilder ConfigureLibp2p(ILibp2pPeerFactoryBuilder builder) + { + return base.ConfigureLibp2p(builder.WithPubsub()); + } + + protected override IServiceCollection ConfigureServices(IServiceCollection col) + { + return base.ConfigureServices(col); + } + + protected override void AddToPrintState(StringBuilder sb, int index) + { + base.AddToPrintState(sb, index); + sb.AppendLine(Routers[index].ToString()); + } + + protected override void AddAt(int index) + { + base.AddAt(index); + Routers[index] = ServiceProviders[index].GetService()!; + _ = Routers[index].StartAsync(Peers[index]); + } + + /// + /// Manual heartbeat in case the period is set to infinite + /// + /// + public async Task Heartbeat() + { + foreach (PubsubRouter router in Routers.Values) + { + await router.Heartbeat(); + } + } + + public void Subscribe(string topic) + { + foreach (PubsubRouter router in Routers.Values) + { + router.GetTopic(topic); + } + } + + public async Task WaitForFullMeshAsync(string topic, int timeoutMs = 15_000) + { + int requiredCount = int.Min(Routers.Count - 1, DefaultSettings.LowestDegree); + + CancellationTokenSource cts = new(); + Task delayTask = Task.Delay(timeoutMs).ContinueWith((t) => cts.Cancel()); + + while (true) + { + PrintState(); + + if (cts.IsCancellationRequested) + { + throw new Exception("Timeout waiting for the network"); + } + + + cts.Token.ThrowIfCancellationRequested(); + await Task.Delay(1000); + + bool stillWaiting = false; + + foreach (IRoutingStateContainer router in Routers.Values) + { + if (router.Mesh[topic].Count < requiredCount) + { + stillWaiting = true; + } + } + + if (!stillWaiting) break; + } + } +} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index f0465548..c4433534 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -10,6 +10,7 @@ namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; [TestFixture] public class FloodsubProtocolTests { + [Ignore("TODO")] [Test] public async Task Test_Peer_is_in_fpeers() { @@ -29,15 +30,15 @@ public async Task Test_Peer_is_in_fpeers() peer.DialAsync(discoveredPeerAddress, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddress)); CancellationToken token = default; - List sentRpcs = new(); + List sentRpcs = []; - _ = router.RunAsync(peer, token: token); + _ = router.StartAsync(peer, token: token); router.GetTopic(commonTopic); Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); peerStore.Discover([discoveredPeerAddress]); await Task.Delay(100); - _ = peer.Received().DialAsync(discoveredPeerAddress, Arg.Any()); + _ = peer.Received().DialAsync([discoveredPeerAddress], Arg.Any()); TaskCompletionSource tcs = new(); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 0c2f6a55..0e50e148 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -4,9 +4,10 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Exceptions; +using Nethermind.Libp2p.Protocols.Pubsub; using Nethermind.Libp2p.Protocols.Pubsub.Dto; -namespace Nethermind.Libp2p.Protocols.Pubsub; +namespace Nethermind.Libp2p.Protocols; /// /// https://github.com/libp2p/specs/tree/master/pubsub diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs index 7bbbf8c5..a354d177 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs @@ -89,7 +89,7 @@ public void UnsubscribeAll() peerState.GetValueOrDefault(peerId)?.Send(msg); } - Dictionary peerMessages = new(); + Dictionary peerMessages = []; foreach (PeerId? peerId in gPeers.SelectMany(kv => kv.Value)) { @@ -137,7 +137,7 @@ public void Publish(string topicId, byte[] message) else { fanoutLastPublished[topicId] = DateTime.Now; - HashSet topicFanout = fanout.GetOrAdd(topicId, _ => new HashSet()); + HashSet topicFanout = fanout.GetOrAdd(topicId, _ => []); if (topicFanout.Count == 0) { diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index b48ea9dd..7ddfc52d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -19,10 +19,11 @@ public interface IRoutingStateContainer ConcurrentDictionary> Fanout { get; } ConcurrentDictionary FanoutLastPublished { get; } ICollection ConnectedPeers { get; } + bool Started { get; } Task Heartbeat(); } -public partial class PubsubRouter : IRoutingStateContainer +public partial class PubsubRouter : IRoutingStateContainer, IDisposable { static int routerCounter = 0; readonly int routerId = Interlocked.Increment(ref routerCounter); @@ -124,6 +125,7 @@ public Action? SendRpc ConcurrentDictionary> IRoutingStateContainer.Mesh => mesh; ConcurrentDictionary> IRoutingStateContainer.Fanout => fanout; ConcurrentDictionary IRoutingStateContainer.FanoutLastPublished => fanoutLastPublished; + bool IRoutingStateContainer.Started => localPeer is not null; ICollection IRoutingStateContainer.ConnectedPeers => peerState.Keys; Task IRoutingStateContainer.Heartbeat() => Heartbeat(); #endregion @@ -155,7 +157,7 @@ public Action? SendRpc // all peers with their connection status private readonly ConcurrentDictionary peerState = new(); - private readonly ConcurrentBag reconnections = new(); + private readonly ConcurrentBag reconnections = []; private readonly PeerStore _peerStore; private ulong seqNo = 1; @@ -179,7 +181,7 @@ public PubsubRouter(PeerStore store, PubsubSettings? settings = null, ILoggerFac _dontWantMessages = new(_settings.MessageCacheTtl); } - public async Task RunAsync(IPeer localPeer, CancellationToken token = default) + public async Task StartAsync(IPeer localPeer, CancellationToken token = default) { logger?.LogDebug($"Running pubsub for {string.Join(",", localPeer.ListenAddresses)}"); @@ -190,7 +192,6 @@ public async Task RunAsync(IPeer localPeer, CancellationToken token = default) this.localPeer = localPeer; - logger?.LogInformation("Started"); _peerStore.OnNewPeer += (addrs) => { @@ -239,12 +240,15 @@ public async Task RunAsync(IPeer localPeer, CancellationToken token = default) } }, token); - await Task.Delay(Timeout.Infinite, token); + logger?.LogInformation("Started"); + } + + public void Dispose() + { _messageCache.Dispose(); _limboMessageCache.Dispose(); } - private async Task Reconnect(CancellationToken token) { while (reconnections.TryTake(out Reconnection? rec)) @@ -652,7 +656,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) if (rpc.Control.Ihave.Any()) { - List messageIds = new(); + List messageIds = []; foreach (ControlIHave? ihave in rpc.Control.Ihave .Where(iw => topicState.ContainsKey(iw.TopicID))) @@ -677,7 +681,7 @@ internal async Task OnRpc(PeerId peerId, Rpc rpc) if (rpc.Control.Iwant.Any()) { IEnumerable messageIds = rpc.Control.Iwant.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())); - List messages = new(); + List messages = []; foreach (MessageId? mId in messageIds) { Message message = _messageCache.Get(mId); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/TtlCache.cs b/src/libp2p/Libp2p.Protocols.Pubsub/TtlCache.cs index e8d6edef..1c777b74 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/TtlCache.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/TtlCache.cs @@ -13,7 +13,7 @@ private struct CachedItem public DateTimeOffset ValidTill { get; set; } } - private readonly SortedDictionary items = new(); + private readonly SortedDictionary items = []; private bool isDisposed; public TtlCache(int ttl) diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests.csproj similarity index 81% rename from src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj rename to src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests.csproj index eac2a5e3..a0a90143 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests.csproj @@ -3,13 +3,14 @@ enable enable - Nethermind.$(MSBuildProjectName.Replace(" ", "_")) - false - Nethermind.$(MSBuildProjectName) + + + + @@ -18,14 +19,14 @@ - - + + diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/NetworkDiscoveryTests.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/NetworkDiscoveryTests.cs new file mode 100644 index 00000000..19dd6c22 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/NetworkDiscoveryTests.cs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core.Discovery; +using NUnit.Framework; + +namespace Libp2p.Protocols.PubsubPeerDiscovery.E2eTests; + +public class NetworkDiscoveryTests +{ + [Test] + public async Task Test_NetworkDiscoveredByEveryPeer() + { + string commonTopic = "test"; + + int totalCount = 2; + using PubsubDiscoveryE2eTestSetup test = new(); + + await test.AddPeersAsync(totalCount); + test.Subscribe(commonTopic); + foreach ((_, PeerStore peerStore) in test.PeerStores.Skip(1)) + { + peerStore.Discover(test.Peers[0].ListenAddresses.ToArray()); + } + + + await test.WaitForFullMeshAsync(commonTopic); + } +} diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs new file mode 100644 index 00000000..28327853 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs @@ -0,0 +1,36 @@ +using Libp2p.E2eTests; +using Libp2p.Protocols.Pubsub.E2eTests; +using Microsoft.Extensions.DependencyInjection; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Protocols; +using Nethermind.Libp2p.Protocols.PubsubPeerDiscovery; + +namespace Libp2p.Protocols.PubsubPeerDiscovery.E2eTests; + +public class PubsubDiscoveryE2eTestSetup : PubsubE2eTestSetup +{ + public PubsubPeerDiscoverySettings DefaultDiscoverySettings { get; set; } = new PubsubPeerDiscoverySettings { Interval = 300 }; + + public Dictionary Discovery { get; } = []; + + protected override IPeerFactoryBuilder ConfigureLibp2p(ILibp2pPeerFactoryBuilder builder) + { + return base.ConfigureLibp2p(builder) + .AddAppLayerProtocol(); + } + + protected override IServiceCollection ConfigureServices(IServiceCollection col) + { + return base.ConfigureServices(col) + .AddSingleton(new PubsubPeerDiscoverySettings()) + .AddSingleton(); + } + + protected override void AddAt(int index) + { + base.AddAt(index); + Discovery[index] = new PubsubPeerDiscoveryProtocol(Routers[index], PeerStores[index], DefaultDiscoverySettings, Peers[index], loggerFactory); + + _ = Discovery[index].DiscoverAsync(Peers[index].ListenAddresses, Token); + } +} diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs deleted file mode 100644 index e1ee165c..00000000 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/E2eTests.cs +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -using Libp2p.Protocols.Pubsub.E2eTests; -using Nethermind.Libp2p.Protocols.Pubsub; -using NUnit.Framework.Internal; - -namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; - -public class E2eTests -{ - [Test] - public async Task Test_NetworkEstablished() - { - int totalCount = 10; - PubsubTestSetup test = new(); - - await test.StartPeersAsync(totalCount); - test.StartPubsub(); - test.AddPubsubPeerDiscovery(); - test.Subscribe("test"); - - foreach ((int index, PubsubRouter router) in test.Routers.Skip(1)) - { - test.PeerStores[index].Discover(test.Peers[0].ListenAddresses.ToArray()); - } - - - await test.WaitForFullMeshAsync("test", 150_000); - - test.PrintState(); - } -} diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs deleted file mode 100644 index 1c1fe3e5..00000000 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/PubsubTestSetupExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Libp2p.Protocols.Pubsub.E2eTests; -using Nethermind.Libp2p.Protocols.Pubsub; - -namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Tests; - -public static class PubsubTestSetupExtensions -{ - public static void AddPubsubPeerDiscovery(this PubsubTestSetup self, bool start = true) - { - foreach ((int index, PubsubRouter router) in self.Routers) - { - PubsubPeerDiscoveryProtocol disc = new(router, self.PeerStores[index], new PubsubPeerDiscoverySettings() { Interval = 300 }, self.Peers[index]); - if (start) _ = disc.DiscoverAsync(self.Peers[index].ListenAddresses); - } - } -} diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Usings.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Usings.cs deleted file mode 100644 index 6c255752..00000000 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.Tests/Usings.cs +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -global using Nethermind.Libp2p.Core; -global using Nethermind.Libp2p.Core.TestsBase; -global using NUnit.Framework; diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs index d3067d98..523adcda 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs @@ -1,17 +1,11 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Nethermind.Libp2p.Protocols.PubsubPeerDiscovery; using Nethermind.Libp2p.Protocols.PubsubPeerDiscovery.Dto; namespace Nethermind.Libp2p.Protocols; -public class PubsubPeerDiscoverySettings -{ - public string[] Topics { get; set; } = ["_peer-discovery._p2p._pubsub"]; - public int Interval { get; set; } = 10_000; - public bool ListenOnly { get; set; } -} - public class PubsubPeerDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore peerStore, PubsubPeerDiscoverySettings settings, IPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol { private readonly PubsubRouter _pubSubRouter = pubSubRouter; @@ -19,7 +13,7 @@ public class PubsubPeerDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore pe private PeerId? localPeerId; private ITopic[]? topics; private readonly PubsubPeerDiscoverySettings _settings = settings; - private ILogger? logger = loggerFactory?.CreateLogger(); + private readonly ILogger? logger = loggerFactory?.CreateLogger(); public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, CancellationToken token = default) { diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoverySettings.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoverySettings.cs new file mode 100644 index 00000000..a948182c --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoverySettings.cs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Protocols.PubsubPeerDiscovery; + +public class PubsubPeerDiscoverySettings +{ + public string[] Topics { get; set; } = ["_peer-discovery._p2p._pubsub"]; + public int Interval { get; set; } = 10_000; + public bool ListenOnly { get; set; } +} + diff --git a/src/libp2p/Libp2p.Protocols.Quic.Tests/Libp2p.Protocols.Quic.Tests.csproj b/src/libp2p/Libp2p.Protocols.Quic.Tests/Libp2p.Protocols.Quic.Tests.csproj index bde9026d..c7fd46c0 100644 --- a/src/libp2p/Libp2p.Protocols.Quic.Tests/Libp2p.Protocols.Quic.Tests.csproj +++ b/src/libp2p/Libp2p.Protocols.Quic.Tests/Libp2p.Protocols.Quic.Tests.csproj @@ -3,7 +3,6 @@ enable enable - true Nethermind.$(MSBuildProjectName.Replace(" ", "_")) Nethermind.$(MSBuildProjectName) diff --git a/src/libp2p/Libp2p.Protocols.Quic.Tests/ProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Quic.Tests/ProtocolTests.cs new file mode 100644 index 00000000..0b72f45d --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Quic.Tests/ProtocolTests.cs @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; + +namespace Nethermind.Libp2p.Protocols.Quic.Tests; + +public class ProtocolTests +{ + [Test] + public async Task Test_CreateProtocol() + { + CancellationTokenSource cts = new(); + QuicProtocol proto = new(); + _ = new QuicProtocol().ListenAsync(new TransportContext(new LocalPeer(new Identity(), new Core.Discovery.PeerStore(), new ProtocolStackSettings()), new ProtocolRef(proto), true), "/ip4/127.0.0.1/udp/0", cts.Token); + await Task.Delay(1000); + cts.Cancel(); + } +} diff --git a/src/libp2p/Libp2p.Protocols.Quic/Libp2p.Protocols.Quic.csproj b/src/libp2p/Libp2p.Protocols.Quic/Libp2p.Protocols.Quic.csproj index 75536ddd..b4bd9659 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/Libp2p.Protocols.Quic.csproj +++ b/src/libp2p/Libp2p.Protocols.Quic/Libp2p.Protocols.Quic.csproj @@ -7,6 +7,7 @@ latest Nethermind.$(MSBuildProjectName) Nethermind.$(MSBuildProjectName.Replace(" ", "_")) + true diff --git a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs index 01075083..28f853bd 100644 --- a/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Quic/QuicProtocol.cs @@ -5,42 +5,39 @@ using Multiformats.Address; using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.Utils; using Nethermind.Libp2p.Protocols.Quic; using System.Buffers; using System.Net; using System.Net.Quic; using System.Net.Security; using System.Net.Sockets; -using System.Runtime.Versioning; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; namespace Nethermind.Libp2p.Protocols; #pragma warning disable CA1416 // Do not inform about platform compatibility +#pragma warning disable CA2252 // Do not inform about platform compatibility /// /// https://github.com/libp2p/specs/blob/master/quic/README.md /// -[RequiresPreviewFeatures] -public class QuicProtocol : ITransportProtocol +public class QuicProtocol(ILoggerFactory? loggerFactory = null) : ITransportProtocol { - private readonly ILogger? _logger; - private readonly ECDsa _sessionKey; + private readonly ILogger? _logger = loggerFactory?.CreateLogger(); + private readonly ECDsa _sessionKey = ECDsa.Create(); - public QuicProtocol(ILoggerFactory? loggerFactory = null) - { - _logger = loggerFactory?.CreateLogger(); - _sessionKey = ECDsa.Create(); - } - - private static readonly List protocols = new() - { + private static readonly List protocols = + [ new SslApplicationProtocol("libp2p"), // SslApplicationProtocol.Http3, // webtransport - }; + ]; public string Id => "quic-v1"; + public static Multiaddress[] GetDefaultAddresses(PeerId peerId) => IpHelper.GetListenerAddresses() + .Select(a => Multiaddress.Decode($"/{(a.AddressFamily is AddressFamily.InterNetwork ? "ip4" : "ip6")}/{a}/udp/0/quic-v1/p2p/{peerId}")).ToArray(); + public static bool IsAddressMatch(Multiaddress addr) => addr.Has(); public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, CancellationToken token) { @@ -90,7 +87,7 @@ public async Task ListenAsync(ITransportContext context, Multiaddress localAddr, { try { - QuicConnection connection = await listener.AcceptConnectionAsync(); + QuicConnection connection = await listener.AcceptConnectionAsync(token); INewConnectionContext clientContext = context.CreateConnection(); _ = ProcessStreams(clientContext, connection, token).ContinueWith(t => clientContext.Dispose()); @@ -130,7 +127,7 @@ public async Task DialAsync(ITransportContext context, Multiaddress remoteAddr, TargetHost = null, ApplicationProtocols = protocols, RemoteCertificateValidationCallback = (_, c, _, _) => VerifyRemoteCertificate(remoteAddr, c), - ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity) }, + ClientCertificates = [CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity)], }, RemoteEndPoint = remoteEndpoint, }; diff --git a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs index 444f9fb8..b6304ffc 100644 --- a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs @@ -81,7 +81,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) ApplicationProtocols = ApplicationProtocols.Value, EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13, RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.State.RemoteAddress, certificate), - ClientCertificates = new X509CertificateCollection { CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity) }, + ClientCertificates = [CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity)], }; _logger?.LogTrace("SslClientAuthenticationOptions initialized for PeerId {RemotePeerId}.", context.State.RemotePeerId); Stream str = new ChannelStream(downChannel); diff --git a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs index 56be74aa..d67f0e76 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux/YamuxProtocol.cs @@ -31,18 +31,20 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext _logger?.LogInformation("Ctx({ctx}): {mode} {peer}", context.Id, isListener ? "Listen" : "Dial", context.State.RemoteAddress); TaskAwaiter downChannelAwaiter = channel.GetAwaiter(); + Dictionary channels = []; try { int streamIdCounter = isListener ? 2 : 1; using INewSessionContext session = context.UpgradeToSession(); - _logger?.LogInformation("Ctx({ctx}): Session created for {peer}", context.Id, context.State.RemoteAddress); + + _logger?.LogInformation("Ctx({ctx}): Session created for {peer}", session.Id, session.State.RemoteAddress); int pingCounter = 0; using Timer timer = new((s) => { - _ = WriteHeaderAsync(context.Id, channel, new YamuxHeader { Type = YamuxHeaderType.Ping, Flags = YamuxHeaderFlags.Syn, Length = ++pingCounter }); + _ = WriteHeaderAsync(session.Id, channel, new YamuxHeader { Type = YamuxHeaderType.Ping, Flags = YamuxHeaderFlags.Syn, Length = ++pingCounter }); }, null, PingDelay, PingDelay); _ = Task.Run(() => @@ -52,14 +54,14 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext int streamId = streamIdCounter; Interlocked.Add(ref streamIdCounter, 2); - _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Dialing with protocol {proto}", context.Id, streamId, request.SelectedProtocol?.Id); - channels[streamId] = CreateUpchannel(context.Id, streamId, YamuxHeaderFlags.Syn, request); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Dialing with protocol {proto}", session.Id, streamId, request.SelectedProtocol?.Id); + channels[streamId] = CreateUpchannel(session.Id, streamId, YamuxHeaderFlags.Syn, request); } }); while (!downChannelAwaiter.IsCompleted) { - YamuxHeader header = await ReadHeaderAsync(context.Id, channel); + YamuxHeader header = await ReadHeaderAsync(session.Id, channel); ReadOnlySequence data = default; if (header.Type > YamuxHeaderType.GoAway) @@ -72,7 +74,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { if ((header.Flags & YamuxHeaderFlags.Syn) == YamuxHeaderFlags.Syn) { - _ = WriteHeaderAsync(context.Id, channel, + _ = WriteHeaderAsync(session.Id, channel, new YamuxHeader { Flags = YamuxHeaderFlags.Ack, @@ -80,14 +82,14 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext Length = header.Length, }); - _logger?.LogDebug("Ctx({ctx}): Ping received and acknowledged", context.Id); + _logger?.LogDebug("Ctx({ctx}): Ping received and acknowledged", session.Id); } continue; } if (header.Type == YamuxHeaderType.GoAway) { - _logger?.LogDebug("Ctx({ctx}): Closing all streams", context.Id); + _logger?.LogDebug("Ctx({ctx}): Closing all streams", session.Id); foreach (ChannelState channelState in channels.Values) { @@ -105,7 +107,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext if ((header.Flags & YamuxHeaderFlags.Syn) == YamuxHeaderFlags.Syn && !channels.ContainsKey(header.StreamID)) { - channels[header.StreamID] = CreateUpchannel(context.Id, header.StreamID, YamuxHeaderFlags.Ack, new UpgradeOptions()); + channels[header.StreamID] = CreateUpchannel(session.Id, header.StreamID, YamuxHeaderFlags.Ack, new UpgradeOptions()); } if (!channels.ContainsKey(header.StreamID)) @@ -114,7 +116,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { await channel.ReadAsync(header.Length); } - _logger?.LogDebug("Ctx({ctx}): Stream {stream id}: Ignored for closed stream", context.Id, header.StreamID); + _logger?.LogDebug("Ctx({ctx}): Stream {stream id}: Ignored for closed stream", session.Id, header.StreamID); continue; } @@ -122,10 +124,10 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { if (header.Length > channels[header.StreamID].LocalWindow.Available) { - _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Data length > windows size: {length} > {window size}", context.Id, + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Data length > windows size: {length} > {window size}", session.Id, header.StreamID, header.Length, channels[header.StreamID].LocalWindow.Available); - await WriteGoAwayAsync(context.Id, channel, SessionTerminationCode.ProtocolError); + await WriteGoAwayAsync(session.Id, channel, SessionTerminationCode.ProtocolError); return; } @@ -134,8 +136,8 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext bool spent = channels[header.StreamID].LocalWindow.SpendWindow((int)data.Length); if (!spent) { - _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Window spent out of budget", context.Id, header.StreamID); - await WriteGoAwayAsync(context.Id, channel, SessionTerminationCode.InternalError); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Window spent out of budget", session.Id, header.StreamID); + await WriteGoAwayAsync(session.Id, channel, SessionTerminationCode.InternalError); return; } @@ -148,7 +150,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext int extendedBy = channels[header.StreamID].LocalWindow.ExtendWindowIfNeeded(); if (extendedBy is not 0) { - _ = WriteHeaderAsync(context.Id, channel, + _ = WriteHeaderAsync(session.Id, channel, new YamuxHeader { Type = YamuxHeaderType.WindowUpdate, @@ -167,7 +169,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext int extendedBy = channelState.LocalWindow.ExtendWindowIfNeeded(); if (extendedBy is not 0) { - _ = WriteHeaderAsync(context.Id, channel, + _ = WriteHeaderAsync(session.Id, channel, new YamuxHeader { Type = YamuxHeaderType.WindowUpdate, @@ -184,7 +186,7 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext { int oldSize = channels[header.StreamID].RemoteWindow.Available; int newSize = channels[header.StreamID].RemoteWindow.ExtendWindow(header.Length); - _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Window update requested: {old} => {new}", context.Id, header.StreamID, oldSize, newSize); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Window update requested: {old} => {new}", session.Id, header.StreamID, oldSize, newSize); } if ((header.Flags & YamuxHeaderFlags.Fin) == YamuxHeaderFlags.Fin) @@ -195,17 +197,17 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext } _ = state.Channel?.WriteEofAsync(); - _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Finish receiving", context.Id, header.StreamID); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Finish receiving", session.Id, header.StreamID); } if ((header.Flags & YamuxHeaderFlags.Rst) == YamuxHeaderFlags.Rst) { _ = channels[header.StreamID].Channel?.CloseAsync(); - _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Reset", context.Id, header.StreamID); + _logger?.LogDebug("Ctx({ctx}), stream {stream id}: Reset", session.Id, header.StreamID); } } - await WriteGoAwayAsync(context.Id, channel, SessionTerminationCode.Ok); + await WriteGoAwayAsync(session.Id, channel, SessionTerminationCode.Ok); ChannelState CreateUpchannel(string contextId, int streamId, YamuxHeaderFlags initiationFlag, UpgradeOptions upgradeOptions) { diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index c5ece94d..7a8f3706 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -70,14 +70,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransportInterop", "..\samp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubPeerDiscovery", "Libp2p.Protocols.PubsubPeerDiscovery\Libp2p.Protocols.PubsubPeerDiscovery.csproj", "{F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubPeerDiscovery.Tests", "Libp2p.Protocols.PubsubPeerDiscovery.Tests\Libp2p.Protocols.PubsubPeerDiscovery.Tests.csproj", "{5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Pubsub.E2eTests", "Libp2p.Protocols.Pubsub.E2eTests\Libp2p.Protocols.Pubsub.E2eTests.csproj", "{BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Tls", "Libp2p.Protocols.Tls\Libp2p.Protocols.Tls.csproj", "{C3CDBAAE-C790-443A-A293-D6E2330160F7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.Protocols.Tls.Tests", "Libp2p.Protocols.Tls.Tests\Libp2p.Protocols.Tls.Tests.csproj", "{89BD907E-1399-4BE7-98CC-E541EAB21842}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.E2eTests", "Libp2p.E2eTests\Libp2p.E2eTests.csproj", "{DBC86C19-3374-4001-AC8A-F672E29CB7B2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.Protocols.PubsubPeerDiscovery.E2eTests", "Libp2p.Protocols.PubsubPeerDiscovery.E2eTests\Libp2p.Protocols.PubsubPeerDiscovery.E2eTests.csproj", "{EC0B1626-C006-4138-A119-FE61CDAB824D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -192,10 +194,6 @@ Global {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}.Debug|Any CPU.Build.0 = Debug|Any CPU {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}.Release|Any CPU.ActiveCfg = Release|Any CPU {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}.Release|Any CPU.Build.0 = Release|Any CPU - {5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5883B53B-2BA5-4444-8E65-DA4B69EB8B2F}.Release|Any CPU.Build.0 = Release|Any CPU {BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -208,6 +206,14 @@ Global {89BD907E-1399-4BE7-98CC-E541EAB21842}.Debug|Any CPU.Build.0 = Debug|Any CPU {89BD907E-1399-4BE7-98CC-E541EAB21842}.Release|Any CPU.ActiveCfg = Release|Any CPU {89BD907E-1399-4BE7-98CC-E541EAB21842}.Release|Any CPU.Build.0 = Release|Any CPU + {DBC86C19-3374-4001-AC8A-F672E29CB7B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBC86C19-3374-4001-AC8A-F672E29CB7B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBC86C19-3374-4001-AC8A-F672E29CB7B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBC86C19-3374-4001-AC8A-F672E29CB7B2}.Release|Any CPU.Build.0 = Release|Any CPU + {EC0B1626-C006-4138-A119-FE61CDAB824D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC0B1626-C006-4138-A119-FE61CDAB824D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC0B1626-C006-4138-A119-FE61CDAB824D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC0B1626-C006-4138-A119-FE61CDAB824D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -233,8 +239,8 @@ Global {D9003366-1562-49CA-B32D-087BBE3973ED} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {EC505F21-FC69-4432-88A8-3CD5F7899B08} = {0DC1C6A1-0A5B-43BA-9605-621C21A16716} {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} - {5883B53B-2BA5-4444-8E65-DA4B69EB8B2F} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} + {EC0B1626-C006-4138-A119-FE61CDAB824D} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E337E37C-3DB8-42FA-9A83-AC4E3B2557B4} diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 392203fe..07f33321 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -2,45 +2,20 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.Logging; -using Multiformats.Address; -using Multiformats.Address.Protocols; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Protocols; namespace Nethermind.Libp2p.Stack; public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, peerStore, loggerFactory) { - public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, peerStore, identity ?? new Identity(), loggerFactory); + public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, PeerStore, identity ?? new Identity(), LoggerFactory); } class Libp2pPeer(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, Identity identity, ILoggerFactory? loggerFactory = null) : LocalPeer(identity, peerStore, protocolStackSettings, loggerFactory) { protected override async Task ConnectedTo(ISession session, bool isDialer) { - await session.DialAsync(); - } - - protected override ProtocolRef SelectProtocol(Multiaddress addr) - { - ArgumentNullException.ThrowIfNull(_protocolStackSettings.TopProtocols); - - ProtocolRef? protocol; - - if (addr.Has()) - { - protocol = _protocolStackSettings.TopProtocols.FirstOrDefault(proto => proto.Protocol.Id == "quic-v1") ?? throw new ApplicationException("QUICv1 is not supported"); - } - else if (addr.Has()) - { - protocol = _protocolStackSettings.TopProtocols!.FirstOrDefault(proto => proto.Protocol.Id == "ip-tcp") ?? throw new ApplicationException("TCP is not supported"); - } - else - { - throw new NotImplementedException($"No transport protocol found for the given address: {addr}"); - } - - return protocol; + //await session.DialAsync(); } } diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index 3f1c0452..f7833eff 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -3,7 +3,6 @@ using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols; -using Nethermind.Libp2p.Protocols.Pubsub; namespace Nethermind.Libp2p.Stack; @@ -13,6 +12,7 @@ public class Libp2pPeerFactoryBuilder(IServiceProvider? serviceProvider = defaul private bool enforcePlaintext; private bool addPubsub; private bool addRelay; + private bool addQuic; public ILibp2pPeerFactoryBuilder WithPlaintextEnforced() { @@ -32,21 +32,22 @@ public ILibp2pPeerFactoryBuilder WithRelay() return this; } + public ILibp2pPeerFactoryBuilder WithQuic() + { + addQuic = true; + return this; + } + protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { ProtocolRef tcp = Get(); - ProtocolRef[] encryption = [enforcePlaintext ? - Get() : - Get()]; + ProtocolRef[] encryption = enforcePlaintext ? [Get()] : [Get(), Get()]; ProtocolRef[] muxers = [Get()]; - ProtocolRef[] commonSelector = [Get()]; - Connect([tcp], [Get()], encryption, [Get()], muxers, commonSelector); - - //ProtocolRef quic = Get(); - //Connect([quic], commonSelector); + ProtocolRef[] commonAppProtocolSelector = [Get()]; + Connect([tcp], [Get()], encryption, [Get()], muxers, commonAppProtocolSelector); ProtocolRef[] relay = addRelay ? [Get(), Get()] : []; ProtocolRef[] pubsub = addPubsub ? [ @@ -63,16 +64,20 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) .. relay, .. pubsub, ]; - Connect(commonSelector, apps); + Connect(commonAppProtocolSelector, apps); if (addRelay) { - ProtocolRef[] relaySelector = [Get()]; - Connect(relay, relaySelector); - Connect(relaySelector, apps.Where(a => !relay.Contains(a)).ToArray()); + Connect(relay, [Get()], apps.Where(a => !relay.Contains(a)).ToArray()); + } + + if (addQuic) + { + ProtocolRef quic = Get(); + Connect([quic], commonAppProtocolSelector); + return [tcp, quic]; } - //return [tcp, quic]; return [tcp]; } } diff --git a/src/samples/perf-benchmarks/PerfBenchmarks.csproj b/src/samples/perf-benchmarks/PerfBenchmarks.csproj index 7a0264b2..e82be68b 100644 --- a/src/samples/perf-benchmarks/PerfBenchmarks.csproj +++ b/src/samples/perf-benchmarks/PerfBenchmarks.csproj @@ -5,7 +5,6 @@ enable enable true - true net8.0 PerfBenchmarks diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 649eabf8..9dc076b2 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -52,12 +52,12 @@ } }; -_ = peer.StartListenAsync([addr], ts.Token); +await peer.StartListenAsync([addr], ts.Token); string peerId = peer.Identity.PeerId.ToString(); _ = serviceProvider.GetService()!.DiscoverAsync(peer.ListenAddresses, token: ts.Token); -_ = router.RunAsync(peer, token: ts.Token); +await router.StartAsync(peer, token: ts.Token); string nickName = "libp2p-dotnet"; diff --git a/src/samples/pubsub-chat/PubsubChat.csproj b/src/samples/pubsub-chat/PubsubChat.csproj index 84c66c97..c2f86896 100644 --- a/src/samples/pubsub-chat/PubsubChat.csproj +++ b/src/samples/pubsub-chat/PubsubChat.csproj @@ -4,7 +4,6 @@ Exe enable enable - true net8.0 PubsubChat diff --git a/src/samples/transport-interop/TransportInterop.csproj b/src/samples/transport-interop/TransportInterop.csproj index 42309408..13deab2a 100644 --- a/src/samples/transport-interop/TransportInterop.csproj +++ b/src/samples/transport-interop/TransportInterop.csproj @@ -8,7 +8,6 @@ enable true true - true diff --git a/src/samples/transport-interop/packages.lock.json b/src/samples/transport-interop/packages.lock.json index 2ee01cdd..77f360a2 100644 --- a/src/samples/transport-interop/packages.lock.json +++ b/src/samples/transport-interop/packages.lock.json @@ -176,8 +176,8 @@ }, "Nethermind.Multiformats.Address": { "type": "Transitive", - "resolved": "1.1.5", - "contentHash": "wm3ooKVG2w0jIuqtHXUPWMck1gQ/DxIFB3RAqxsPIhJesm+dSOUmACkJ4t3GL+VhxtHlYdDVbIKimuKe83ZCGQ==", + "resolved": "1.1.7", + "contentHash": "fp8qIQqHOQVetRgkR8nz047UcTYF1Z8uxSNxkRL6jwYhJI9ETsGcGL4FDZP2U56RyRFiin4k0nwCja0ayrJPYQ==", "dependencies": { "BinaryEncoding": "1.4.0", "Nethermind.Multiformats.Base": "2.0.3-preview.1", @@ -1200,7 +1200,7 @@ "Microsoft.Extensions.DependencyInjection": "[8.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.0, )", "Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )", - "Nethermind.Multiformats.Address": "[1.1.5, )", + "Nethermind.Multiformats.Address": "[1.1.7, )", "SimpleBase": "[4.0.0, )" } }, From ea070a9127e4df8b2cff821beb6a6d70a5144813 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 12 Dec 2024 11:35:30 +0300 Subject: [PATCH 15/25] Fix tests --- .../Libp2p.Core.TestsBase/LocalPeerStub.cs | 2 +- src/libp2p/Libp2p.Core/ChannelFactory.cs | 152 -------- src/libp2p/Libp2p.Core/ChannelRequest.cs | 15 - src/libp2p/Libp2p.Core/ConnectionContext.cs | 46 --- src/libp2p/Libp2p.Core/IChannelRequest.cs | 10 - src/libp2p/Libp2p.Core/PeerContext.cs | 43 --- .../Libp2p.Core/ProtocolStackSettings.cs | 4 - .../MultistreamProtocolTests.cs | 325 +++++++++--------- .../GossipsubProtocolTests.cs | 87 ++--- .../PubsubProtocolTests.cs | 40 ++- .../TlsProtocolTests.cs | 95 ++--- .../Libp2p.Protocols.Tls/TlsProtocol.cs | 138 ++++---- .../YamuxProtocolTests.cs | 65 ++-- src/libp2p/Libp2p/Libp2pLocalPeer.cs | 7 - src/libp2p/Libp2p/Libp2pPeerFactory.cs | 3 +- .../MultiaddressBasedSelectorProtocol.cs | 49 --- .../Libp2p/ServiceProviderExtensions.cs | 12 +- 17 files changed, 396 insertions(+), 697 deletions(-) delete mode 100644 src/libp2p/Libp2p.Core/ChannelFactory.cs delete mode 100644 src/libp2p/Libp2p.Core/ChannelRequest.cs delete mode 100644 src/libp2p/Libp2p.Core/ConnectionContext.cs delete mode 100644 src/libp2p/Libp2p.Core/IChannelRequest.cs delete mode 100644 src/libp2p/Libp2p.Core/PeerContext.cs delete mode 100644 src/libp2p/Libp2p/Libp2pLocalPeer.cs delete mode 100644 src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs diff --git a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs index cb3bb9ed..1154e26d 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs @@ -58,7 +58,7 @@ public TestRemotePeer(Multiaddress addr) public Identity Identity { get; set; } public Multiaddress Address { get; set; } - public Multiaddress RemoteAddress => new(); + public Multiaddress RemoteAddress => $"/p2p/{Identity.PeerId}"; public Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { diff --git a/src/libp2p/Libp2p.Core/ChannelFactory.cs b/src/libp2p/Libp2p.Core/ChannelFactory.cs deleted file mode 100644 index 91a02a28..00000000 --- a/src/libp2p/Libp2p.Core/ChannelFactory.cs +++ /dev/null @@ -1,152 +0,0 @@ -//// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -//// SPDX-License-Identifier: MIT - -//using Microsoft.Extensions.DependencyInjection; -//using Microsoft.Extensions.Logging; -//using Nethermind.Libp2p.Core.Exceptions; -//using Nethermind.Libp2p.Core.Extensions; - -//namespace Nethermind.Libp2p.Core; - -//public class ChannelFactory : IChannelFactory -//{ -// private readonly IServiceProvider _serviceProvider; -// private readonly ILoggerFactory? _loggerFactory; -// private IDictionary _factories; -// private readonly ILogger? _logger; - -// public ChannelFactory(IServiceProvider serviceProvider) -// { -// _serviceProvider = serviceProvider; -// _loggerFactory = _serviceProvider.GetService(); -// _logger = _loggerFactory?.CreateLogger(); -// } - -// public IEnumerable SubProtocols => _factories.Keys; - -// //public IChannel SubDial(IPeerContext context, IChannelRequest? req = null) -// //{ -// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); -// // if (subProtocolId is not IProtocol subProtocol) -// // { -// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); -// // } - -// // Channel channel = new(); -// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - - -// // _ = subProtocol.DialAsync(channel.Reverse, channelFactory, context) -// // .ContinueWith(async task => -// // { -// // if (!task.IsCompletedSuccessfully) -// // { -// // _logger?.DialFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); -// // } -// // await channel.CloseAsync(); - -// // req?.CompletionSource?.SetResult(); -// // }); - -// // return channel; -// //} - -// //public IChannel SubListen(IPeerContext context, IChannelRequest? req = null) -// //{ -// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); -// // if (subProtocolId is not IProtocol subProtocol) -// // { -// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); -// // } - -// // Channel channel = new(); -// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - - -// // _ = subProtocol.ListenAsync(channel.Reverse, channelFactory, context) -// // .ContinueWith(async task => -// // { -// // if (!task.IsCompletedSuccessfully) -// // { -// // _logger?.ListenFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); -// // } -// // await channel.CloseAsync(); - -// // req?.CompletionSource?.SetResult(); -// // }); - -// // return channel; -// //} - -// //public Task SubDialAndBind(IChannel parent, IPeerContext context, -// // IChannelRequest? req = null) -// //{ -// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - -// // if (subProtocolId is not IProtocol subProtocol) -// // { -// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); -// // } - -// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - -// // return subProtocol.DialAsync(((Channel)parent), channelFactory, context) -// // .ContinueWith(async task => -// // { -// // if (!task.IsCompletedSuccessfully) -// // { -// // _logger?.DialAndBindFailed(subProtocol.Id, task.Exception, task.Exception.GetErrorMessage()); -// // } -// // await parent.CloseAsync(); - -// // req?.CompletionSource?.SetResult(); -// // }); -// //} - -// //public Task SubListenAndBind(IChannel parent, IPeerContext context, -// // IChannelRequest? req = null) -// //{ -// // IProtocol? subProtocolId = req?.SubProtocol ?? SubProtocols.FirstOrDefault(); - -// // if (subProtocolId is not IProtocol subProtocol) -// // { -// // throw new Libp2pSetupException($"{nameof(IProtocol)} or {nameof(ITransportProtocol)} should be implemented by {subProtocolId?.GetType()}"); -// // } - -// // ChannelFactory? channelFactory = _factories[subProtocol] as ChannelFactory; - -// // return subProtocol.ListenAsync(((Channel)parent), channelFactory, context) -// // .ContinueWith(async task => -// // { -// // await parent.CloseAsync(); -// // req?.CompletionSource?.SetResult(); -// // }); -// //} - -// public ChannelFactory Setup(IDictionary factories) -// { -// _factories = factories; -// return this; -// } - -// public IChannel SubDial(IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } - -// public IChannel SubListen(IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } - -// public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } - -// public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } -//} diff --git a/src/libp2p/Libp2p.Core/ChannelRequest.cs b/src/libp2p/Libp2p.Core/ChannelRequest.cs deleted file mode 100644 index ea3d3362..00000000 --- a/src/libp2p/Libp2p.Core/ChannelRequest.cs +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core; - -//public class ChannelRequest -//{ -// public IProtocol? SubProtocol { get; init; } -// public TaskCompletionSource? CompletionSource { get; init; } - -// public override string ToString() -// { -// return $"Request for {SubProtocol?.Id ?? "unknown protocol"}"; -// } -//} diff --git a/src/libp2p/Libp2p.Core/ConnectionContext.cs b/src/libp2p/Libp2p.Core/ConnectionContext.cs deleted file mode 100644 index 0bcf4890..00000000 --- a/src/libp2p/Libp2p.Core/ConnectionContext.cs +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core; - -//public class ConnectionContext(LocalPeer localPeer, ITransportProtocol transportProtocol) : ITransportConnectionContext -//{ - -// public CancellationToken Token => throw new NotImplementedException(); - -// public string Id => throw new NotImplementedException(); - -// public IEnumerable SubProtocols => throw new NotImplementedException(); - -// public Identity Identity => throw new NotImplementedException(); - -// public ISessionContext UpgradeToSession() -// { -// throw new NotImplementedException(); -// } - -// public void Dispose() -// { -// throw new NotImplementedException(); -// } - -// public IChannel SubDial(IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } - -// public Task SubDialAndBind(IChannel parentChannel, IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } - -// public IChannel SubListen(IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } - -// public Task SubListenAndBind(IChannel parentChannel, IChannelRequest? request = null) -// { -// throw new NotImplementedException(); -// } -//} diff --git a/src/libp2p/Libp2p.Core/IChannelRequest.cs b/src/libp2p/Libp2p.Core/IChannelRequest.cs deleted file mode 100644 index 251a56bd..00000000 --- a/src/libp2p/Libp2p.Core/IChannelRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core; - -//public interface IChannelRequest -//{ -// IProtocol? SubProtocol { get; } -// public TaskCompletionSource? CompletionSource { get; } -//} diff --git a/src/libp2p/Libp2p.Core/PeerContext.cs b/src/libp2p/Libp2p.Core/PeerContext.cs deleted file mode 100644 index 6462fd0c..00000000 --- a/src/libp2p/Libp2p.Core/PeerContext.cs +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core; - -//public class PeerContext : IPeerContext -//{ - - -// public string Id { get; set; } -// public IPeerInfo LocalPeer { get; set; } -// public IPeerInfo RemotePeer { get; set; } -// public Multiaddress RemoteEndpoint { get; set; } -// public Multiaddress LocalEndpoint { get; set; } -// public BlockingCollection SubDialRequests { get; set; } = new(); -// public IChannelRequest? SpecificProtocolRequest { get; set; } - -// public IPeerContext Fork() -// { -// PeerContext result = (PeerContext)MemberwiseClone(); -// result.RemotePeer = ((PeerFactory.RemotePeer)RemotePeer).Fork(); -// return result; -// } - - - -// public event RemotePeerConnected? OnRemotePeerConnection; -// public void Connected(IPeerInfo peer) -// { -// OnRemotePeerConnection?.Invoke((ISession)peer); -// } - -// public event ListenerReady? OnListenerReady; -// public void ListenerReady() -// { -// OnListenerReady?.Invoke(); -// } - -// public void Dispose() -// { -// throw new NotImplementedException(); -// } -//} diff --git a/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs b/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs index a56bd69a..e1184fc3 100644 --- a/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs +++ b/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs @@ -1,10 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT - -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; diff --git a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs index 5d7c17ed..876a511b 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs @@ -1,176 +1,169 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.TestsBase; +using NSubstitute; + namespace Nethermind.Libp2p.Protocols.Multistream.Tests; [TestFixture] [Parallelizable(scope: ParallelScope.All)] public class MultistreamProtocolTests { - //[Test] - //public async Task Test_ConnectionEstablished_AfterHandshake() - //{ - // IChannel downChannel = new TestChannel(); - // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - // IChannelFactory channelFactory = Substitute.For(); - // IPeerContext peerContext = Substitute.For(); - // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - // IProtocol? proto1 = Substitute.For(); - // proto1.Id.Returns("proto1"); - // channelFactory.SubProtocols.Returns(new[] { proto1 }); - // IChannel upChannel = new TestChannel(); - // channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) - // .Returns(Task.CompletedTask); - - // MultistreamProtocol proto = new(); - // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - // _ = Task.Run(async () => - // { - // await downChannel.WriteLineAsync(proto.Id); - // await downChannel.WriteLineAsync("proto1"); - // }); - - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); - - // await dialTask; - - // _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); - // await downChannel.CloseAsync(); - //} - - //[Test] - //public async Task Test_ConnectionEstablished_AfterHandshake_With_SpecificRequest() - //{ - // IChannel downChannel = new TestChannel(); - // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - // IChannelFactory channelFactory = Substitute.For(); - // IPeerContext peerContext = Substitute.For(); - // IChannelRequest channelRequest = Substitute.For(); - // peerContext.SpecificProtocolRequest.Returns(channelRequest); - - // IProtocol? proto1 = Substitute.For(); - // proto1.Id.Returns("proto1"); - // channelRequest.SubProtocol.Returns(proto1); - // IChannel upChannel = new TestChannel(); - - // channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) - // .Returns(Task.CompletedTask); - - // MultistreamProtocol proto = new(); - // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - // _ = Task.Run(async () => - // { - // await downChannel.WriteLineAsync(proto.Id); - // await downChannel.WriteLineAsync("proto1"); - // }); - - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); - - // await dialTask; - - // _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto1); - // await downChannel.CloseAsync(); - //} - - //[Test] - //public async Task Test_ConnectionClosed_ForUnknownProtocol() - //{ - // IChannel downChannel = new TestChannel(); - // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - // IChannelFactory channelFactory = Substitute.For(); - // IPeerContext peerContext = Substitute.For(); - // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - // IProtocol? proto1 = Substitute.For(); - // proto1.Id.Returns("proto1"); - // channelFactory.SubProtocols.Returns(new[] { proto1 }); - - // MultistreamProtocol proto = new(); - // _ = Task.Run(async () => - // { - // await downChannel.WriteLineAsync(proto.Id); - // await downChannel.WriteLineAsync("proto2"); - // }); - - // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); - - // await dialTask; - - // _ = channelFactory.DidNotReceive().SubDialAndBind(downChannelFromProtocolPov, proto1); - //} - - //[Test] - //public async Task Test_ConnectionEstablished_ForAnyOfProtocols() - //{ - // IChannel downChannel = new TestChannel(); - // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - // IChannelFactory channelFactory = Substitute.For(); - // IPeerContext peerContext = Substitute.For(); - // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - // IProtocol? proto1 = Substitute.For(); - // proto1.Id.Returns("proto1"); - // IProtocol? proto2 = Substitute.For(); - // proto2.Id.Returns("proto2"); - // channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); - // IChannel upChannel = new TestChannel(); - // channelFactory.SubDialAndBind(Arg.Any(), Arg.Any()) - // .Returns(Task.CompletedTask); - - // MultistreamProtocol proto = new(); - // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - // _ = Task.Run(async () => - // { - // await downChannel.WriteLineAsync(proto.Id); - // await downChannel.WriteLineAsync("na"); - // await downChannel.WriteLineAsync("proto2"); - // }); - - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto2.Id)); - - // await dialTask; - - // _ = channelFactory.Received().SubDialAndBind(downChannelFromProtocolPov, proto2); - // await upChannel.CloseAsync(); - //} - - //[Test] - //public async Task Test_ConnectionClosed_ForBadProtocol() - //{ - // IChannel downChannel = new TestChannel(); - // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - // IChannelFactory channelFactory = Substitute.For(); - // IPeerContext peerContext = Substitute.For(); - // peerContext.SpecificProtocolRequest.Returns((IChannelRequest?)null); - - // IProtocol? proto1 = Substitute.For(); - // proto1.Id.Returns("proto1"); - // IProtocol? proto2 = Substitute.For(); - // proto1.Id.Returns("proto2"); - // channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); - - // MultistreamProtocol proto = new(); - // Task dialTask = proto.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - // _ = Task.Run(async () => - // { - // await downChannel.WriteLineAsync(proto.Id); - // await downChannel.WriteLineAsync("na1"); - // await downChannel.WriteLineAsync("proto2"); - // }); - - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); - // Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); - - // await dialTask; - - // _ = channelFactory.DidNotReceiveWithAnyArgs().SubDialAndBind(null!, (IProtocol)null!); - //} + [Test] + public async Task Test_ConnectionEstablished_AfterHandshake() + { + IChannel downChannel = new TestChannel(); + IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + IConnectionContext peerContext = Substitute.For(); + peerContext.UpgradeOptions.Returns(new UpgradeOptions()); + + IProtocol? proto1 = Substitute.For(); + proto1.Id.Returns("proto1"); + peerContext.SubProtocols.Returns([proto1]); + peerContext.Upgrade(Arg.Any(), Arg.Any()).Returns(Task.CompletedTask); + + MultistreamProtocol proto = new(); + Task dialTask = proto.DialAsync(downChannelFromProtocolPov, peerContext); + _ = Task.Run(async () => + { + await downChannel.WriteLineAsync(proto.Id); + await downChannel.WriteLineAsync("proto1"); + }); + + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); + + await dialTask; + + _ = peerContext.Received().Upgrade(downChannelFromProtocolPov, proto1); + await downChannel.CloseAsync(); + } + + [Test] + public async Task Test_ConnectionEstablished_AfterHandshake_With_SpecificRequest() + { + IChannel downChannel = new TestChannel(); + IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + IConnectionContext peerContext = Substitute.For(); + + IProtocol? proto1 = Substitute.For(); + proto1.Id.Returns("proto1"); + peerContext.UpgradeOptions.Returns(new UpgradeOptions()); + + peerContext.Upgrade(Arg.Any(), Arg.Any()).Returns(Task.CompletedTask); + + MultistreamProtocol proto = new(); + Task dialTask = proto.DialAsync(downChannelFromProtocolPov, peerContext); + _ = Task.Run(async () => + { + await downChannel.WriteLineAsync(proto.Id); + await downChannel.WriteLineAsync("proto1"); + }); + + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); + + await dialTask; + + _ = peerContext.Received().Upgrade(downChannelFromProtocolPov, proto1); + await downChannel.CloseAsync(); + } + + [Test] + public async Task Test_ConnectionClosed_ForUnknownProtocol() + { + IChannel downChannel = new TestChannel(); + IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + IConnectionContext peerContext = Substitute.For(); + peerContext.UpgradeOptions.Returns(new UpgradeOptions() { SelectedProtocol = null }); + + IProtocol? proto1 = Substitute.For(); + proto1.Id.Returns("proto1"); + peerContext.SubProtocols.Returns([proto1]); + + MultistreamProtocol proto = new(); + _ = Task.Run(async () => + { + await downChannel.WriteLineAsync(proto.Id); + await downChannel.WriteLineAsync("proto2"); + }); + + Task dialTask = proto.DialAsync(downChannelFromProtocolPov, peerContext); + + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo("proto1")); + + await dialTask; + + _ = peerContext.DidNotReceive().Upgrade(downChannelFromProtocolPov, proto1); + } + + [Test] + public async Task Test_ConnectionEstablished_ForAnyOfProtocols() + { + IChannel downChannel = new TestChannel(); + IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + IConnectionContext peerContext = Substitute.For(); + peerContext.UpgradeOptions.Returns(new UpgradeOptions()); + + IProtocol? proto1 = Substitute.For(); + proto1.Id.Returns("proto1"); + IProtocol? proto2 = Substitute.For(); + proto2.Id.Returns("proto2"); + peerContext.SubProtocols.Returns([proto1, proto2]); + IChannel upChannel = new TestChannel(); + peerContext.Upgrade(Arg.Any(), Arg.Any()) + .Returns(Task.CompletedTask); + + MultistreamProtocol proto = new(); + Task dialTask = proto.DialAsync(downChannelFromProtocolPov, peerContext); + _ = Task.Run(async () => + { + await downChannel.WriteLineAsync(proto.Id); + await downChannel.WriteLineAsync("na"); + await downChannel.WriteLineAsync("proto2"); + }); + + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto2.Id)); + + await dialTask; + + _ = peerContext.Received().Upgrade(downChannelFromProtocolPov, proto2); + await upChannel.CloseAsync(); + } + + [Test] + public async Task Test_ConnectionClosed_ForBadProtocol() + { + IChannel downChannel = new TestChannel(); + IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + IConnectionContext peerContext = Substitute.For(); + peerContext.UpgradeOptions.Returns(new UpgradeOptions()); + + IProtocol? proto1 = Substitute.For(); + proto1.Id.Returns("proto1"); + IProtocol? proto2 = Substitute.For(); + proto1.Id.Returns("proto2"); + peerContext.SubProtocols.Returns([proto1, proto2]); + + MultistreamProtocol proto = new(); + Task dialTask = proto.DialAsync(downChannelFromProtocolPov, peerContext); + _ = Task.Run(async () => + { + await downChannel.WriteLineAsync(proto.Id); + await downChannel.WriteLineAsync("na1"); + await downChannel.WriteLineAsync("proto2"); + }); + + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto.Id)); + Assert.That(await downChannel.ReadLineAsync(), Is.EqualTo(proto1.Id)); + + await dialTask; + + _ = peerContext.DidNotReceiveWithAnyArgs().Upgrade(Arg.Any(), Arg.Any()); + } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs index fb8d0209..c6e61fbe 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs @@ -1,52 +1,53 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Multiformats.Address; +using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Protocols.Pubsub.Dto; + namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; [TestFixture] public class GossipsubProtocolTests { - //[Test] - //public async Task Test_New_messages_are_sent_to_mesh_only() - //{ - // PeerStore peerStore = new(); - // PubsubRouter router = new(peerStore); - // Settings settings = new() { HeartbeatInterval = int.MaxValue }; - // IRoutingStateContainer state = router; - // int peerCount = Settings.Default.HighestDegree + 1; - // const string commonTopic = "topic1"; - - // IPeer peer = new LocalPeerStub(); - // TestDiscoveryProtocol discovery = new(); - // CancellationToken token = default; - // List sentRpcs = []; - - // _ = router.RunAsync(peer, token: token); - // router.GetTopic(commonTopic); - // Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); - // Assert.That(state.GossipsubPeers.Keys, Has.Member(commonTopic)); - - // TaskCompletionSource tcs = new(); - - // foreach (int index in Enumerable.Range(1, peerCount)) - // { - // Multiaddress discoveredPeer = TestPeers.Multiaddr(index); - // PeerId peerId = TestPeers.PeerId(index); - - // peerStore.Discover([discoveredPeer]); - // router.OutboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, sentRpcs.Add); - // router.InboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, tcs.Task, () => Task.CompletedTask); - // await router.OnRpc(peerId, new Rpc().WithTopics([commonTopic], [])); - // } - - // await router.Heartbeat(); - - // Assert.Multiple(() => - // { - // Assert.That(state.GossipsubPeers[commonTopic], Has.Count.EqualTo(peerCount)); - // Assert.That(state.Mesh[commonTopic], Has.Count.EqualTo(Settings.Default.Degree)); - // }); - - // tcs.SetResult(); - //} + [Test] + public async Task Test_New_messages_are_sent_to_mesh_only() + { + PeerStore peerStore = new(); + PubsubRouter router = new(peerStore); + PubsubSettings settings = new() { HeartbeatInterval = int.MaxValue }; + IRoutingStateContainer state = router; + int peerCount = PubsubSettings.Default.HighestDegree + 1; + const string commonTopic = "topic1"; + + IPeer peer = new LocalPeerStub(); + List sentRpcs = []; + + router.GetTopic(commonTopic); + Assert.That(state.FloodsubPeers.Keys, Has.Member(commonTopic)); + Assert.That(state.GossipsubPeers.Keys, Has.Member(commonTopic)); + await router.StartAsync(peer); + TaskCompletionSource tcs = new(); + + foreach (int index in Enumerable.Range(1, peerCount)) + { + Multiaddress discoveredPeer = TestPeers.Multiaddr(index); + PeerId peerId = TestPeers.PeerId(index); + + peerStore.Discover([discoveredPeer]); + router.OutboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, sentRpcs.Add); + router.InboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, tcs.Task, () => Task.CompletedTask); + await router.OnRpc(peerId, new Rpc().WithTopics([commonTopic], [])); + } + + await router.Heartbeat(); + + Assert.Multiple(() => + { + Assert.That(state.GossipsubPeers[commonTopic], Has.Count.EqualTo(peerCount)); + Assert.That(state.Mesh[commonTopic], Has.Count.EqualTo(PubsubSettings.Default.Degree)); + }); + + tcs.SetResult(); + } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs index d72f350a..7160bd1d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs @@ -3,34 +3,38 @@ //namespace Nethermind.Libp2p.Protocols.Pubsub.Tests; +using Multiformats.Address; +using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Protocols.Pubsub; + [TestFixture] public class PubsubProtocolTests { [Test] public async Task Test_Peer_is_dialed_when_added_by_discovery() { - //PeerStore peerStore = new(); - //PubsubRouter router = new(peerStore); - //IRoutingStateContainer state = router; - //Multiaddress discoveredPeerAddr = TestPeers.Multiaddr(1); - //Multiaddress localPeer = TestPeers.Multiaddr(2); + PeerStore peerStore = new(); + PubsubRouter router = new(peerStore); + IRoutingStateContainer state = router; + Multiaddress localPeerAddr = TestPeers.Multiaddr(1); + Multiaddress[] discoveredPeerAddrs = [TestPeers.Multiaddr(2)]; - //ILocalPeer peer = Substitute.For(); - //peer.Address.Returns(localPeer); - //peer.Identity.Returns(TestPeers.Identity(2)); - //peer.DialAsync(discoveredPeerAddr, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddr)); + IPeer peer = Substitute.For(); + peer.ListenAddresses.Returns([localPeerAddr]); + peer.Identity.Returns(TestPeers.Identity(1)); + peer.DialAsync(discoveredPeerAddrs, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddrs[0])); - //CancellationToken token = default; - //TaskCompletionSource taskCompletionSource = new(); + CancellationToken token = default; + TaskCompletionSource taskCompletionSource = new(); - //_ = router.RunAsync(peer, token: token); - //peerStore.Discover([discoveredPeerAddr]); + await router.StartAsync(peer, token: token); + peerStore.Discover(discoveredPeerAddrs); - //await Task.Delay(100); - //_ = peer.Received().DialAsync(discoveredPeerAddr, Arg.Any()); + await Task.Delay(100); + _ = peer.Received().DialAsync(discoveredPeerAddrs, Arg.Any()); - //router.OutboundConnection(discoveredPeerAddr, PubsubRouter.FloodsubProtocolVersion, taskCompletionSource.Task, (rpc) => { }); - //Assert.That(state.ConnectedPeers, Has.Member(discoveredPeerAddr.GetPeerId())); - //taskCompletionSource.SetResult(); + router.OutboundConnection(discoveredPeerAddrs[0], PubsubRouter.FloodsubProtocolVersion, taskCompletionSource.Task, (rpc) => { }); + Assert.That(state.ConnectedPeers, Has.Member(discoveredPeerAddrs[0].GetPeerId())); + taskCompletionSource.SetResult(); } } diff --git a/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs index 3672f3fe..4ee6309b 100644 --- a/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs @@ -1,64 +1,71 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.Logging; +using Multiformats.Address; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.TestsBase; +using NSubstitute; + namespace Nethermind.Libp2p.Protocols.TLS.Tests; [TestFixture] [Parallelizable(scope: ParallelScope.All)] public class TlsProtocolTests { - //[Test] - //[Ignore("Infinite loop")] - //public async Task Test_ConnectionEstablished_AfterHandshake() - //{ - // // Arrange - // IChannel downChannel = new TestChannel(); - // IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); - // IChannelFactory channelFactory = Substitute.For(); - // IPeerContext peerContext = Substitute.For(); - // IPeerContext listenerContext = Substitute.For(); - // ILoggerFactory loggerFactory = Substitute.For(); + [Test] + [Ignore("Infinite loop")] + public async Task Test_ConnectionEstablished_AfterHandshake() + { + // Arrange + IChannel downChannel = new TestChannel(); + IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); + IChannelFactory channelFactory = Substitute.For(); + IConnectionContext listenerContext = Substitute.For(); + ILoggerFactory loggerFactory = Substitute.For(); + + TestChannel upChannel = new(); + channelFactory.Upgrade(Arg.Any()).Returns(upChannel); + + TestChannel listenerUpChannel = new(); + channelFactory.Upgrade(Arg.Any()).Returns(listenerUpChannel); - // TestChannel upChannel = new TestChannel(); - // channelFactory.SubDial(Arg.Any(), Arg.Any()) - // .Returns(upChannel); + IConnectionContext dialerContext = Substitute.For(); + dialerContext.Peer.Identity.Returns(TestPeers.Identity(1)); + dialerContext.Peer.ListenAddresses.Returns([TestPeers.Multiaddr(1)]); + dialerContext.State.Returns(new State()); - // TestChannel listenerUpChannel = new TestChannel(); - // channelFactory.SubListen(Arg.Any(), Arg.Any()) - // .Returns(listenerUpChannel); - // peerContext.LocalPeer.Identity.Returns(new Identity()); - // listenerContext.LocalPeer.Identity.Returns(new Identity()); + listenerContext.Peer.Identity.Returns(TestPeers.Identity(2)); - // string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); - // Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; - // peerContext.LocalPeer.Address.Returns(localAddr); - // listenerContext.RemotePeer.Address.Returns(localAddr); + string peerId = dialerContext.Peer.Identity.PeerId.ToString(); + Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; + //listenerContext.State.RemoteAddress.Returns(localAddr); - // string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); - // Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; - // peerContext.RemotePeer.Address.Returns(listenerAddr); - // listenerContext.LocalPeer.Address.Returns(listenerAddr); + string listenerPeerId = listenerContext.Peer.Identity.PeerId.ToString(); + Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; + //dialerContext.State.RemoteAddress.Returns(listenerAddr); + //listenerContext.State.RemoteAddress.Returns(listenerAddr); - // var i_multiplexerSettings = new MultiplexerSettings(); - // var r_multiplexerSettings = new MultiplexerSettings(); - // TlsProtocol tlsProtocolListener = new TlsProtocol(i_multiplexerSettings, loggerFactory); - // TlsProtocol tlsProtocolInitiator = new TlsProtocol(r_multiplexerSettings, loggerFactory); + MultiplexerSettings i_multiplexerSettings = new(); + MultiplexerSettings r_multiplexerSettings = new(); + TlsProtocol tlsProtocolListener = new(i_multiplexerSettings, loggerFactory); + TlsProtocol tlsProtocolInitiator = new(r_multiplexerSettings, loggerFactory); - // // Act - // Task listenTask = tlsProtocolListener.ListenAsync(downChannel, channelFactory, listenerContext); - // Task dialTask = tlsProtocolInitiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + // Act + Task listenTask = tlsProtocolListener.ListenAsync(downChannel, listenerContext); + Task dialTask = tlsProtocolInitiator.DialAsync(downChannelFromProtocolPov, dialerContext); - // int sent = 42; - // ValueTask writeTask = listenerUpChannel.Reverse().WriteVarintAsync(sent); - // int received = await upChannel.Reverse().ReadVarintAsync(); - // await writeTask; + int sent = 42; + ValueTask writeTask = listenerUpChannel.Reverse().WriteVarintAsync(sent); + int received = await upChannel.Reverse().ReadVarintAsync(); + await writeTask; - // await upChannel.CloseAsync(); - // await listenerUpChannel.CloseAsync(); - // await downChannel.CloseAsync(); + await upChannel.CloseAsync(); + await listenerUpChannel.CloseAsync(); + await downChannel.CloseAsync(); - // // Assert - // Assert.That(received, Is.EqualTo(sent)); - //} + // Assert + Assert.That(received, Is.EqualTo(sent)); + } } diff --git a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs index b6304ffc..8ad134ad 100644 --- a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs @@ -1,5 +1,4 @@ using System.Buffers; -using System.Net; using System.Net.Security; using Nethermind.Libp2p.Protocols.Quic; using System.Security.Cryptography.X509Certificates; @@ -23,38 +22,45 @@ public class TlsProtocol(MultiplexerSettings? multiplexerSettings = null, ILogge public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { - _logger?.LogInformation("Starting ListenAsync: PeerId {LocalPeerId}", context.Peer.Identity.PeerId); + try + { + _logger?.LogInformation("Starting ListenAsync: PeerId {LocalPeerId}", context.Peer.Identity.PeerId); - Stream str = new ChannelStream(downChannel); - X509Certificate certificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity); - _logger?.LogDebug("Successfully created X509Certificate for PeerId {LocalPeerId}. Certificate Subject: {Subject}, Issuer: {Issuer}", context.Peer.Identity.PeerId, certificate.Subject, certificate.Issuer); + Stream str = new ChannelStream(downChannel); + X509Certificate certificate = CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity); + _logger?.LogDebug("Successfully created X509Certificate for PeerId {LocalPeerId}. Certificate Subject: {Subject}, Issuer: {Issuer}", context.Peer.Identity.PeerId, certificate.Subject, certificate.Issuer); - SslServerAuthenticationOptions serverAuthenticationOptions = new() - { - ApplicationProtocols = ApplicationProtocols.Value, - RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.State.RemoteAddress, certificate), - ServerCertificate = certificate, - ClientCertificateRequired = true, - }; - _logger?.LogTrace("SslServerAuthenticationOptions initialized with ApplicationProtocols: {Protocols}.", string.Join(", ", ApplicationProtocols.Value)); - SslStream sslStream = new(str, false, serverAuthenticationOptions.RemoteCertificateValidationCallback); - _logger?.LogTrace("SslStream initialized."); - try - { - await sslStream.AuthenticateAsServerAsync(serverAuthenticationOptions); - _logger?.LogInformation("Server TLS Authentication successful. PeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.State.RemotePeerId, sslStream.NegotiatedApplicationProtocol.Protocol); + SslServerAuthenticationOptions serverAuthenticationOptions = new() + { + ApplicationProtocols = ApplicationProtocols.Value, + RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.State.RemoteAddress, certificate), + ServerCertificate = certificate, + ClientCertificateRequired = true, + }; + _logger?.LogTrace("SslServerAuthenticationOptions initialized with ApplicationProtocols: {Protocols}.", string.Join(", ", ApplicationProtocols.Value)); + SslStream sslStream = new(str, false, serverAuthenticationOptions.RemoteCertificateValidationCallback); + _logger?.LogTrace("SslStream initialized."); + try + { + await sslStream.AuthenticateAsServerAsync(serverAuthenticationOptions); + _logger?.LogInformation("Server TLS Authentication successful. PeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.State.RemotePeerId, sslStream.NegotiatedApplicationProtocol.Protocol); + } + catch (Exception ex) + { + _logger?.LogError("Error during TLS authentication for PeerId {RemotePeerId}: {ErrorMessage}.", context.State.RemotePeerId, ex.Message); + _logger?.LogDebug("TLS Authentication Exception Details: {StackTrace}", ex.StackTrace); + throw; + } + _logger?.LogDebug($"{Encoding.UTF8.GetString(sslStream.NegotiatedApplicationProtocol.Protocol.ToArray())} protocol negotiated"); + IChannel upChannel = context.Upgrade(); + await ExchangeData(sslStream, upChannel, _logger); + _ = upChannel.CloseAsync(); } - catch (Exception ex) + catch (Exception e) { - _logger?.LogError("Error during TLS authentication for PeerId {RemotePeerId}: {ErrorMessage}.", context.State.RemotePeerId, ex.Message); - _logger?.LogDebug("TLS Authentication Exception Details: {StackTrace}", ex.StackTrace); - throw; + } - _logger?.LogDebug($"{Encoding.UTF8.GetString(sslStream.NegotiatedApplicationProtocol.Protocol.ToArray())} protocol negotiated"); - IChannel upChannel = context.Upgrade(); - await ExchangeData(sslStream, upChannel, _logger); - _ = upChannel.CloseAsync(); } private static bool VerifyRemoteCertificate(Multiaddress remotePeerAddress, X509Certificate certificate) => @@ -62,48 +68,54 @@ private static bool VerifyRemoteCertificate(Multiaddress remotePeerAddress, X509 public async Task DialAsync(IChannel downChannel, IConnectionContext context) { - _logger?.LogInformation("Starting DialAsync: LocalPeerId {LocalPeerId}", context.Peer.Identity.PeerId); + try + { + _logger?.LogInformation("Starting DialAsync: LocalPeerId {LocalPeerId}", context.Peer.Identity.PeerId); - // TODO - Multiaddress addr = context.Peer.ListenAddresses.First(); - bool isIP4 = addr.Has(); - MultiaddressProtocol ipProtocol = isIP4 ? addr.Get() : addr.Get(); - IPAddress ipAddress = IPAddress.Parse(ipProtocol.ToString()); + // TODO + Multiaddress addr = context.Peer.ListenAddresses.First(); + bool isIP4 = addr.Has(); + MultiaddressProtocol ipProtocol = isIP4 ? addr.Get() : addr.Get(); - SslClientAuthenticationOptions clientAuthenticationOptions = new() - { - CertificateChainPolicy = new X509ChainPolicy + SslClientAuthenticationOptions clientAuthenticationOptions = new() { - RevocationMode = X509RevocationMode.NoCheck, - VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority - }, - TargetHost = ipAddress.ToString(), - ApplicationProtocols = ApplicationProtocols.Value, - EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13, - RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.State.RemoteAddress, certificate), - ClientCertificates = [CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity)], - }; - _logger?.LogTrace("SslClientAuthenticationOptions initialized for PeerId {RemotePeerId}.", context.State.RemotePeerId); - Stream str = new ChannelStream(downChannel); - SslStream sslStream = new(str, false, clientAuthenticationOptions.RemoteCertificateValidationCallback); - _logger?.LogTrace("Sslstream initialized."); - try - { - await sslStream.AuthenticateAsClientAsync(clientAuthenticationOptions); - _logger?.LogInformation("Client TLS Authentication successful. RemotePeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.State.RemotePeerId, sslStream.NegotiatedApplicationProtocol.Protocol); + CertificateChainPolicy = new X509ChainPolicy + { + RevocationMode = X509RevocationMode.NoCheck, + VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority + }, + TargetHost = ipProtocol?.ToString(), + ApplicationProtocols = ApplicationProtocols.Value, + EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls13, + RemoteCertificateValidationCallback = (_, certificate, _, _) => VerifyRemoteCertificate(context.State.RemoteAddress, certificate), + ClientCertificates = [CertificateHelper.CertificateFromIdentity(_sessionKey, context.Peer.Identity)], + }; + //_logger?.LogTrace("SslClientAuthenticationOptions initialized for PeerId {RemotePeerId}.", context.State.RemotePeerId); + Stream str = new ChannelStream(downChannel); + SslStream sslStream = new(str, false, clientAuthenticationOptions.RemoteCertificateValidationCallback); + _logger?.LogTrace("Sslstream initialized."); + try + { + await sslStream.AuthenticateAsClientAsync(clientAuthenticationOptions); + //_logger?.LogInformation("Client TLS Authentication successful. RemotePeerId: {RemotePeerId}, NegotiatedProtocol: {Protocol}.", context.State.RemotePeerId, sslStream.NegotiatedApplicationProtocol.Protocol); + } + catch (Exception ex) + { + //_logger?.LogError("Error during TLS client authentication for RemotePeerId {RemotePeerId}: {ErrorMessage}.", context.State.RemotePeerId, ex.Message); + _logger?.LogDebug("TLS Authentication Exception Details: {StackTrace}", ex.StackTrace); + return; + } + _logger?.LogDebug("Subdialing protocols: {Protocols}.", string.Join(", ", context.SubProtocols.Select(x => x.Id))); + IChannel upChannel = context.Upgrade(); + _logger?.LogDebug("SubDial completed for PeerId {RemotePeerId}.", context.State.RemotePeerId); + await ExchangeData(sslStream, upChannel, _logger); + _logger?.LogDebug("Connection closed for PeerId {RemotePeerId}.", context.State.RemotePeerId); + _ = upChannel.CloseAsync(); } - catch (Exception ex) + catch (Exception e) { - _logger?.LogError("Error during TLS client authentication for RemotePeerId {RemotePeerId}: {ErrorMessage}.", context.State.RemotePeerId, ex.Message); - _logger?.LogDebug("TLS Authentication Exception Details: {StackTrace}", ex.StackTrace); - return; + } - _logger?.LogDebug("Subdialing protocols: {Protocols}.", string.Join(", ", context.SubProtocols.Select(x => x.Id))); - IChannel upChannel = context.Upgrade(); - _logger?.LogDebug("SubDial completed for PeerId {RemotePeerId}.", context.State.RemotePeerId); - await ExchangeData(sslStream, upChannel, _logger); - _logger?.LogDebug("Connection closed for PeerId {RemotePeerId}.", context.State.RemotePeerId); - _ = upChannel.CloseAsync(); } private static async Task ExchangeData(SslStream sslStream, IChannel upChannel, ILogger? logger) diff --git a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs index cc3a5d1c..44a04098 100644 --- a/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Yamux.Tests/YamuxProtocolTests.cs @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.TestsBase; +using NSubstitute; using NUnit.Framework.Internal; namespace Nethermind.Libp2p.Protocols.Noise.Tests; @@ -18,46 +21,50 @@ public class YamuxProtocolTests // Expect error and react to it [Test] - public async Task Test_Protocol_Communication() + public async Task Test_Protocol_Communication2() { - //IProtocol? proto1 = Substitute.For(); - //proto1.Id.Returns("proto1"); - //IPeerContext dialerPeerContext = Substitute.For(); - //var dialerRequests = new BlockingCollection() { new ChannelRequest() { SubProtocol = proto1 } }; - //dialerPeerContext.SubDialRequests.Returns(dialerRequests); + IProtocol? proto1 = Substitute.For(); + proto1.Id.Returns("proto1"); - //TestChannel dialerDownChannel = new TestChannel(); - //IChannelFactory dialerUpchannelFactory = Substitute.For(); - //dialerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); - //TestChannel dialerUpChannel = new TestChannel(); - //dialerUpchannelFactory.SubDial(Arg.Any()) - // .Returns(dialerUpChannel); + IConnectionContext dialerContext = Substitute.For(); + INewSessionContext dialerSessionContext = Substitute.For(); + dialerContext.UpgradeToSession().Returns(dialerSessionContext); + dialerContext.State.Returns(new State { RemoteAddress = TestPeers.Multiaddr(2) }); + dialerSessionContext.State.Returns(new State { RemoteAddress = TestPeers.Multiaddr(2) }); + dialerSessionContext.Id.Returns("dialer"); + dialerSessionContext.DialRequests.Returns([new UpgradeOptions() { SelectedProtocol = proto1 }]); - //_ = dialerUpChannel.Reverse().WriteLineAsync("hello").AsTask().ContinueWith((e) => dialerUpChannel.CloseAsync()); + TestChannel dialerDownChannel = new(); + dialerSessionContext.SubProtocols.Returns([proto1]); + TestChannel dialerUpChannel = new(); + dialerSessionContext.Upgrade(Arg.Any()).Returns(dialerUpChannel); - //IPeerContext listenerPeerContext = Substitute.For(); - //IChannel listenerDownChannel = dialerDownChannel.Reverse(); - //IChannelFactory listenerUpchannelFactory = Substitute.For(); - //var listenerRequests = new BlockingCollection(); - //listenerPeerContext.SubDialRequests.Returns(listenerRequests); - //listenerUpchannelFactory.SubProtocols.Returns(new[] { proto1 }); - //TestChannel listenerUpChannel = new TestChannel(); - //listenerUpchannelFactory.SubListen(Arg.Any()) - // .Returns(listenerUpChannel); + _ = dialerUpChannel.Reverse().WriteLineAsync("hello").AsTask().ContinueWith((e) => dialerUpChannel.CloseAsync()); - //YamuxProtocol proto = new(loggerFactory: new TestContextLoggerFactory()); + IChannel listenerDownChannel = dialerDownChannel.Reverse(); - //_ = proto.ListenAsync(listenerDownChannel, listenerUpchannelFactory, listenerPeerContext); + IConnectionContext listenerContext = Substitute.For(); + INewSessionContext listenerSessionContext = Substitute.For(); + listenerContext.UpgradeToSession().Returns(listenerSessionContext); + listenerContext.State.Returns(new State { RemoteAddress = TestPeers.Multiaddr(1) }); + listenerSessionContext.State.Returns(new State { RemoteAddress = TestPeers.Multiaddr(1) }); + listenerSessionContext.Id.Returns("listener"); - //_ = proto.DialAsync(dialerDownChannel, dialerUpchannelFactory, dialerPeerContext); + listenerSessionContext.SubProtocols.Returns([proto1]); + TestChannel listenerUpChannel = new(); + listenerSessionContext.Upgrade(Arg.Any()).Returns(listenerUpChannel); + YamuxProtocol proto = new(loggerFactory: new TestContextLoggerFactory()); - //var res = await listenerUpChannel.Reverse().ReadLineAsync(); - //await listenerUpChannel.CloseAsync(); + _ = proto.ListenAsync(listenerDownChannel, listenerContext); - //Assert.That(res, Is.EqualTo("hello")); + _ = proto.DialAsync(dialerDownChannel, dialerContext); - //await Task.Delay(1000); + + string res = await listenerUpChannel.Reverse().ReadLineAsync(); + await listenerUpChannel.CloseAsync(); + + Assert.That(res, Is.EqualTo("hello")); } } diff --git a/src/libp2p/Libp2p/Libp2pLocalPeer.cs b/src/libp2p/Libp2p/Libp2pLocalPeer.cs deleted file mode 100644 index 465f0cc1..00000000 --- a/src/libp2p/Libp2p/Libp2pLocalPeer.cs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p; -//internal class Libp2pLocalPeer: IPeer -//{ -//} diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 07f33321..672fc4bf 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Discovery; +using Nethermind.Libp2p.Protocols; namespace Nethermind.Libp2p.Stack; @@ -16,6 +17,6 @@ class Libp2pPeer(IProtocolStackSettings protocolStackSettings, PeerStore peerSto { protected override async Task ConnectedTo(ISession session, bool isDialer) { - //await session.DialAsync(); + await session.DialAsync(); } } diff --git a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs b/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs deleted file mode 100644 index 8cd6984b..00000000 --- a/src/libp2p/Libp2p/MultiaddressBasedSelectorProtocol.cs +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Protocols; - -/// -/// Select protocol based on multiaddr -/// -//public class MultiaddressBasedSelector(ILoggerFactory? loggerFactory = null): ITransportProtocol -//{ -// private readonly ILogger? _logger = loggerFactory?.CreateLogger(); - -// public string Id => "multiaddr-select"; - -// public Task DialAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) -// { -// return ConnectAsync(context, listenAddr, token, false); -// } - -// public Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) -// { -// return ConnectAsync(context, listenAddr, token, true); -// } - -// protected async Task ConnectAsync(ITransportContext context, Multiaddress addr, CancellationToken token, bool isListener) -// { -// throw new NotImplementedException(); -// //ITransportProtocol protocol = null!; - -// //if (addr.Has()) -// //{ -// // protocol = context!.SubProtocols.FirstOrDefault(proto => proto.Id == "quic-v1") as ITransportProtocol ?? throw new ApplicationException("QUICv1 is not supported"); -// //} -// //else if (addr.Has()) -// //{ -// // protocol = channelFactory!.SubProtocols.FirstOrDefault(proto => proto.Id == "ip-tcp") as ITransportProtocol ?? throw new ApplicationException("TCP is not supported"); -// //} -// //else -// //{ -// // throw new NotImplementedException($"No transport protocol found for the given address: {addr}"); -// //} - -// //_logger?.LogPickedProtocol(protocol.Id, isListener ? "listen" : "dial"); - -// //await (isListener -// // ? protocol.ListenAsync(context, addr, token) -// // : protocol.DialAsync(context, addr, token)); -// } -//} diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index 8109cb72..7dbd6794 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -15,14 +15,14 @@ public static IServiceCollection AddLibp2p(this IServiceCollection services, Fun { return services .AddSingleton(sp => new Libp2pPeerFactoryBuilder(sp)) + .AddSingleton(sp => (ILibp2pPeerFactoryBuilder)sp.GetRequiredService()) .AddSingleton() .AddSingleton(sp => factorySetup is null ? sp.GetRequiredService() : factorySetup(sp.GetRequiredService())) - .AddSingleton(sp => (ILibp2pPeerFactoryBuilder)sp.GetRequiredService()) - .AddScoped(sp => sp.GetService()!.Build()) - .AddScoped() - .AddScoped() - .AddScoped() - .AddScoped() + .AddSingleton(sp => sp.GetService()!.Build()) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() ; } } From 35154d8bcacfa23c3e784c3801db8e0672202166 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 12 Dec 2024 12:08:09 +0300 Subject: [PATCH 16/25] Adjust namespaces --- README.md | 8 ++++---- src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 1 - src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs | 1 - src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs | 6 ++++-- src/libp2p/Libp2p.Core/Peer.cs | 1 - src/libp2p/Libp2p.Core/PeerFactory.cs | 1 - src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs | 1 - src/libp2p/Libp2p.Core/ProtocolStackSettings.cs | 2 -- src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs | 2 +- src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs | 1 - src/libp2p/Libp2p.Protocols.Noise/README.md | 2 +- src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs | 2 +- src/libp2p/Libp2p.sln | 2 ++ src/libp2p/Libp2p/Libp2pPeerFactory.cs | 2 +- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 4 ++-- src/libp2p/Libp2p/LogMessages.cs | 2 +- src/libp2p/Libp2p/ServiceProviderExtensions.cs | 2 +- src/samples/chat/Program.cs | 2 +- src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs | 2 +- src/samples/perf-benchmarks/Program.cs | 2 +- src/samples/pubsub-chat/Program.cs | 2 +- 21 files changed, 22 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index cac0d299..e8d2b180 100644 --- a/README.md +++ b/README.md @@ -40,14 +40,14 @@ The target is to provide a performant well-tested implementation of a wide range | Protocol | Version | Status | |--------------------|--------------------|-----------------| | TCP | tcp | ✅ | -| QUIC | quic-v1 | ✅ | +| QUIC | quic-v1 | 🚧 | | multistream-select | /multistream/1.0.0 | ✅ | | plaintext | /plaintext/2.0.0 | ✅ | | noise | /noise | ✅ | | tls | /tls/1.0.0 | 🚧 | | WebTransport | | ⬜ help wanted | | yamux | /yamux/1.0.0 | ✅ | -| Circuit Relay | /libp2p/circuit/relay/0.2.0/* | ⬜ help wanted | +| Circuit Relay | /libp2p/circuit/relay/0.2.0/* | 🚧 | | hole punching | | ⬜ help wanted | | **Application layer** | Identify | /ipfs/id/1.0.0 | ✅ | @@ -55,11 +55,11 @@ The target is to provide a performant well-tested implementation of a wide range | pubsub | /floodsub/1.0.0 | ✅ | | | /meshsub/1.0.0 | ✅ | | | /meshsub/1.1.0 | 🚧 | -| | /meshsub/1.2.0 | ⬜ | +| | /meshsub/1.2.0 | 🚧 | | **Discovery** | mDns | basic | ✅ | | | DNS-SD | 🚧 | -| [discv5](https://github.com/Pier-Two/Lantern.Discv5) | 5.1 | 🚧 help wanted | +| [discv5](https://github.com/Pier-Two/Lantern.Discv5) (wrapper) | 5.1 | 🚧 help wanted | ⬜ - not yet implemented
🚧 - work in progress
diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index 5a499280..13ea2dbb 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols; -using Nethermind.Libp2p.Stack; using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core.TestsBase.E2e; diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs index 2e2ae85e..cb5dd3af 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection; using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Stack; using NUnit.Framework; namespace Nethermind.Libp2p.Core.TestsBase.E2e; diff --git a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs index f6a1cfe7..fc5011e8 100644 --- a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs +++ b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs @@ -1,9 +1,11 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Nethermind.Libp2p.Core; -namespace Nethermind.Libp2p.Stack; +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Core; public interface IProtocolStackSettings { diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index c44016a0..dd9bd4ed 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -7,7 +7,6 @@ using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Core.Extensions; -using Nethermind.Libp2p.Stack; using System.Collections.Concurrent; using System.Collections.ObjectModel; diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index bc554ba7..66bed12d 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core.Discovery; -using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index 86800156..b7dda9b5 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: MIT using Microsoft.Extensions.DependencyInjection; -using Nethermind.Libp2p.Stack; namespace Nethermind.Libp2p.Core; diff --git a/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs b/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs index e1184fc3..f3cc0da2 100644 --- a/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs +++ b/src/libp2p/Libp2p.Core/ProtocolStackSettings.cs @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT -using Nethermind.Libp2p.Stack; - namespace Nethermind.Libp2p.Core; public class ProtocolStackSettings : IProtocolStackSettings diff --git a/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs b/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs index 3c8e571c..957bd966 100644 --- a/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs +++ b/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs @@ -1,9 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Nethermind.Libp2p; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.TestsBase; -using Nethermind.Libp2p.Stack; using System.Text; namespace Libp2p.E2eTests; diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index 353dccbd..a3bb48b5 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -6,7 +6,6 @@ using Multiformats.Address.Net; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols.Identify; -using Nethermind.Libp2p.Stack; using System.Net.Sockets; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.Dto; diff --git a/src/libp2p/Libp2p.Protocols.Noise/README.md b/src/libp2p/Libp2p.Protocols.Noise/README.md index ea8941e7..5deb2726 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/README.md +++ b/src/libp2p/Libp2p.Protocols.Noise/README.md @@ -1,4 +1,4 @@ -# Noise protocol +# TLS protocol - [libp2p spec](https://github.com/libp2p/specs/blob/master/noise) - [The Noise Protocol Framework](https://www.noiseprotocol.org/noise.html) diff --git a/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs index 4ee6309b..d3c08ec8 100644 --- a/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Tls.Tests/TlsProtocolTests.cs @@ -14,7 +14,7 @@ namespace Nethermind.Libp2p.Protocols.TLS.Tests; public class TlsProtocolTests { [Test] - [Ignore("Infinite loop")] + [Ignore("Needs a fix on Windows")] public async Task Test_ConnectionEstablished_AfterHandshake() { // Arrange diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index 7a8f3706..a1da66b1 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -240,6 +240,8 @@ Global {EC505F21-FC69-4432-88A8-3CD5F7899B08} = {0DC1C6A1-0A5B-43BA-9605-621C21A16716} {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} + {C3CDBAAE-C790-443A-A293-D6E2330160F7} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} + {89BD907E-1399-4BE7-98CC-E541EAB21842} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {EC0B1626-C006-4138-A119-FE61CDAB824D} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 672fc4bf..644dc068 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -6,7 +6,7 @@ using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Protocols; -namespace Nethermind.Libp2p.Stack; +namespace Nethermind.Libp2p; public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, peerStore, loggerFactory) { diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index f7833eff..56c665e3 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -4,7 +4,7 @@ using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols; -namespace Nethermind.Libp2p.Stack; +namespace Nethermind.Libp2p; public class Libp2pPeerFactoryBuilder(IServiceProvider? serviceProvider = default) : PeerFactoryBuilderBase(serviceProvider), ILibp2pPeerFactoryBuilder @@ -42,7 +42,7 @@ protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) { ProtocolRef tcp = Get(); - ProtocolRef[] encryption = enforcePlaintext ? [Get()] : [Get(), Get()]; + ProtocolRef[] encryption = enforcePlaintext ? [Get()] : [Get()/*, Get()*/]; ProtocolRef[] muxers = [Get()]; diff --git a/src/libp2p/Libp2p/LogMessages.cs b/src/libp2p/Libp2p/LogMessages.cs index 01b41117..17777302 100644 --- a/src/libp2p/Libp2p/LogMessages.cs +++ b/src/libp2p/Libp2p/LogMessages.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.Logging; -namespace Nethermind.Libp2p.Stack; +namespace Nethermind.Libp2p; internal static partial class LogMessages { diff --git a/src/libp2p/Libp2p/ServiceProviderExtensions.cs b/src/libp2p/Libp2p/ServiceProviderExtensions.cs index 7dbd6794..3f70a92f 100644 --- a/src/libp2p/Libp2p/ServiceProviderExtensions.cs +++ b/src/libp2p/Libp2p/ServiceProviderExtensions.cs @@ -7,7 +7,7 @@ using Nethermind.Libp2p.Protocols; using Nethermind.Libp2p.Protocols.Pubsub; -namespace Nethermind.Libp2p.Stack; +namespace Nethermind.Libp2p; public static class ServiceProviderExtensions { diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index ca871f4b..e9a7353d 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -3,10 +3,10 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Nethermind.Libp2p.Stack; using Nethermind.Libp2p.Core; using Multiformats.Address; using Multiformats.Address.Protocols; +using Nethermind.Libp2p; ServiceProvider serviceProvider = new ServiceCollection() .AddLibp2p(builder => builder.AddAppLayerProtocol()) diff --git a/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs b/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs index dee72361..fe90774c 100644 --- a/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs +++ b/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText:2023 Demerzel Solutions Limited // SPDX-License-Identifier:MIT -using Nethermind.Libp2p.Stack; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols; +using Nethermind.Libp2p; namespace DataTransferBenchmark; diff --git a/src/samples/perf-benchmarks/Program.cs b/src/samples/perf-benchmarks/Program.cs index 0d779128..c5a0a335 100644 --- a/src/samples/perf-benchmarks/Program.cs +++ b/src/samples/perf-benchmarks/Program.cs @@ -4,9 +4,9 @@ using System.Diagnostics; using DataTransferBenchmark; using Microsoft.Extensions.DependencyInjection; -using Nethermind.Libp2p.Stack; using Nethermind.Libp2p.Core; using Multiformats.Address; +using Nethermind.Libp2p; await Task.Delay(1000); { diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 9dc076b2..4daa6dea 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -2,13 +2,13 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Nethermind.Libp2p.Stack; using Nethermind.Libp2p.Core; using System.Text; using System.Text.Json; using Nethermind.Libp2p.Protocols.Pubsub; using Nethermind.Libp2p.Protocols; using System.Text.RegularExpressions; +using Nethermind.Libp2p; Regex omittedLogs = new(".*(MDnsDiscoveryProtocol|IpTcpProtocol).*"); From 3e454fa36da9f81942381c45f09d962e1b258d95 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 12 Dec 2024 18:55:28 +0300 Subject: [PATCH 17/25] Fix signined records --- src/libp2p/Libp2p.Core/Dto/SigningHelper.cs | 58 ++++++-- .../Libp2p.Core/Exceptions/Libp2pException.cs | 3 + src/libp2p/Libp2p.Core/Identity.cs | 8 +- src/libp2p/Libp2p.Core/Peer.cs | 10 +- .../Libp2p.Core/PeerConnectionException.cs | 9 -- .../IdentifyProtocol.cs | 13 +- .../MultistreamProtocol.cs | 129 ++++++++++++++++++ .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 8 +- .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 1 - .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 2 +- src/samples/chat/Program.cs | 2 +- .../chat/Properties/launchSettings.json | 2 +- 12 files changed, 203 insertions(+), 42 deletions(-) delete mode 100644 src/libp2p/Libp2p.Core/PeerConnectionException.cs diff --git a/src/libp2p/Libp2p.Core/Dto/SigningHelper.cs b/src/libp2p/Libp2p.Core/Dto/SigningHelper.cs index 596b6714..e11c6b0b 100644 --- a/src/libp2p/Libp2p.Core/Dto/SigningHelper.cs +++ b/src/libp2p/Libp2p.Core/Dto/SigningHelper.cs @@ -8,8 +8,8 @@ namespace Nethermind.Libp2p.Core.Dto; public static class SigningHelper { - private static readonly byte[] Libp2pPeerRecordAsArray = [((ushort)Enums.Libp2p.Libp2pPeerRecord >> 8) & 0xFF, (ushort)Enums.Libp2p.Libp2pPeerRecord & 0xFF]; - + private static readonly byte[] PayloadType = [((ushort)Enums.Libp2p.Libp2pPeerRecord >> 8) & 0xFF, (ushort)Enums.Libp2p.Libp2pPeerRecord & 0xFF]; + private static readonly byte[] Domain = "libp2p-peer-record"u8.ToArray().ToArray(); public static bool VerifyPeerRecord(ByteString signedEnvelopeBytes, PublicKey publicKey) { SignedEnvelope signedEnvelope = SignedEnvelope.Parser.ParseFrom(signedEnvelopeBytes); @@ -20,7 +20,7 @@ public static bool VerifyPeerRecord(SignedEnvelope signedEnvelope, PublicKey pub { Identity identity = new(publicKey); - if (signedEnvelope.PayloadType?.Take(2).SequenceEqual(Libp2pPeerRecordAsArray) is not true) + if (signedEnvelope.PayloadType?.Take(2).SequenceEqual(PayloadType) is not true) { return false; } @@ -32,15 +32,30 @@ public static bool VerifyPeerRecord(SignedEnvelope signedEnvelope, PublicKey pub return false; } - SignedEnvelope envelopeWithoutSignature = signedEnvelope.Clone(); - envelopeWithoutSignature.ClearSignature(); + byte[] signedData = new byte[ + VarInt.GetSizeInBytes(Domain.Length) + Domain.Length + + VarInt.GetSizeInBytes(PayloadType.Length) + PayloadType.Length + + VarInt.GetSizeInBytes(signedEnvelope.Payload.Length) + signedEnvelope.Payload.Length]; + + int offset = 0; + + VarInt.Encode(Domain.Length, signedData.AsSpan(), ref offset); + Array.Copy(Domain, 0, signedData, offset, Domain.Length); + offset += Domain.Length; - return identity.VerifySignature(envelopeWithoutSignature.ToByteArray(), signedEnvelope.Signature.ToByteArray()); + VarInt.Encode(PayloadType.Length, signedData.AsSpan(), ref offset); + Array.Copy(PayloadType, 0, signedData, offset, PayloadType.Length); + offset += PayloadType.Length; + + VarInt.Encode(signedEnvelope.Payload.Length, signedData.AsSpan(), ref offset); + Array.Copy(signedEnvelope.Payload.ToByteArray(), 0, signedData, offset, signedEnvelope.Payload.Length); + + return identity.VerifySignature(signedData, signedEnvelope.Signature.ToByteArray()); } public static ByteString CreateSignedEnvelope(Identity identity, Multiaddress[] addresses, ulong seq) { - PeerRecord paylaod = new() + PeerRecord payload = new() { PeerId = ByteString.CopyFrom(identity.PeerId.Bytes), Seq = seq @@ -48,7 +63,7 @@ public static ByteString CreateSignedEnvelope(Identity identity, Multiaddress[] foreach (Multiaddress address in addresses) { - paylaod.Addresses.Add(new AddressInfo + payload.Addresses.Add(new AddressInfo { Multiaddr = ByteString.CopyFrom(address.ToBytes()) }); @@ -56,12 +71,33 @@ public static ByteString CreateSignedEnvelope(Identity identity, Multiaddress[] SignedEnvelope envelope = new() { - PayloadType = ByteString.CopyFrom(Libp2pPeerRecordAsArray), - Payload = paylaod.ToByteString(), + PayloadType = ByteString.CopyFrom(PayloadType), + Payload = payload.ToByteString(), PublicKey = identity.PublicKey.ToByteString(), }; - envelope.Signature = ByteString.CopyFrom(identity.Sign(envelope.ToByteArray())); + int payloadLength = payload.CalculateSize(); + + byte[] signingData = new byte[ + VarInt.GetSizeInBytes(Domain.Length) + Domain.Length + + VarInt.GetSizeInBytes(PayloadType.Length) + PayloadType.Length + + VarInt.GetSizeInBytes(payloadLength) + payloadLength]; + + int offset = 0; + + VarInt.Encode(Domain.Length, signingData.AsSpan(), ref offset); + Array.Copy(Domain, 0, signingData, offset, Domain.Length); + offset += Domain.Length; + + VarInt.Encode(PayloadType.Length, signingData.AsSpan(), ref offset); + Array.Copy(PayloadType, 0, signingData, offset, PayloadType.Length); + offset += PayloadType.Length; + + VarInt.Encode(payloadLength, signingData.AsSpan(), ref offset); + Array.Copy(payload.ToByteArray(), 0, signingData, offset, payloadLength); + + envelope.Signature = ByteString.CopyFrom(identity.Sign(signingData).ToArray()); + return envelope.ToByteString(); } } diff --git a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs index 92cd6625..ccd6c7d4 100644 --- a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs +++ b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs @@ -27,3 +27,6 @@ public class Libp2pSetupException(string? message = null) : Libp2pException(mess /// Appears when there is already active session for the given peer ///
public class SessionExistsException(PeerId remotePeerId) : Libp2pException($"Session is already established with {remotePeerId}"); + + +public class PeerConnectionException : Libp2pException; diff --git a/src/libp2p/Libp2p.Core/Identity.cs b/src/libp2p/Libp2p.Core/Identity.cs index 212b7a5b..287ad3e8 100644 --- a/src/libp2p/Libp2p.Core/Identity.cs +++ b/src/libp2p/Libp2p.Core/Identity.cs @@ -149,10 +149,7 @@ private static PublicKey GetPublicKey(PrivateKey privateKey) public bool VerifySignature(byte[] message, byte[] signature) { - if (PublicKey is null) - { - throw new ArgumentNullException(nameof(PublicKey)); - } + ArgumentNullException.ThrowIfNull(PublicKey); switch (PublicKey.Type) { @@ -164,6 +161,7 @@ public bool VerifySignature(byte[] message, byte[] signature) { using RSA rsa = RSA.Create(); rsa.ImportSubjectPublicKeyInfo(PublicKey.Data.Span, out _); + return rsa.VerifyData(message, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } case KeyType.Secp256K1: @@ -219,7 +217,7 @@ public byte[] Sign(byte[] message) { using RSA rsa = RSA.Create(); rsa.ImportRSAPrivateKey(PrivateKey.Data.Span, out _); - return rsa.SignData(message, 0, message.Length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + return rsa.SignData(message, 0, message.Length, HashAlgorithmName.SHA3_256, RSASignaturePadding.Pkcs1); } case KeyType.Secp256K1: { diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index dd9bd4ed..60a30e29 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -52,15 +52,15 @@ public class Session(LocalPeer peer) : ISession public async Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol { - TaskCompletionSource tcs = new(); - SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance() }, token); + TaskCompletionSource tcs = new(); + SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs!, SelectedProtocol = peer.GetProtocolInstance() }, token); await tcs.Task; MarkAsConnected(); } public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = default) { - TaskCompletionSource tcs = new(); + TaskCompletionSource tcs = new(); SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = protocol }, token); await tcs.Task; MarkAsConnected(); @@ -68,7 +68,7 @@ public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = public async Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol { - TaskCompletionSource tcs = new(); + TaskCompletionSource tcs = new(); SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance(), Argument = request }, token); await tcs.Task; MarkAsConnected(); @@ -247,6 +247,8 @@ internal IEnumerable GetProtocolsFor(ProtocolRef protocol) return _protocolStackSettings.Protocols[protocol].Select(p => p.Protocol); } + + // TODO: Remove lloking in the entire stack, look only on level for the given parent protocol internal IProtocol? GetProtocolInstance() { return _protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.Protocol.GetType() == typeof(TProtocol))?.Protocol; diff --git a/src/libp2p/Libp2p.Core/PeerConnectionException.cs b/src/libp2p/Libp2p.Core/PeerConnectionException.cs deleted file mode 100644 index f16439ba..00000000 --- a/src/libp2p/Libp2p.Core/PeerConnectionException.cs +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-FileCopyrightText:2023 Demerzel Solutions Limited -// SPDX-License-Identifier:MIT - -namespace Nethermind.Libp2p.Core; - -public class PeerConnectionException : Exception -{ - -} diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index a3bb48b5..b2ea9fe8 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -9,6 +9,7 @@ using System.Net.Sockets; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.Dto; +using Nethermind.Libp2p.Core.Exceptions; namespace Nethermind.Libp2p.Protocols; @@ -57,12 +58,14 @@ public async Task DialAsync(IChannel channel, ISessionContext context) { if (!SigningHelper.VerifyPeerRecord(identify.SignedPeerRecord, context.State.RemotePublicKey)) { - throw new PeerConnectionException(); + //throw new PeerConnectionException(); + _logger?.LogError("Peer record is not valid: {peerId}", context.State.RemotePeerId); + } + else + { + _peerStore.GetPeerInfo(context.State.RemotePeerId).SignedPeerRecord = identify.SignedPeerRecord; + _logger?.LogInformation("Confirmed peer record: {peerId}", context.State.RemotePeerId); } - - _peerStore.GetPeerInfo(context.State.RemotePeerId).SignedPeerRecord = identify.SignedPeerRecord; - - _logger?.LogInformation("Confirmed peer record: {peerId}", context.State.RemotePeerId); } } diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index 1cbd0006..16529cf5 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -22,11 +22,140 @@ public MultistreamProtocol(ILoggerFactory? loggerFactory = null) public async Task DialAsync(IChannel channel, IConnectionContext context) { + _logger?.LogTrace($"Hello started"); + + if (!await SendHello(channel)) + { + await channel.CloseAsync(); + _logger?.LogTrace($"Hello failed"); + return; + } + _logger?.LogTrace($"Hello passed"); + + async Task DialProtocol(IProtocol selector) + { + await channel.WriteLineAsync(selector.Id); + string selectorLine = await channel.ReadLineAsync(); + _logger?.LogTrace($"Proposed {selector.Id}, answer: {selectorLine}"); + if (selectorLine == selector.Id) + { + return true; + } + + if (selectorLine != ProtocolNotSupported) + { + return false; + } + + return null; + } + + IProtocol? selected = null; + + if (context.UpgradeOptions?.SelectedProtocol is not null) + { + _logger?.LogDebug($"Proposing just {context.UpgradeOptions.SelectedProtocol}"); + if (await DialProtocol(context.UpgradeOptions.SelectedProtocol) == true) + { + selected = context.UpgradeOptions.SelectedProtocol; + } + } + else + { + foreach (IProtocol selector in context!.SubProtocols) + { + bool? dialResult = await DialProtocol(selector); + if (dialResult == true) + { + selected = selector; + break; + } + else if (dialResult == false) + { + break; + } + } + } + + if (selected is null) + { + _logger?.LogDebug($"Negotiation failed"); + return; + } + _logger?.LogDebug($"Protocol selected during dialing: {selected.Id}"); + await context.Upgrade(channel, selected); + } + + public async Task ListenAsync(IChannel channel, IConnectionContext context) + { + if (!await SendHello(channel)) + { + await channel.CloseAsync(); + return; + } + + IProtocol? selected = null; + for (; ; ) + { + string proto = await channel.ReadLineAsync(); + selected = context.SubProtocols.FirstOrDefault(x => x.Id == proto) as IProtocol; + if (selected is not null) + { + await channel.WriteLineAsync(selected.Id); + _logger?.LogTrace($"Proposed by remote {proto}, answer: {selected?.Id}"); + break; + } + + _logger?.LogTrace($"Proposed by remote {proto}, answer: {ProtocolNotSupported}"); + await channel.WriteLineAsync(ProtocolNotSupported); + } + + if (selected is null) + { + _logger?.LogDebug($"Negotiation failed"); + return; + } + + _logger?.LogDebug($"Protocol selected during listening: {selected}"); + await context.Upgrade(channel, selected); + } + + private async Task SendHello(IChannel channel) + { + await channel.WriteLineAsync(Id); + string line = await channel.ReadLineAsync(); + return line == Id; + } +} + + + +public class MultistreamProtocol2 : IConnectionProtocol +{ + private readonly ILogger? _logger; + private const string ProtocolNotSupported = "na"; + public string Id => "/multistream/1.0.0"; + + public MultistreamProtocol2(ILoggerFactory? loggerFactory = null) + { + _logger = loggerFactory?.CreateLogger(); + } + + public async Task DialAsync(IChannel channel, IConnectionContext context) + { + await foreach (var item in channel.ReadAllAsync()) + { + _logger?.LogTrace(item.ToString()); + } + _logger?.LogTrace($"Hello started"); + if (!await SendHello(channel)) { await channel.CloseAsync(); + _logger?.LogTrace($"Hello failed"); return; } + _logger?.LogTrace($"Hello passed"); async Task DialProtocol(IProtocol selector) { diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index 99eddb0a..223c85b0 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -28,10 +28,10 @@ public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILog private readonly ILogger? _logger = loggerFactory?.CreateLogger(); private NoiseExtensions _extensions => new() { - StreamMuxers = - { - multiplexerSettings is null || !multiplexerSettings.Multiplexers.Any() ? ["na"] : [.. multiplexerSettings.Multiplexers.Select(proto => proto.Id)] - } + StreamMuxers = { } // TODO: return the following after go question resolution: + //{ + // multiplexerSettings is null || !multiplexerSettings.Multiplexers.Any() ? ["na"] : [.. multiplexerSettings.Multiplexers.Select(proto => proto.Id)] + //} }; public string Id => "/noise"; diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 0e50e148..9b21ed22 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -54,7 +54,6 @@ public async Task DialAsync(IChannel channel, ISessionContext context) await channel; dialTcs.SetResult(); _logger?.LogDebug($"Finished dial({context.Id}) {context.State.RemoteAddress}"); - } public async Task ListenAsync(IChannel channel, ISessionContext context) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 7ddfc52d..7e91c9b3 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -207,7 +207,7 @@ public async Task StartAsync(IPeer localPeer, CancellationToken token = default) if (!peerState.ContainsKey(session.RemoteAddress.Get().ToString())) { - await session.DialAsync(token); + await session.DialAsync(token); if (peerState.TryGetValue(session.RemoteAddress.GetPeerId()!, out PubsubPeer? state) && state.InititatedBy == ConnectionInitiation.Remote) { _ = session.DisconnectAsync(); diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index e9a7353d..9512e337 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -11,7 +11,7 @@ ServiceProvider serviceProvider = new ServiceCollection() .AddLibp2p(builder => builder.AddAppLayerProtocol()) .AddLogging(builder => - builder.SetMinimumLevel(args.Contains("--trace") ? LogLevel.Trace : LogLevel.Information) + builder.SetMinimumLevel(args.Contains("--trace") ? LogLevel.Trace : LogLevel.Trace) .AddSimpleConsole(l => { l.SingleLine = true; diff --git a/src/samples/chat/Properties/launchSettings.json b/src/samples/chat/Properties/launchSettings.json index 9f74c6ad..bb7462a9 100644 --- a/src/samples/chat/Properties/launchSettings.json +++ b/src/samples/chat/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Chat": { "commandName": "Project", - "commandLineArgs": "-d /ip4/127.0.0.1/tcp/9001/p2p/QmcJcjTEp33VYj4FjsyM9G7NTYW76HCrTvKWqoJmzXucJk --trace" + "commandLineArgs": "-d /ip4/127.0.0.1/tcp/9001/p2p/QmbbX5NgFcQgEWuHiQHKob1oQyhAUNncYpT3D9ZSkBQyFx --trace " }, "Chat server": { "commandName": "Project", From e7e7ad5d44f1feae0aa2034282b0d53d881075f8 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 12 Dec 2024 19:30:53 +0300 Subject: [PATCH 18/25] Fix noise a bit --- src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index 223c85b0..69a2dab8 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -71,7 +71,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) List responderMuxers = msg1Decoded.Extensions.StreamMuxers .Where(m => !string.IsNullOrEmpty(m)) .ToList(); - IProtocol? commonMuxer = multiplexerSettings?.Multiplexers.FirstOrDefault(m => responderMuxers.Contains(m.Id)); + IProtocol? commonMuxer = null;// multiplexerSettings?.Multiplexers.FirstOrDefault(m => responderMuxers.Contains(m.Id)); UpgradeOptions? upgradeOptions = null; @@ -171,7 +171,7 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) Transport? transport = msg2.Transport; List initiatorMuxers = msg2Decoded.Extensions.StreamMuxers.Where(m => !string.IsNullOrEmpty(m)).ToList(); - IProtocol? commonMuxer = multiplexerSettings?.Multiplexers.FirstOrDefault(m => initiatorMuxers.Contains(m.Id)); + IProtocol? commonMuxer = null; // multiplexerSettings?.Multiplexers.FirstOrDefault(m => initiatorMuxers.Contains(m.Id)); UpgradeOptions? upgradeOptions = null; From 092bdaddb88e9f30e86c85a6fdbd24a0c1781104 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 12 Dec 2024 19:39:03 +0300 Subject: [PATCH 19/25] Use Sha256 everywhere --- src/libp2p/Libp2p.Core/Identity.cs | 52 +------------------ .../chat/Properties/launchSettings.json | 4 +- 2 files changed, 3 insertions(+), 53 deletions(-) diff --git a/src/libp2p/Libp2p.Core/Identity.cs b/src/libp2p/Libp2p.Core/Identity.cs index 287ad3e8..44d5a83b 100644 --- a/src/libp2p/Libp2p.Core/Identity.cs +++ b/src/libp2p/Libp2p.Core/Identity.cs @@ -217,7 +217,7 @@ public byte[] Sign(byte[] message) { using RSA rsa = RSA.Create(); rsa.ImportRSAPrivateKey(PrivateKey.Data.Span, out _); - return rsa.SignData(message, 0, message.Length, HashAlgorithmName.SHA3_256, RSASignaturePadding.Pkcs1); + return rsa.SignData(message, 0, message.Length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } case KeyType.Secp256K1: { @@ -240,54 +240,4 @@ public byte[] Sign(byte[] message) } public PeerId PeerId => new(PublicKey); - - - //public byte[] CreateSignedEnvelope(byte[] message) - //{ - // if (PrivateKey is null) - // { - // throw new ArgumentException(nameof(PrivateKey)); - // } - - // switch (PublicKey.Type) - // { - // case KeyType.Ed25519: - // { - // byte[] sig = new byte[Ed25519.SignatureSize]; - // Ed25519.Sign(PrivateKey.Data.ToByteArray(), 0, PublicKey.Data.ToByteArray(), 0, - // message, 0, message.Length, sig, 0); - // return sig; - // } - // case KeyType.Ecdsa: - // { - // ECDsa e = ECDsa.Create(); - // e.ImportECPrivateKey(PrivateKey.Data.Span, out _); - // return e.SignData(message, HashAlgorithmName.SHA256, - // DSASignatureFormat.Rfc3279DerSequence); - // } - // case KeyType.Rsa: - // { - // using RSA rsa = RSA.Create(); - // rsa.ImportRSAPrivateKey(PrivateKey.Data.Span, out _); - // return rsa.SignData(message, 0, message.Length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); - // } - // case KeyType.Secp256K1: - // { - // X9ECParameters curve = CustomNamedCurves.GetByName("secp256k1"); - // ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA"); - - // ECPrivateKeyParameters privateKeyParams = new( - // "ECDSA", - // new BigInteger(1, PrivateKey.Data.ToArray()), - // new ECDomainParameters(curve) - // ); - - // signer.Init(true, privateKeyParams); - // signer.BlockUpdate(message, 0, message.Length); - // return signer.GenerateSignature(); - // } - // default: - // throw new NotImplementedException($"{PublicKey.Type} is not supported"); - // } - //} } diff --git a/src/samples/chat/Properties/launchSettings.json b/src/samples/chat/Properties/launchSettings.json index bb7462a9..4e8d0ddf 100644 --- a/src/samples/chat/Properties/launchSettings.json +++ b/src/samples/chat/Properties/launchSettings.json @@ -2,11 +2,11 @@ "profiles": { "Chat": { "commandName": "Project", - "commandLineArgs": "-d /ip4/127.0.0.1/tcp/9001/p2p/QmbbX5NgFcQgEWuHiQHKob1oQyhAUNncYpT3D9ZSkBQyFx --trace " + "commandLineArgs": "-d /ip4/127.0.0.1/tcp/9001/p2p/QmNMymSSfNSRvtB1tytWUYB6PXAZqmS8PYW7WKci5vuU8s --trace " }, "Chat server": { "commandName": "Project", "commandLineArgs": "--trace" } } -} +} \ No newline at end of file From c005c9cd11b94174c27c8a48cbe104819955cab9 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Thu, 12 Dec 2024 19:47:57 +0300 Subject: [PATCH 20/25] Fix test execution --- .../MultistreamProtocolTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs index 876a511b..1916bb26 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream.Tests/MultistreamProtocolTests.cs @@ -50,7 +50,7 @@ public async Task Test_ConnectionEstablished_AfterHandshake_With_SpecificRequest IProtocol? proto1 = Substitute.For(); proto1.Id.Returns("proto1"); - peerContext.UpgradeOptions.Returns(new UpgradeOptions()); + peerContext.UpgradeOptions.Returns(new UpgradeOptions { SelectedProtocol = proto1 }); peerContext.Upgrade(Arg.Any(), Arg.Any()).Returns(Task.CompletedTask); From 5970332fe4e0391b91ea571135a2eeb90aee87a0 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Fri, 13 Dec 2024 13:06:13 +0300 Subject: [PATCH 21/25] Hide possible exceptions during shutdown --- .../PubsubRouter.Topics.cs | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs index a354d177..53840a6d 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Topics.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT +using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; using Nethermind.Libp2p.Protocols.Pubsub.Dto; using System.Buffers.Binary; @@ -82,34 +83,41 @@ public void Unsubscribe(string topicId) public void UnsubscribeAll() { - foreach (PeerId? peerId in fPeers.SelectMany(kv => kv.Value)) + try { - Rpc msg = new Rpc().WithTopics([], topicState.Keys); + foreach (PeerId? peerId in fPeers.SelectMany(kv => kv.Value)) + { + Rpc msg = new Rpc().WithTopics([], topicState.Keys); - peerState.GetValueOrDefault(peerId)?.Send(msg); - } + peerState.GetValueOrDefault(peerId)?.Send(msg); + } - Dictionary peerMessages = []; + Dictionary peerMessages = []; - foreach (PeerId? peerId in gPeers.SelectMany(kv => kv.Value)) - { - (peerMessages[peerId] ??= new Rpc()) - .WithTopics([], topicState.Keys); - } - - foreach (KeyValuePair> topicMesh in mesh) - { - foreach (PeerId peerId in topicMesh.Value) + foreach (PeerId? peerId in gPeers.SelectMany(kv => kv.Value)) { (peerMessages[peerId] ??= new Rpc()) - .Ensure(r => r.Control.Prune) - .Add(new ControlPrune { TopicID = topicMesh.Key }); + .WithTopics([], topicState.Keys); } - } - foreach (KeyValuePair peerMessage in peerMessages) + foreach (KeyValuePair> topicMesh in mesh.ToDictionary()) + { + foreach (PeerId peerId in topicMesh.Value) + { + (peerMessages[peerId] ??= new Rpc()) + .Ensure(r => r.Control.Prune) + .Add(new ControlPrune { TopicID = topicMesh.Key }); + } + } + + foreach (KeyValuePair peerMessage in peerMessages) + { + peerState.GetValueOrDefault(peerMessage.Key)?.Send(peerMessage.Value); + } + } + catch (Exception e) { - peerState.GetValueOrDefault(peerMessage.Key)?.Send(peerMessage.Value); + logger?.LogError(e, $"Error during {nameof(UnsubscribeAll)}"); } } From dd81aaa15a50cf2e1a1ecd5be7449d06baae0525 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 16 Dec 2024 15:48:41 +0300 Subject: [PATCH 22/25] Move to separate files, rename, add test, remove junk --- .../Libp2p.Core.TestsBase/E2e/TestBuilder.cs | 6 +- .../E2e/TestMuxerTests.cs | 4 +- .../Libp2p.Core.TestsBase/LocalPeerStub.cs | 2 +- .../Libp2p.Core/Context/ConnectionContext.cs | 14 + src/libp2p/Libp2p.Core/Context/ContextBase.cs | 57 +++ .../Context/NewConnectionContext.cs | 14 + .../Libp2p.Core/Context/NewSessionContext.cs | 16 + .../Libp2p.Core/Context/SessionContext.cs | 24 + .../Discovery/IDiscoveryProtocol.cs | 2 +- src/libp2p/Libp2p.Core/Discovery/PeerStore.cs | 3 +- .../Libp2p.Core/Exceptions/Libp2pException.cs | 23 +- .../Extensions/ChannelFactoryExtensions.cs | 10 - .../Extensions/EnumerableExtensions.cs | 9 + src/libp2p/Libp2p.Core/IChannel.cs | 6 +- .../Libp2p.Core/ILibp2pBuilderContext.cs | 4 - src/libp2p/Libp2p.Core/IListener.cs | 7 - .../Libp2p.Core/{IPeer.cs => ILocalPeer.cs} | 10 +- src/libp2p/Libp2p.Core/IPeerContext.cs | 4 +- src/libp2p/Libp2p.Core/IPeerFactory.cs | 2 +- src/libp2p/Libp2p.Core/IProtocol.cs | 8 +- src/libp2p/Libp2p.Core/IRemotePeer.cs | 5 - src/libp2p/Libp2p.Core/ISession.cs | 14 + src/libp2p/Libp2p.Core/Ids.cs | 8 - src/libp2p/Libp2p.Core/LocalPeer.Session.cs | 68 +++ src/libp2p/Libp2p.Core/Peer.cs | 297 ++---------- src/libp2p/Libp2p.Core/PeerFactory.cs | 2 +- .../Libp2p.Core/PeerFactoryBuilderBase.cs | 2 +- src/libp2p/Libp2p.Core/TransportContext.cs | 2 +- src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs | 4 +- .../IdentifyProtocol.cs | 31 +- .../IdentifyProtocolSettings.cs | 17 +- .../Libp2p.Protocols.IpTcp/IpTcpProtocol.cs | 2 +- .../MDnsDiscoveryProtocol.cs | 12 +- .../MultistreamProtocol.cs | 125 ----- .../NoiseProtocolTests.cs | 161 +++---- .../Libp2p.Protocols.Noise/NoiseProtocol.cs | 16 +- src/libp2p/Libp2p.Protocols.Noise/README.md | 2 +- .../Libp2p.Protocols.Ping/PingProtocol.cs | 11 +- .../PlainTextProtocol.cs | 29 -- .../FloodsubProtocolTests.cs | 4 +- .../GossipsubProtocolTests.cs | 4 +- .../PubsubProtocolTests.cs | 2 +- .../IRoutingStateContainer.cs | 19 + .../Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 17 +- .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 446 +++++++++--------- .../PubsubDiscoveryE2eTestSetup.cs | 2 +- .../PubsubPeerDiscoveryProtocol.cs | 24 +- .../Libp2p.Protocols.Relay/Dto/Exchange.cs | 307 ++++++++++++ .../Libp2p.Protocols.Relay/Dto/Exchange.proto | 8 + .../Libp2p.Protocols.Relay.csproj | 37 ++ src/libp2p/Libp2p.Protocols.Relay/README.md | 3 + .../RelayHopProtocol.cs | 21 + .../RelayStopProtocol.cs | 21 + .../Libp2p.Protocols.Tls/TlsProtocol.cs | 12 +- src/libp2p/Libp2p.sln | 7 + src/libp2p/Libp2p/Libp2pPeerFactory.cs | 2 +- src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs | 2 +- src/samples/chat/Program.cs | 4 +- .../NoStackPeerFactoryBuilder.cs | 2 +- src/samples/perf-benchmarks/Program.cs | 8 +- src/samples/pubsub-chat/Program.cs | 4 +- src/samples/transport-interop/Program.cs | 6 +- 62 files changed, 1090 insertions(+), 905 deletions(-) create mode 100644 src/libp2p/Libp2p.Core/Context/ConnectionContext.cs create mode 100644 src/libp2p/Libp2p.Core/Context/ContextBase.cs create mode 100644 src/libp2p/Libp2p.Core/Context/NewConnectionContext.cs create mode 100644 src/libp2p/Libp2p.Core/Context/NewSessionContext.cs create mode 100644 src/libp2p/Libp2p.Core/Context/SessionContext.cs delete mode 100644 src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs create mode 100644 src/libp2p/Libp2p.Core/Extensions/EnumerableExtensions.cs delete mode 100644 src/libp2p/Libp2p.Core/IListener.cs rename src/libp2p/Libp2p.Core/{IPeer.cs => ILocalPeer.cs} (68%) delete mode 100644 src/libp2p/Libp2p.Core/IRemotePeer.cs create mode 100644 src/libp2p/Libp2p.Core/ISession.cs delete mode 100644 src/libp2p/Libp2p.Core/Ids.cs create mode 100644 src/libp2p/Libp2p.Core/LocalPeer.Session.cs create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub/IRoutingStateContainer.cs create mode 100644 src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs create mode 100644 src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto create mode 100644 src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj create mode 100644 src/libp2p/Libp2p.Protocols.Relay/README.md create mode 100644 src/libp2p/Libp2p.Protocols.Relay/RelayHopProtocol.cs create mode 100644 src/libp2p/Libp2p.Protocols.Relay/RelayStopProtocol.cs diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs index 13ea2dbb..7912768d 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestBuilder.cs @@ -10,7 +10,7 @@ namespace Nethermind.Libp2p.Core.TestsBase.E2e; public class TestBuilder(IServiceProvider? serviceProvider = null) : PeerFactoryBuilderBase(serviceProvider) { - protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) + protected override ProtocolRef[] BuildStack(IEnumerable additionalProtocols) { ProtocolRef root = Get(); @@ -27,9 +27,9 @@ .. additionalProtocols public class TestPeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, peerStore) { - ConcurrentDictionary peers = new(); + ConcurrentDictionary peers = new(); - public override IPeer Create(Identity? identity = default) + public override ILocalPeer Create(Identity? identity = default) { ArgumentNullException.ThrowIfNull(identity); return peers.GetOrAdd(identity.PeerId, (p) => new TestLocalPeer(identity, protocolStackSettings, peerStore, loggerFactory)); diff --git a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs index cb5dd3af..496fe013 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/E2e/TestMuxerTests.cs @@ -20,9 +20,9 @@ public async Task Test_ConnectionEstablished_AfterHandshake() .AddSingleton(sp => sp.GetService()!.Build()) .BuildServiceProvider(); - IPeer peerA = MakeServiceProvider().GetRequiredService().Create(TestPeers.Identity(1)); + ILocalPeer peerA = MakeServiceProvider().GetRequiredService().Create(TestPeers.Identity(1)); await peerA.StartListenAsync(); - IPeer peerB = MakeServiceProvider().GetRequiredService().Create(TestPeers.Identity(2)); + ILocalPeer peerB = MakeServiceProvider().GetRequiredService().Create(TestPeers.Identity(2)); await peerB.StartListenAsync(); ISession remotePeerB = await peerA.DialAsync(TestPeers.Multiaddr(2)); diff --git a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs index 1154e26d..78fbf036 100644 --- a/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs +++ b/src/libp2p/Libp2p.Core.TestsBase/LocalPeerStub.cs @@ -6,7 +6,7 @@ namespace Nethermind.Libp2p.Core.TestsBase; -public class LocalPeerStub : IPeer +public class LocalPeerStub : ILocalPeer { public LocalPeerStub() { diff --git a/src/libp2p/Libp2p.Core/Context/ConnectionContext.cs b/src/libp2p/Libp2p.Core/Context/ConnectionContext.cs new file mode 100644 index 00000000..ae912a68 --- /dev/null +++ b/src/libp2p/Libp2p.Core/Context/ConnectionContext.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Core.Context; + +public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), IConnectionContext +{ + public UpgradeOptions? UpgradeOptions => upgradeOptions; + + public Task DisconnectAsync() + { + return session.DisconnectAsync(); + } +} diff --git a/src/libp2p/Libp2p.Core/Context/ContextBase.cs b/src/libp2p/Libp2p.Core/Context/ContextBase.cs new file mode 100644 index 00000000..3c70d6ab --- /dev/null +++ b/src/libp2p/Libp2p.Core/Context/ContextBase.cs @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; + +namespace Nethermind.Libp2p.Core.Context; + +public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : IChannelFactory +{ + protected bool isListener = isListener; + public ILocalPeer Peer => localPeer; + public State State => session.State; + + public IEnumerable SubProtocols => localPeer.GetProtocolsFor(protocol); + + public string Id { get; } = session.Id; + + protected LocalPeer localPeer = localPeer; + protected LocalPeer.Session session = session; + protected ProtocolRef protocol = protocol; + protected UpgradeOptions? upgradeOptions = upgradeOptions; + + public IChannel Upgrade(UpgradeOptions? upgradeOptions = null) + { + return localPeer.Upgrade(session, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); + } + + public IChannel Upgrade(IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) + { + return localPeer.Upgrade(session, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); + } + + public Task Upgrade(IChannel parentChannel, UpgradeOptions? upgradeOptions = null) + { + return localPeer.Upgrade(session, parentChannel, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); + } + + public Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) + { + return localPeer.Upgrade(session, parentChannel, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); + } + + public INewConnectionContext CreateConnection() + { + return localPeer.CreateConnection(protocol, null, isListener); + } + + public INewSessionContext UpgradeToSession() + { + return localPeer.UpgradeToSession(session, protocol, isListener); + } + + public void ListenerReady(Multiaddress addr) + { + localPeer.ListenerReady(this, addr); + } +} diff --git a/src/libp2p/Libp2p.Core/Context/NewConnectionContext.cs b/src/libp2p/Libp2p.Core/Context/NewConnectionContext.cs new file mode 100644 index 00000000..e6744d48 --- /dev/null +++ b/src/libp2p/Libp2p.Core/Context/NewConnectionContext.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Core.Context; + +public class NewConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), INewConnectionContext +{ + public CancellationToken Token => session.ConnectionToken; + + public void Dispose() + { + + } +} diff --git a/src/libp2p/Libp2p.Core/Context/NewSessionContext.cs b/src/libp2p/Libp2p.Core/Context/NewSessionContext.cs new file mode 100644 index 00000000..21a6bbcf --- /dev/null +++ b/src/libp2p/Libp2p.Core/Context/NewSessionContext.cs @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Core.Context; + +public class NewSessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), INewSessionContext +{ + public IEnumerable DialRequests => session.GetRequestQueue(); + + public CancellationToken Token => session.ConnectionToken; + + public void Dispose() + { + + } +} diff --git a/src/libp2p/Libp2p.Core/Context/SessionContext.cs b/src/libp2p/Libp2p.Core/Context/SessionContext.cs new file mode 100644 index 00000000..73cdff53 --- /dev/null +++ b/src/libp2p/Libp2p.Core/Context/SessionContext.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Core.Context; + +public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), ISessionContext +{ + public UpgradeOptions? UpgradeOptions => upgradeOptions; + + public async Task DialAsync() where TProtocol : ISessionProtocol + { + await session.DialAsync(); + } + + public async Task DialAsync(ISessionProtocol protocol) + { + await session.DialAsync(protocol); + } + + public Task DisconnectAsync() + { + return session.DisconnectAsync(); + } +} diff --git a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs index 0b4c0e80..fd4029a5 100644 --- a/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Core/Discovery/IDiscoveryProtocol.cs @@ -7,5 +7,5 @@ namespace Nethermind.Libp2p.Core.Discovery; public interface IDiscoveryProtocol { - Task DiscoverAsync(IReadOnlyList localPeerAddr, CancellationToken token = default); + Task StartDiscoveryAsync(IReadOnlyList localPeerAddr, CancellationToken token = default); } diff --git a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs index 51681092..c026dc5c 100644 --- a/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs +++ b/src/libp2p/Libp2p.Core/Discovery/PeerStore.cs @@ -4,6 +4,7 @@ using Google.Protobuf; using Multiformats.Address; using Nethermind.Libp2p.Core.Dto; +using Nethermind.Libp2p.Core.Extensions; using System.Collections.Concurrent; namespace Nethermind.Libp2p.Core.Discovery; @@ -49,7 +50,7 @@ public void Discover(Multiaddress[] addrs) { PeerInfo? newOne = null; PeerInfo peerInfo = _store.GetOrAdd(peerId, (id) => newOne = new PeerInfo { Addrs = [.. addrs] }); - if (peerInfo != newOne && peerInfo.Addrs is not null && peerInfo.Addrs.Count == addrs.Length && addrs.All(a => peerInfo.Addrs.Any(a2 => a2.ToString() == a.ToString()))) + if (peerInfo != newOne && peerInfo.Addrs is not null && addrs.UnorderedSequenceEqual(peerInfo.Addrs)) { return; } diff --git a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs index ccd6c7d4..afe39951 100644 --- a/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs +++ b/src/libp2p/Libp2p.Core/Exceptions/Libp2pException.cs @@ -5,16 +5,13 @@ namespace Nethermind.Libp2p.Core.Exceptions; public class Libp2pException : Exception { - public Libp2pException(string? message) : base(message) - { - - } - public Libp2pException() : base() - { - - } + public Libp2pException(string? message) : base(message) { } + public Libp2pException() : base() { } } +/// +/// Exception instead of IOResult to signal a channel cannot send or receive data anymore +/// public class ChannelClosedException() : Libp2pException("Channel closed"); /// @@ -29,4 +26,12 @@ public class Libp2pSetupException(string? message = null) : Libp2pException(mess public class SessionExistsException(PeerId remotePeerId) : Libp2pException($"Session is already established with {remotePeerId}"); -public class PeerConnectionException : Libp2pException; +/// +/// Appears if connection to peer failed or declined +/// +public class PeerConnectionException(string? message = null) : Libp2pException(message); + +public class DLibp2pException : Libp2pException +{ + +} diff --git a/src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs b/src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs deleted file mode 100644 index 22eeed56..00000000 --- a/src/libp2p/Libp2p.Core/Extensions/ChannelFactoryExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core.Extensions; - -//internal static class ChannelFactoryExtensions -//{ -// public static IEnumerable GetSubProtocols(this ChannelFactory? channelFactory) -// => channelFactory?.SubProtocols.Select(protocol => protocol.Id) ?? Enumerable.Empty(); -//} diff --git a/src/libp2p/Libp2p.Core/Extensions/EnumerableExtensions.cs b/src/libp2p/Libp2p.Core/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..a34ab6ba --- /dev/null +++ b/src/libp2p/Libp2p.Core/Extensions/EnumerableExtensions.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +namespace Nethermind.Libp2p.Core.Extensions; + +public static class EnumerableExtensions +{ + public static bool UnorderedSequenceEqual(this IEnumerable left, IEnumerable right) => left.OrderBy(x => x).SequenceEqual(right.OrderBy(x => x)); +} diff --git a/src/libp2p/Libp2p.Core/IChannel.cs b/src/libp2p/Libp2p.Core/IChannel.cs index 6de73dcc..c933bb2d 100644 --- a/src/libp2p/Libp2p.Core/IChannel.cs +++ b/src/libp2p/Libp2p.Core/IChannel.cs @@ -14,9 +14,9 @@ CancellationToken CancellationToken { get { - CancellationTokenSource token = new(); - GetAwaiter().OnCompleted(token.Cancel); - return token.Token; + CancellationTokenSource cts = new(); + GetAwaiter().OnCompleted(cts.Cancel); + return cts.Token; } } } diff --git a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs index fc5011e8..41799cca 100644 --- a/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs +++ b/src/libp2p/Libp2p.Core/ILibp2pBuilderContext.cs @@ -1,10 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: MIT - -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - namespace Nethermind.Libp2p.Core; public interface IProtocolStackSettings diff --git a/src/libp2p/Libp2p.Core/IListener.cs b/src/libp2p/Libp2p.Core/IListener.cs deleted file mode 100644 index b1b0ff47..00000000 --- a/src/libp2p/Libp2p.Core/IListener.cs +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core; - - - diff --git a/src/libp2p/Libp2p.Core/IPeer.cs b/src/libp2p/Libp2p.Core/ILocalPeer.cs similarity index 68% rename from src/libp2p/Libp2p.Core/IPeer.cs rename to src/libp2p/Libp2p.Core/ILocalPeer.cs index 64530716..0364a98b 100644 --- a/src/libp2p/Libp2p.Core/IPeer.cs +++ b/src/libp2p/Libp2p.Core/ILocalPeer.cs @@ -6,7 +6,7 @@ namespace Nethermind.Libp2p.Core; -public interface IPeer +public interface ILocalPeer { Identity Identity { get; } @@ -28,11 +28,3 @@ public interface IPeer } public delegate Task Connected(ISession newSession); - -public interface ISession -{ - Multiaddress RemoteAddress { get; } - Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol; - Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol; - Task DisconnectAsync(); -} diff --git a/src/libp2p/Libp2p.Core/IPeerContext.cs b/src/libp2p/Libp2p.Core/IPeerContext.cs index 154b755c..be01190e 100644 --- a/src/libp2p/Libp2p.Core/IPeerContext.cs +++ b/src/libp2p/Libp2p.Core/IPeerContext.cs @@ -8,7 +8,7 @@ namespace Nethermind.Libp2p.Core; public interface ITransportContext { - IPeer Peer { get; } + ILocalPeer Peer { get; } void ListenerReady(Multiaddress addr); INewConnectionContext CreateConnection(); } @@ -35,7 +35,7 @@ public interface ISessionContext : IConnectionContext public interface INewConnectionContext : IDisposable, IChannelFactory, IContextState { - IPeer Peer { get; } + ILocalPeer Peer { get; } CancellationToken Token { get; } INewSessionContext UpgradeToSession(); } diff --git a/src/libp2p/Libp2p.Core/IPeerFactory.cs b/src/libp2p/Libp2p.Core/IPeerFactory.cs index e5875146..7531410f 100644 --- a/src/libp2p/Libp2p.Core/IPeerFactory.cs +++ b/src/libp2p/Libp2p.Core/IPeerFactory.cs @@ -5,5 +5,5 @@ namespace Nethermind.Libp2p.Core; public interface IPeerFactory { - IPeer Create(Identity? identity = default); + ILocalPeer Create(Identity? identity = default); } diff --git a/src/libp2p/Libp2p.Core/IProtocol.cs b/src/libp2p/Libp2p.Core/IProtocol.cs index 7e3a5a3e..4a4890a8 100644 --- a/src/libp2p/Libp2p.Core/IProtocol.cs +++ b/src/libp2p/Libp2p.Core/IProtocol.cs @@ -12,6 +12,12 @@ public interface IProtocol public interface ITransportProtocol : IProtocol { + static bool IsAddressMatch(IProtocol proto, Multiaddress addr) => (bool)proto.GetType() + .GetMethod(nameof(IsAddressMatch))!.Invoke(null, [addr])!; + static Multiaddress[] GetDefaultAddresses(IProtocol proto, PeerId peerId) => (Multiaddress[])proto.GetType() + .GetMethod(nameof(GetDefaultAddresses))!.Invoke(null, [peerId])!; + + static abstract Multiaddress[] GetDefaultAddresses(PeerId peerId); static abstract bool IsAddressMatch(Multiaddress addr); @@ -36,8 +42,6 @@ public interface ISessionProtocol : ISessionListenerProtocol Task DialAsync(IChannel downChannel, ISessionContext context); } -public class Void; - public interface ISessionProtocol : ISessionListenerProtocol { Task DialAsync(IChannel downChannel, ISessionContext context, TRequest request); diff --git a/src/libp2p/Libp2p.Core/IRemotePeer.cs b/src/libp2p/Libp2p.Core/IRemotePeer.cs deleted file mode 100644 index 9b38be09..00000000 --- a/src/libp2p/Libp2p.Core/IRemotePeer.cs +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core; - diff --git a/src/libp2p/Libp2p.Core/ISession.cs b/src/libp2p/Libp2p.Core/ISession.cs new file mode 100644 index 00000000..0bf75d29 --- /dev/null +++ b/src/libp2p/Libp2p.Core/ISession.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; + +namespace Nethermind.Libp2p.Core; + +public interface ISession +{ + Multiaddress RemoteAddress { get; } + Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol; + Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol; + Task DisconnectAsync(); +} diff --git a/src/libp2p/Libp2p.Core/Ids.cs b/src/libp2p/Libp2p.Core/Ids.cs deleted file mode 100644 index 0e0ad8fa..00000000 --- a/src/libp2p/Libp2p.Core/Ids.cs +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited -// SPDX-License-Identifier: MIT - -namespace Nethermind.Libp2p.Core; -public static class Ids -{ - public static int IdCounter = 0; -} diff --git a/src/libp2p/Libp2p.Core/LocalPeer.Session.cs b/src/libp2p/Libp2p.Core/LocalPeer.Session.cs new file mode 100644 index 00000000..b9779fd6 --- /dev/null +++ b/src/libp2p/Libp2p.Core/LocalPeer.Session.cs @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Multiformats.Address; +using Nethermind.Libp2p.Core.Exceptions; +using System.Collections.Concurrent; + +namespace Nethermind.Libp2p.Core; + +public partial class LocalPeer +{ + public class Session(LocalPeer peer) : ISession + { + private static int SessionIdCounter; + + public string Id { get; } = Interlocked.Increment(ref SessionIdCounter).ToString(); + public State State { get; } = new(); + public Multiaddress RemoteAddress => State.RemoteAddress ?? throw new Libp2pException("Session contains uninitialized remote address."); + + private readonly BlockingCollection SubDialRequests = []; + + public async Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol + { + TaskCompletionSource tcs = new(); + SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs!, SelectedProtocol = peer.GetProtocolInstance() }, token); + await tcs.Task; + MarkAsConnected(); + } + + public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = default) + { + TaskCompletionSource tcs = new(); + SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = protocol }, token); + await tcs.Task; + MarkAsConnected(); + } + + public async Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol + { + TaskCompletionSource tcs = new(); + SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance(), Argument = request }, token); + await tcs.Task; + MarkAsConnected(); + return (TResponse)tcs.Task.Result; + } + + + private CancellationTokenSource connectionTokenSource = new(); + + public Task DisconnectAsync() + { + connectionTokenSource.Cancel(); + peer.sessions.Remove(this); + return Task.CompletedTask; + } + + public CancellationToken ConnectionToken => connectionTokenSource.Token; + + + public TaskCompletionSource ConnectedTcs = new(); + public Task Connected => ConnectedTcs.Task; + internal void MarkAsConnected() => ConnectedTcs?.TrySetResult(); + + + internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); + + } +} diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 60a30e29..62d09e6b 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging; using Multiformats.Address; using Multiformats.Address.Protocols; +using Nethermind.Libp2p.Core.Context; using Nethermind.Libp2p.Core.Discovery; using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Core.Extensions; -using System.Collections.Concurrent; using System.Collections.ObjectModel; namespace Nethermind.Libp2p.Core; -public class LocalPeer : IPeer +public partial class LocalPeer : ILocalPeer { protected readonly ILogger? _logger; protected readonly PeerStore _peerStore; @@ -42,61 +42,6 @@ public override string ToString() protected virtual Task ConnectedTo(ISession peer, bool isDialer) => Task.CompletedTask; - public class Session(LocalPeer peer) : ISession - { - public string Id { get; } = Interlocked.Increment(ref Ids.IdCounter).ToString(); - public State State { get; } = new(); - public Multiaddress RemoteAddress => State.RemoteAddress ?? throw new Libp2pException("Session contains uninitialized remote address."); - - private readonly BlockingCollection SubDialRequests = []; - - public async Task DialAsync(CancellationToken token = default) where TProtocol : ISessionProtocol - { - TaskCompletionSource tcs = new(); - SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs!, SelectedProtocol = peer.GetProtocolInstance() }, token); - await tcs.Task; - MarkAsConnected(); - } - - public async Task DialAsync(ISessionProtocol protocol, CancellationToken token = default) - { - TaskCompletionSource tcs = new(); - SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = protocol }, token); - await tcs.Task; - MarkAsConnected(); - } - - public async Task DialAsync(TRequest request, CancellationToken token = default) where TProtocol : ISessionProtocol - { - TaskCompletionSource tcs = new(); - SubDialRequests.Add(new UpgradeOptions() { CompletionSource = tcs, SelectedProtocol = peer.GetProtocolInstance(), Argument = request }, token); - await tcs.Task; - MarkAsConnected(); - return (TResponse)tcs.Task.Result; - } - - - private CancellationTokenSource connectionTokenSource = new(); - - public Task DisconnectAsync() - { - connectionTokenSource.Cancel(); - peer.sessions.Remove(this); - return Task.CompletedTask; - } - - public CancellationToken ConnectionToken => connectionTokenSource.Token; - - - public TaskCompletionSource ConnectedTcs = new(); - public Task Connected => ConnectedTcs.Task; - internal void MarkAsConnected() => ConnectedTcs?.TrySetResult(); - - - internal IEnumerable GetRequestQueue() => SubDialRequests.GetConsumingEnumerable(ConnectionToken); - - } - protected virtual ProtocolRef SelectProtocol(Multiaddress addr) { if (_protocolStackSettings.TopProtocols is null or []) @@ -104,8 +49,9 @@ protected virtual ProtocolRef SelectProtocol(Multiaddress addr) throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - return _protocolStackSettings.TopProtocols.First(p => (bool)p.Protocol.GetType().GetMethod(nameof(ITransportProtocol.IsAddressMatch))!.Invoke(null, [addr])!); + return _protocolStackSettings.TopProtocols.First(p => ITransportProtocol.IsAddressMatch(p.Protocol, addr)); } + protected virtual Multiaddress[] GetDefaultAddresses() { if (_protocolStackSettings.TopProtocols is null or []) @@ -113,7 +59,7 @@ protected virtual Multiaddress[] GetDefaultAddresses() throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - return _protocolStackSettings.TopProtocols.SelectMany(p => (Multiaddress[])p.Protocol.GetType().GetMethod(nameof(ITransportProtocol.GetDefaultAddresses))!.Invoke(null, [Identity.PeerId])!).ToArray(); + return _protocolStackSettings.TopProtocols.SelectMany(p => ITransportProtocol.GetDefaultAddresses(p.Protocol, Identity.PeerId)).ToArray(); } protected virtual IEnumerable PrepareAddresses(Multiaddress[] addrs) @@ -338,100 +284,6 @@ public Task DialAsync(PeerId peerId, CancellationToken token = default return DialAsync([.. existingPeerInfo.Addrs], token); } - internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) - { - if (_protocolStackSettings.Protocols is null) - { - throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); - } - - if (!_protocolStackSettings.Protocols.ContainsKey(parentProtocol)) - { - throw new Libp2pSetupException($"{parentProtocol} is not added"); - } - - ProtocolRef top = upgradeProtocol is not null ? _protocolStackSettings.Protocols[parentProtocol].SingleOrDefault(x => x.Protocol == options.SelectedProtocol) ?? new ProtocolRef(upgradeProtocol) : - _protocolStackSettings.Protocols[parentProtocol].Single(); - - Channel downChannel = new(); - - isListener = options?.ModeOverride switch { UpgradeModeOverride.Dial => false, UpgradeModeOverride.Listen => true, _ => isListener }; - - Task upgradeTask; - - _logger?.LogInformation($"Upgrade {parentProtocol} to {top}, listen={isListener}"); - - switch (top.Protocol) - { - case IConnectionProtocol tProto: - { - ConnectionContext ctx = new(this, session, top, isListener, options); - upgradeTask = isListener ? tProto.ListenAsync(downChannel.Reverse, ctx) : tProto.DialAsync(downChannel.Reverse, ctx); - - break; - } - case ISessionProtocol sProto: - { - SessionContext ctx = new(this, session, top, isListener, options); - upgradeTask = isListener ? sProto.ListenAsync(downChannel.Reverse, ctx) : sProto.DialAsync(downChannel.Reverse, ctx); - break; - } - default: - if (isListener && top.Protocol is ISessionListenerProtocol listenerProtocol) - { - SessionContext ctx = new(this, session, top, isListener, options); - upgradeTask = listenerProtocol.ListenAsync(downChannel.Reverse, ctx); - break; - } - - Type? genericInterface = top.Protocol.GetType().GetInterfaces() - .FirstOrDefault(i => - i.IsGenericType && - i.GetGenericTypeDefinition() == typeof(ISessionProtocol<,>)); - - if (genericInterface != null) - { - Type[] genericArguments = genericInterface.GetGenericArguments(); - Type requestType = genericArguments[0]; - - if (options?.Argument is not null && !options.Argument.GetType().IsAssignableTo(requestType)) - { - throw new ArgumentException($"Invalid request. Argument is of {options.Argument.GetType()} type which is not assignable to {requestType.FullName}"); - } - - System.Reflection.MethodInfo? dialAsyncMethod = genericInterface.GetMethod("DialAsync"); - if (dialAsyncMethod != null) - { - SessionContext ctx = new(this, session, top, isListener, options); - upgradeTask = (Task)dialAsyncMethod.Invoke(top.Protocol, [downChannel.Reverse, ctx, options?.Argument])!; - break; - } - } - throw new Libp2pSetupException($"Protocol {top.Protocol} does not implement proper protocol interface"); - } - - if (options?.SelectedProtocol == top.Protocol && options?.CompletionSource is not null) - { - _ = upgradeTask.ContinueWith(async t => - { - MapToTaskCompletionSource(t, options.CompletionSource); - await downChannel.CloseAsync(); - }); - } - - upgradeTask.ContinueWith(t => - { - if (t.IsFaulted) - { - _logger?.LogError($"Upgrade task failed for {top} with {t.Exception}"); - } - _ = downChannel.CloseAsync(); - _logger?.LogInformation($"Finished {parentProtocol} to {top}, listen={isListener}"); - }); - - return downChannel; - } - private static void MapToTaskCompletionSource(Task t, TaskCompletionSource tcs) { if (t.IsCompletedSuccessfully) @@ -462,25 +314,34 @@ private static void MapToTaskCompletionSource(Task t, TaskCompletionSource tcs) tcs.SetException(t.Exception!); } - internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef protocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) + internal IChannel Upgrade(Session session, ProtocolRef parentProtocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) + { + Channel downChannel = new(); + + _ = Upgrade(session, downChannel.Reverse, parentProtocol, upgradeProtocol, options, isListener); + + return downChannel; + } + + internal Task Upgrade(Session session, IChannel downChannel, ProtocolRef parentProtocol, IProtocol? upgradeProtocol, UpgradeOptions? options, bool isListener) { if (_protocolStackSettings.Protocols is null) { throw new Libp2pSetupException($"Protocols are not set in {nameof(_protocolStackSettings)}"); } - if (upgradeProtocol is not null && !_protocolStackSettings.Protocols[protocol].Any(p => p.Protocol == upgradeProtocol)) + if (upgradeProtocol is not null && !_protocolStackSettings.Protocols[parentProtocol].Any(p => p.Protocol == upgradeProtocol)) { _protocolStackSettings.Protocols.Add(new ProtocolRef(upgradeProtocol, false), []); } ProtocolRef top = upgradeProtocol is not null ? - _protocolStackSettings.Protocols[protocol].FirstOrDefault(p => p.Protocol == upgradeProtocol, _protocolStackSettings.Protocols.Keys.First(k => k.Protocol == upgradeProtocol)) : - _protocolStackSettings.Protocols[protocol].Single(); + _protocolStackSettings.Protocols[parentProtocol].FirstOrDefault(p => p.Protocol == upgradeProtocol, _protocolStackSettings.Protocols.Keys.First(k => k.Protocol == upgradeProtocol)) : + _protocolStackSettings.Protocols[parentProtocol].Single(); isListener = options?.ModeOverride switch { UpgradeModeOverride.Dial => false, UpgradeModeOverride.Listen => true, _ => isListener }; - _logger?.LogInformation($"Upgrade and bind {protocol} to {top}, listen={isListener}"); + _logger?.LogInformation($"Upgrade and bind {parentProtocol} to {top}, listen={isListener}"); Task upgradeTask; switch (top.Protocol) @@ -488,20 +349,20 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef case IConnectionProtocol tProto: { ConnectionContext ctx = new(this, session, top, isListener, options); - upgradeTask = isListener ? tProto.ListenAsync(parentChannel, ctx) : tProto.DialAsync(parentChannel, ctx); + upgradeTask = isListener ? tProto.ListenAsync(downChannel, ctx) : tProto.DialAsync(downChannel, ctx); break; } case ISessionProtocol sProto: { SessionContext ctx = new(this, session, top, isListener, options); - upgradeTask = isListener ? sProto.ListenAsync(parentChannel, ctx) : sProto.DialAsync(parentChannel, ctx); + upgradeTask = isListener ? sProto.ListenAsync(downChannel, ctx) : sProto.DialAsync(downChannel, ctx); break; } default: if (isListener && top.Protocol is ISessionListenerProtocol listenerProtocol) { SessionContext ctx = new(this, session, top, isListener, options); - upgradeTask = listenerProtocol.ListenAsync(parentChannel, ctx); + upgradeTask = listenerProtocol.ListenAsync(downChannel, ctx); break; } @@ -514,7 +375,6 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef { Type[] genericArguments = genericInterface.GetGenericArguments(); Type requestType = genericArguments[0]; - Type responseType = genericArguments[1]; if (options?.Argument is not null && !options.Argument.GetType().IsAssignableTo(requestType)) { @@ -526,7 +386,7 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef if (dialAsyncMethod != null) { SessionContext ctx = new(this, session, top, isListener, options); - upgradeTask = (Task)dialAsyncMethod.Invoke(top.Protocol, [parentChannel, ctx, options?.Argument])!; + upgradeTask = (Task)dialAsyncMethod.Invoke(top.Protocol, [downChannel, ctx, options?.Argument])!; break; } } @@ -538,123 +398,20 @@ internal async Task Upgrade(Session session, IChannel parentChannel, ProtocolRef _ = upgradeTask.ContinueWith(async t => { MapToTaskCompletionSource(t, options.CompletionSource); - await parentChannel.CloseAsync(); + await downChannel.CloseAsync(); }); } - await upgradeTask.ContinueWith(t => + return upgradeTask.ContinueWith(t => { if (t.IsFaulted) { _logger?.LogError($"Upgrade task failed with {t.Exception}"); } - _ = parentChannel.CloseAsync(); - _logger?.LogInformation($"Finished#2 {protocol} to {top}, listen={isListener}"); + _ = downChannel.CloseAsync(); + _logger?.LogInformation($"Finished#2 {parentProtocol} to {top}, listen={isListener}"); }); } public Task DisconnectAsync() => Task.WhenAll(sessions.ToArray().Select(s => s.DisconnectAsync())); } - -public class NewSessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), INewSessionContext -{ - public IEnumerable DialRequests => session.GetRequestQueue(); - - public CancellationToken Token => session.ConnectionToken; - - public void Dispose() - { - - } -} - -public class SessionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), ISessionContext -{ - public UpgradeOptions? UpgradeOptions => upgradeOptions; - - public async Task DialAsync() where TProtocol : ISessionProtocol - { - await session.DialAsync(); - } - - public async Task DialAsync(ISessionProtocol protocol) - { - await session.DialAsync(protocol); - } - - public Task DisconnectAsync() - { - return session.DisconnectAsync(); - } -} - -public class NewConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), INewConnectionContext -{ - public CancellationToken Token => session.ConnectionToken; - - public void Dispose() - { - - } -} - -public class ConnectionContext(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : ContextBase(localPeer, session, protocol, isListener, upgradeOptions), IConnectionContext -{ - public UpgradeOptions? UpgradeOptions => upgradeOptions; - - public Task DisconnectAsync() - { - return session.DisconnectAsync(); - } -} - -public class ContextBase(LocalPeer localPeer, LocalPeer.Session session, ProtocolRef protocol, bool isListener, UpgradeOptions? upgradeOptions) : IChannelFactory -{ - protected bool isListener = isListener; - public IPeer Peer => localPeer; - public State State => session.State; - - public IEnumerable SubProtocols => localPeer.GetProtocolsFor(protocol); - - public string Id { get; } = session.Id; - - protected LocalPeer localPeer = localPeer; - protected LocalPeer.Session session = session; - protected ProtocolRef protocol = protocol; - protected UpgradeOptions? upgradeOptions = upgradeOptions; - - public IChannel Upgrade(UpgradeOptions? upgradeOptions = null) - { - return localPeer.Upgrade(session, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); - } - - public IChannel Upgrade(IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) - { - return localPeer.Upgrade(session, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); - } - - public Task Upgrade(IChannel parentChannel, UpgradeOptions? upgradeOptions = null) - { - return localPeer.Upgrade(session, parentChannel, protocol, null, upgradeOptions ?? this.upgradeOptions, isListener); - } - - public Task Upgrade(IChannel parentChannel, IProtocol specificProtocol, UpgradeOptions? upgradeOptions = null) - { - return localPeer.Upgrade(session, parentChannel, protocol, specificProtocol, upgradeOptions ?? this.upgradeOptions, isListener); - } - - public INewConnectionContext CreateConnection() - { - return localPeer.CreateConnection(protocol, null, isListener); - } - - public INewSessionContext UpgradeToSession() - { - return localPeer.UpgradeToSession(session, protocol, isListener); - } - - public void ListenerReady(Multiaddress addr) - { - localPeer.ListenerReady(this, addr); - } -} diff --git a/src/libp2p/Libp2p.Core/PeerFactory.cs b/src/libp2p/Libp2p.Core/PeerFactory.cs index 66bed12d..f41610ee 100644 --- a/src/libp2p/Libp2p.Core/PeerFactory.cs +++ b/src/libp2p/Libp2p.Core/PeerFactory.cs @@ -13,7 +13,7 @@ public class PeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore protected PeerStore PeerStore { get; } = peerStore; protected ILoggerFactory? LoggerFactory { get; } = loggerFactory; - public virtual IPeer Create(Identity? identity = default) + public virtual ILocalPeer Create(Identity? identity = default) { return new LocalPeer(identity ?? new Identity(), PeerStore, protocolStackSettings, LoggerFactory); } diff --git a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs index b7dda9b5..790953aa 100644 --- a/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs +++ b/src/libp2p/Libp2p.Core/PeerFactoryBuilderBase.cs @@ -61,7 +61,7 @@ public IPeerFactoryBuilder AddAppLayerProtocol(TProtocol? instance = return (TBuilder)this; } - protected abstract ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols); + protected abstract ProtocolRef[] BuildStack(IEnumerable additionalProtocols); private Dictionary protocols = []; diff --git a/src/libp2p/Libp2p.Core/TransportContext.cs b/src/libp2p/Libp2p.Core/TransportContext.cs index 667dd841..2551cf7b 100644 --- a/src/libp2p/Libp2p.Core/TransportContext.cs +++ b/src/libp2p/Libp2p.Core/TransportContext.cs @@ -8,7 +8,7 @@ namespace Nethermind.Libp2p.Core; public class TransportContext(LocalPeer peer, ProtocolRef proto, bool isListener) : ITransportContext { public Identity Identity => peer.Identity; - public IPeer Peer => peer; + public ILocalPeer Peer => peer; public bool IsListener => isListener; public void ListenerReady(Multiaddress addr) diff --git a/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs b/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs index 957bd966..2015065b 100644 --- a/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs +++ b/src/libp2p/Libp2p.E2eTests/E2eTestSetup.cs @@ -24,7 +24,7 @@ public void Dispose() protected ILogger TestLogger { get; set; } = loggerFactory.CreateLogger("test-setup"); - public Dictionary Peers { get; } = []; + public Dictionary Peers { get; } = []; public Dictionary PeerStores { get; } = []; public Dictionary ServiceProviders { get; } = []; @@ -79,7 +79,7 @@ public void PrintState(bool outputToConsole = false) StringBuilder reportBuilder = new(); reportBuilder.AppendLine($"Test state#{stateCounter++}"); - foreach ((int index, IPeer peer) in Peers) + foreach ((int index, ILocalPeer peer) in Peers) { AddToPrintState(reportBuilder, index); reportBuilder.AppendLine(peer.ToString()); diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs index b2ea9fe8..54e30ce3 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocol.cs @@ -18,13 +18,10 @@ namespace Nethermind.Libp2p.Protocols; /// public class IdentifyProtocol : ISessionProtocol { - private readonly string _agentVersion; - private readonly string _protocolVersion; - private readonly ILogger? _logger; private readonly PeerStore? _peerStore; private readonly IProtocolStackSettings _protocolStackSettings; - + private readonly IdentifyProtocolSettings _settings; public string Id => "/ipfs/id/1.0.0"; @@ -33,9 +30,7 @@ public IdentifyProtocol(IProtocolStackSettings protocolStackSettings, IdentifyPr _logger = loggerFactory?.CreateLogger(); _peerStore = peerStore; _protocolStackSettings = protocolStackSettings; - - _agentVersion = settings?.AgentVersion ?? IdentifyProtocolSettings.Default.AgentVersion!; - _protocolVersion = settings?.ProtocolVersion ?? IdentifyProtocolSettings.Default.ProtocolVersion!; + _settings = settings ?? new IdentifyProtocolSettings(); } @@ -58,20 +53,30 @@ public async Task DialAsync(IChannel channel, ISessionContext context) { if (!SigningHelper.VerifyPeerRecord(identify.SignedPeerRecord, context.State.RemotePublicKey)) { - //throw new PeerConnectionException(); - _logger?.LogError("Peer record is not valid: {peerId}", context.State.RemotePeerId); + if (_settings?.PeerRecordsVerificationPolicy == PeerRecordsVerificationPolicy.RequireCorrect) + { + throw new PeerConnectionException("Malformed peer identity: peer record signature is not valid"); + } + else + { + _logger?.LogWarning("Malformed peer identity: peer record signature is not valid"); + } } else { _peerStore.GetPeerInfo(context.State.RemotePeerId).SignedPeerRecord = identify.SignedPeerRecord; - _logger?.LogInformation("Confirmed peer record: {peerId}", context.State.RemotePeerId); + _logger?.LogDebug("Confirmed peer record: {peerId}", context.State.RemotePeerId); } } + else if (_settings.PeerRecordsVerificationPolicy != PeerRecordsVerificationPolicy.DoesNotRequire) + { + throw new PeerConnectionException("Malformed peer identity: there is no peer record which is required"); + } } if (context.State.RemotePublicKey.ToByteString() != identify.PublicKey) { - throw new PeerConnectionException(); + throw new PeerConnectionException("Malformed peer identity: the remote public key corresponds to a different peer id"); } } @@ -81,8 +86,8 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) Identify.Dto.Identify identify = new() { - ProtocolVersion = _protocolVersion, - AgentVersion = _agentVersion, + ProtocolVersion = _settings.ProtocolVersion, + AgentVersion = _settings.AgentVersion, PublicKey = context.Peer.Identity.PublicKey.ToByteString(), ListenAddrs = { context.Peer.ListenAddresses.Select(x => ByteString.CopyFrom(x.ToBytes())) }, ObservedAddr = ByteString.CopyFrom(context.State.RemoteAddress!.ToEndPoint(out ProtocolType proto).ToMultiaddress(proto).ToBytes()), diff --git a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocolSettings.cs b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocolSettings.cs index ab150f50..3078962e 100644 --- a/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocolSettings.cs +++ b/src/libp2p/Libp2p.Protocols.Identify/IdentifyProtocolSettings.cs @@ -5,12 +5,15 @@ namespace Nethermind.Libp2p.Protocols; public class IdentifyProtocolSettings { - public string? AgentVersion { get; set; } - public string? ProtocolVersion { get; set; } + public string AgentVersion { get; set; } = "ipfs/1.0.0"; + public string ProtocolVersion { get; set; } = "dotnet-libp2p/1.0.0"; + public PeerRecordsVerificationPolicy PeerRecordsVerificationPolicy { get; set; } = PeerRecordsVerificationPolicy.RequireWithWarning; +} + - public static IdentifyProtocolSettings Default { get; } = new() - { - ProtocolVersion = "ipfs/1.0.0", - AgentVersion = "dotnet-libp2p/1.0.0", - }; +public enum PeerRecordsVerificationPolicy +{ + RequireCorrect, + RequireWithWarning, + DoesNotRequire } diff --git a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs index 25de344f..f2471f7c 100644 --- a/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.IpTcp/IpTcpProtocol.cs @@ -20,7 +20,7 @@ public class IpTcpProtocol(ILoggerFactory? loggerFactory = null) : ITransportPro public string Id => "ip-tcp"; public static Multiaddress[] GetDefaultAddresses(PeerId peerId) => IpHelper.GetListenerAddresses() - .Select(a => Multiaddress.Decode($"/{(a.AddressFamily is AddressFamily.InterNetwork ? "ip4" : "ip6")}/{a}/tcp/0/p2p/{peerId}")).Where(x => x.Has()).Take(1).ToArray(); + .Select(a => Multiaddress.Decode($"/{(a.AddressFamily is AddressFamily.InterNetwork ? "ip4" : "ip6")}/{a}/tcp/0/p2p/{peerId}")).ToArray(); public static bool IsAddressMatch(Multiaddress addr) => addr.Has(); public async Task ListenAsync(ITransportContext context, Multiaddress listenAddr, CancellationToken token) diff --git a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs index 2b3d256e..a4a891da 100644 --- a/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.MDns/MDnsDiscoveryProtocol.cs @@ -24,7 +24,7 @@ public class MDnsDiscoveryProtocol(PeerStore peerStore, ILoggerFactory? loggerFa private string PeerName = null!; - public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, CancellationToken token = default) + public Task StartDiscoveryAsync(IReadOnlyList localPeerAddrs, CancellationToken token = default) { ObservableCollection peers = []; ServiceDiscovery sd = new(); @@ -64,12 +64,11 @@ public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, Canc _logger?.LogInformation("Started as {0} {1}", PeerName, ServiceNameOverride ?? ServiceName); - - sd.ServiceDiscovered += (s, serviceName) => { _logger?.LogTrace("Srv disc {0}", serviceName); }; + sd.ServiceInstanceDiscovered += (s, e) => { Multiaddress[] records = e.Message.AdditionalRecords.OfType() @@ -94,6 +93,12 @@ public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, Canc _logger?.LogError(ex, "Error setting up mDNS"); } + _ = RunAsync(sd, token); + return Task.CompletedTask; + } + + private async Task RunAsync(ServiceDiscovery sd, CancellationToken token) + { while (!token.IsCancellationRequested) { try @@ -107,7 +112,6 @@ public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, Canc } await Task.Delay(MdnsQueryInterval, token); } - } private static string RandomString(int length) diff --git a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs index 16529cf5..51a34c20 100644 --- a/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Multistream/MultistreamProtocol.cs @@ -127,128 +127,3 @@ private async Task SendHello(IChannel channel) return line == Id; } } - - - -public class MultistreamProtocol2 : IConnectionProtocol -{ - private readonly ILogger? _logger; - private const string ProtocolNotSupported = "na"; - public string Id => "/multistream/1.0.0"; - - public MultistreamProtocol2(ILoggerFactory? loggerFactory = null) - { - _logger = loggerFactory?.CreateLogger(); - } - - public async Task DialAsync(IChannel channel, IConnectionContext context) - { - await foreach (var item in channel.ReadAllAsync()) - { - _logger?.LogTrace(item.ToString()); - } - _logger?.LogTrace($"Hello started"); - - if (!await SendHello(channel)) - { - await channel.CloseAsync(); - _logger?.LogTrace($"Hello failed"); - return; - } - _logger?.LogTrace($"Hello passed"); - - async Task DialProtocol(IProtocol selector) - { - await channel.WriteLineAsync(selector.Id); - string selectorLine = await channel.ReadLineAsync(); - _logger?.LogTrace($"Proposed {selector.Id}, answer: {selectorLine}"); - if (selectorLine == selector.Id) - { - return true; - } - - if (selectorLine != ProtocolNotSupported) - { - return false; - } - - return null; - } - - IProtocol? selected = null; - - if (context.UpgradeOptions?.SelectedProtocol is not null) - { - _logger?.LogDebug($"Proposing just {context.UpgradeOptions.SelectedProtocol}"); - if (await DialProtocol(context.UpgradeOptions.SelectedProtocol) == true) - { - selected = context.UpgradeOptions.SelectedProtocol; - } - } - else - { - foreach (IProtocol selector in context!.SubProtocols) - { - bool? dialResult = await DialProtocol(selector); - if (dialResult == true) - { - selected = selector; - break; - } - else if (dialResult == false) - { - break; - } - } - } - - if (selected is null) - { - _logger?.LogDebug($"Negotiation failed"); - return; - } - _logger?.LogDebug($"Protocol selected during dialing: {selected.Id}"); - await context.Upgrade(channel, selected); - } - - public async Task ListenAsync(IChannel channel, IConnectionContext context) - { - if (!await SendHello(channel)) - { - await channel.CloseAsync(); - return; - } - - IProtocol? selected = null; - for (; ; ) - { - string proto = await channel.ReadLineAsync(); - selected = context.SubProtocols.FirstOrDefault(x => x.Id == proto) as IProtocol; - if (selected is not null) - { - await channel.WriteLineAsync(selected.Id); - _logger?.LogTrace($"Proposed by remote {proto}, answer: {selected?.Id}"); - break; - } - - _logger?.LogTrace($"Proposed by remote {proto}, answer: {ProtocolNotSupported}"); - await channel.WriteLineAsync(ProtocolNotSupported); - } - - if (selected is null) - { - _logger?.LogDebug($"Negotiation failed"); - return; - } - - _logger?.LogDebug($"Protocol selected during listening: {selected}"); - await context.Upgrade(channel, selected); - } - - private async Task SendHello(IChannel channel) - { - await channel.WriteLineAsync(Id); - string line = await channel.ReadLineAsync(); - return line == Id; - } -} diff --git a/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs index 1224b515..498334b6 100644 --- a/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Noise.Tests/NoiseProtocolTests.cs @@ -1,123 +1,76 @@ -//// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -//// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT -//namespace Nethermind.Libp2p.Protocols.Noise.Tests; - -//[TestFixture] -//[Parallelizable(scope: ParallelScope.All)] -//public class NoiseProtocolTests -//{ -// [Test] -// public async Task Test_ConnectionEstablished_AfterHandshake() -// { -// // Arrange -// IChannel downChannel = new TestChannel(); -// IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); -// IChannelFactory channelFactory = Substitute.For(); -// IPeerContext peerContext = Substitute.For(); -// IPeerContext listenerContext = Substitute.For(); +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Core.TestsBase; +using NSubstitute; +using NUnit.Framework; -// IProtocol? proto1 = Substitute.For(); -// proto1.Id.Returns("proto1"); +namespace Nethermind.Libp2p.Protocols.Noise.Tests; -// IProtocol? proto2 = Substitute.For(); -// proto2.Id.Returns("proto2"); +[TestFixture] +[Parallelizable(scope: ParallelScope.All)] +public class NoiseProtocolTests +{ + [Test] + public async Task Test_ConnectionEstablished_AfterHandshake() + { + // Arrange + IChannel downChannel = new TestChannel(); + IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); -// channelFactory.SubProtocols.Returns([proto1, proto2]); + IProtocol? proto1 = Substitute.For(); + proto1.Id.Returns("proto1"); -// TestChannel upChannel = new TestChannel(); -// channelFactory.SubDial(Arg.Any(), Arg.Any()) -// .Returns(upChannel); + IProtocol? proto2 = Substitute.For(); + proto2.Id.Returns("proto2"); -// TestChannel listenerUpChannel = new TestChannel(); + // Dialer + MultiplexerSettings dialerSettings = new(); + dialerSettings.Add(proto2); + dialerSettings.Add(proto1); -// channelFactory.SubListen(Arg.Any(), Arg.Any()) -// .Returns(listenerUpChannel); + IConnectionContext dialerContext = Substitute.For(); + dialerContext.Peer.Identity.Returns(TestPeers.Identity(1)); + dialerContext.Peer.ListenAddresses.Returns([TestPeers.Multiaddr(1)]); + dialerContext.State.Returns(new State() { RemoteAddress = $"/ip4/0.0.0.0/tcp/0/p2p/{TestPeers.PeerId(2)}" }); -// var i_multiplexerSettings = new MultiplexerSettings(); -// var r_multiplexerSettings = new MultiplexerSettings(); -// r_multiplexerSettings.Add(proto2); -// r_multiplexerSettings.Add(proto1); -// i_multiplexerSettings.Add(proto1); -// NoiseProtocol proto_initiator = new(i_multiplexerSettings); -// NoiseProtocol proto_responder = new(r_multiplexerSettings); + TestChannel dialerUpChannel = new(); + dialerContext.Upgrade(Arg.Any()).Returns(dialerUpChannel); -// peerContext.LocalPeer.Identity.Returns(new Identity()); -// listenerContext.LocalPeer.Identity.Returns(new Identity()); + NoiseProtocol dialer = new(dialerSettings); -// string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); -// Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; -// peerContext.RemotePeer.Address.Returns(localAddr); + // Listener + MultiplexerSettings listenerSettings = new(); + listenerSettings.Add(proto1); -// string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); -// Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; -// listenerContext.RemotePeer.Address.Returns(listenerAddr); + IConnectionContext listenerContext = Substitute.For(); + listenerContext.Peer.Identity.Returns(TestPeers.Identity(2)); + listenerContext.Peer.ListenAddresses.Returns([TestPeers.Multiaddr(2)]); + listenerContext.State.Returns(new State() { RemoteAddress = $"/ip4/0.0.0.0/tcp/0/p2p/{TestPeers.PeerId(1)}" }); -// // Act -// Task listenTask = proto_responder.ListenAsync(downChannel, channelFactory, listenerContext); -// Task dialTask = proto_initiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); + TestChannel listenerUpChannel = new(); + listenerContext.Upgrade(Arg.Any()).Returns(listenerUpChannel); -// int sent = 42; -// ValueTask writeTask = upChannel.Reverse().WriteVarintAsync(sent); -// int received = await listenerUpChannel.Reverse().ReadVarintAsync(); -// await writeTask; + NoiseProtocol listener = new(listenerSettings); -// await upChannel.CloseAsync(); -// await listenerUpChannel.CloseAsync(); -// await downChannel.CloseAsync(); + // Act + Task listenTask = listener.ListenAsync(downChannel, listenerContext); + Task dialTask = dialer.DialAsync(downChannelFromProtocolPov, dialerContext); -// Assert.That(received, Is.EqualTo(sent)); -// } + int sent = 42; + ValueTask writeTask = dialerUpChannel.Reverse().WriteVarintAsync(sent); + int received = await listenerUpChannel.Reverse().ReadVarintAsync(); + await writeTask; -// [Test] -// public async Task Test_ConnectionEstablished_With_PreSelectedMuxer() -// { -// // Arrange -// IChannel downChannel = new TestChannel(); -// IChannel downChannelFromProtocolPov = ((TestChannel)downChannel).Reverse(); -// IChannelFactory channelFactory = Substitute.For(); -// IPeerContext peerContext = Substitute.For(); -// IPeerContext listenerContext = Substitute.For(); + await dialerUpChannel.CloseAsync(); + await listenerUpChannel.CloseAsync(); + await downChannel.CloseAsync(); -// IProtocol? proto1 = Substitute.For(); -// proto1.Id.Returns("proto1"); + await dialTask; + await listenTask; -// IProtocol? proto2 = Substitute.For(); -// proto2.Id.Returns("proto2"); - -// channelFactory.SubProtocols.Returns(new[] { proto1, proto2 }); - - -// var i_multiplexerSettings = new MultiplexerSettings(); -// var r_multiplexerSettings = new MultiplexerSettings(); -// r_multiplexerSettings.Add(proto2); -// r_multiplexerSettings.Add(proto1); -// i_multiplexerSettings.Add(proto1); - -// NoiseProtocol proto_initiator = new(i_multiplexerSettings); -// NoiseProtocol proto_responder = new(r_multiplexerSettings); - -// peerContext.LocalPeer.Identity.Returns(new Identity()); -// listenerContext.LocalPeer.Identity.Returns(new Identity()); -// string peerId = peerContext.LocalPeer.Identity.PeerId.ToString(); -// Multiaddress localAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{peerId}"; -// peerContext.RemotePeer.Address.Returns(localAddr); - -// string listenerPeerId = listenerContext.LocalPeer.Identity.PeerId.ToString(); -// Multiaddress listenerAddr = $"/ip4/0.0.0.0/tcp/0/p2p/{listenerPeerId}"; -// listenerContext.RemotePeer.Address.Returns(listenerAddr); - -// // Act -// Task listenTask = proto_responder.ListenAsync(downChannel, channelFactory, listenerContext); -// Task dialTask = proto_initiator.DialAsync(downChannelFromProtocolPov, channelFactory, peerContext); - -// await Task.Delay(TimeSpan.FromSeconds(2)); - -// // Assert -// Assert.That(peerContext.SpecificProtocolRequest.SubProtocol, Is.EqualTo(proto1)); - -// // Cleanup -// await downChannel.CloseAsync(); -// } -//} + Assert.That(received, Is.EqualTo(sent)); + } +} diff --git a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs index 69a2dab8..34598b41 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Noise/NoiseProtocol.cs @@ -11,7 +11,6 @@ using Multiformats.Address.Protocols; using Nethermind.Libp2p.Protocols.Noise.Dto; using PublicKey = Nethermind.Libp2p.Core.Dto.PublicKey; -using Nethermind.Libp2p.Core.Exceptions; namespace Nethermind.Libp2p.Protocols; @@ -40,10 +39,7 @@ public class NoiseProtocol(MultiplexerSettings? multiplexerSettings = null, ILog public async Task DialAsync(IChannel downChannel, IConnectionContext context) { - if (context.State.RemoteAddress is null) - { - throw new Libp2pException(); - } + ArgumentNullException.ThrowIfNull(context.State.RemoteAddress); KeyPair? clientStatic = KeyPair.Generate(); using HandshakeState? handshakeState = _protocol.Create(true, s: clientStatic.PrivateKey); @@ -124,10 +120,7 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) public async Task ListenAsync(IChannel downChannel, IConnectionContext context) { - if (context.State.RemoteAddress is null) - { - throw new Libp2pException(); - } + ArgumentNullException.ThrowIfNull(context.State.RemoteAddress); KeyPair? serverStatic = KeyPair.Generate(); using HandshakeState? handshakeState = @@ -253,9 +246,6 @@ private static Task ExchangeData(Transport transport, IChannel downChannel, ICha } }); - return Task.WhenAll(t, t2).ContinueWith((t) => - { - - }); + return Task.WhenAll(t, t2); } } diff --git a/src/libp2p/Libp2p.Protocols.Noise/README.md b/src/libp2p/Libp2p.Protocols.Noise/README.md index 5deb2726..ea8941e7 100644 --- a/src/libp2p/Libp2p.Protocols.Noise/README.md +++ b/src/libp2p/Libp2p.Protocols.Noise/README.md @@ -1,4 +1,4 @@ -# TLS protocol +# Noise protocol - [libp2p spec](https://github.com/libp2p/specs/blob/master/noise) - [The Noise Protocol Framework](https://www.noiseprotocol.org/noise.html) diff --git a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs index c677e688..3fa2178a 100644 --- a/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Ping/PingProtocol.cs @@ -4,7 +4,6 @@ using System.Buffers; using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Protocols.Ping; namespace Nethermind.Libp2p.Protocols; @@ -27,10 +26,7 @@ public PingProtocol(ILoggerFactory? loggerFactory = null) public async Task DialAsync(IChannel channel, ISessionContext context) { - if (context.State.RemoteAddress is null) - { - throw new Libp2pException(); - } + ArgumentNullException.ThrowIfNull(context.State.RemoteAddress); byte[] ping = new byte[PayloadLength]; _random.NextBytes(ping.AsSpan(0, PayloadLength)); @@ -56,10 +52,7 @@ public async Task DialAsync(IChannel channel, ISessionContext context) public async Task ListenAsync(IChannel channel, ISessionContext context) { - if (context.State.RemoteAddress is null) - { - throw new Libp2pException(); - } + ArgumentNullException.ThrowIfNull(context.State.RemoteAddress); _logger?.PingListenStarted(context.State.RemoteAddress); diff --git a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs index aa979b79..d38d582d 100644 --- a/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Plaintext/PlainTextProtocol.cs @@ -37,32 +37,3 @@ protected override async Task ConnectAsync(IChannel channel, IConnectionContext await context.Upgrade(channel); } } - -public class RelayHopProtocol : ISessionProtocol -{ - public string Id => "/libp2p/circuit/relay/0.2.0/hop"; - - public Task DialAsync(IChannel downChannel, ISessionContext context) - { - throw new NotImplementedException(); - } - - public Task ListenAsync(IChannel downChannel, ISessionContext context) - { - throw new NotImplementedException(); - } -} -public class RelayStopProtocol : ISessionProtocol -{ - public string Id => "/libp2p/circuit/relay/0.2.0/stop"; - - public Task DialAsync(IChannel downChannel, ISessionContext context) - { - throw new NotImplementedException(); - } - - public Task ListenAsync(IChannel downChannel, ISessionContext context) - { - throw new NotImplementedException(); - } -} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs index c4433534..e2701232 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/FloodsubProtocolTests.cs @@ -24,7 +24,7 @@ public async Task Test_Peer_is_in_fpeers() const string commonTopic = "topic1"; - IPeer peer = Substitute.For(); + ILocalPeer peer = Substitute.For(); peer.ListenAddresses.Returns([localPeerAddr]); peer.Identity.Returns(TestPeers.Identity(2)); peer.DialAsync(discoveredPeerAddress, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddress)); @@ -44,7 +44,7 @@ public async Task Test_Peer_is_in_fpeers() router.OutboundConnection(discoveredPeerAddress, PubsubRouter.FloodsubProtocolVersion, tcs.Task, sentRpcs.Add); router.InboundConnection(discoveredPeerAddress, PubsubRouter.FloodsubProtocolVersion, tcs.Task, tcs.Task, () => Task.CompletedTask); - await router.OnRpc(discoveredPeer.PeerId, new Rpc().WithTopics(new[] { commonTopic }, [])); + router.OnRpc(discoveredPeer.PeerId, new Rpc().WithTopics(new[] { commonTopic }, [])); Assert.Multiple(() => { diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs index c6e61fbe..e190360a 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/GossipsubProtocolTests.cs @@ -20,7 +20,7 @@ public async Task Test_New_messages_are_sent_to_mesh_only() int peerCount = PubsubSettings.Default.HighestDegree + 1; const string commonTopic = "topic1"; - IPeer peer = new LocalPeerStub(); + ILocalPeer peer = new LocalPeerStub(); List sentRpcs = []; router.GetTopic(commonTopic); @@ -37,7 +37,7 @@ public async Task Test_New_messages_are_sent_to_mesh_only() peerStore.Discover([discoveredPeer]); router.OutboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, sentRpcs.Add); router.InboundConnection(discoveredPeer, PubsubRouter.GossipsubProtocolVersionV10, tcs.Task, tcs.Task, () => Task.CompletedTask); - await router.OnRpc(peerId, new Rpc().WithTopics([commonTopic], [])); + router.OnRpc(peerId, new Rpc().WithTopics([commonTopic], [])); } await router.Heartbeat(); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs index 7160bd1d..ec222a15 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub.Tests/PubsubProtocolTests.cs @@ -19,7 +19,7 @@ public async Task Test_Peer_is_dialed_when_added_by_discovery() Multiaddress localPeerAddr = TestPeers.Multiaddr(1); Multiaddress[] discoveredPeerAddrs = [TestPeers.Multiaddr(2)]; - IPeer peer = Substitute.For(); + ILocalPeer peer = Substitute.For(); peer.ListenAddresses.Returns([localPeerAddr]); peer.Identity.Returns(TestPeers.Identity(1)); peer.DialAsync(discoveredPeerAddrs, Arg.Any()).Returns(new TestRemotePeer(discoveredPeerAddrs[0])); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/IRoutingStateContainer.cs b/src/libp2p/Libp2p.Protocols.Pubsub/IRoutingStateContainer.cs new file mode 100644 index 00000000..d5bf0ee7 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub/IRoutingStateContainer.cs @@ -0,0 +1,19 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; +using System.Collections.Concurrent; + +namespace Nethermind.Libp2p.Protocols.Pubsub; + +public interface IRoutingStateContainer +{ + ConcurrentDictionary> FloodsubPeers { get; } + ConcurrentDictionary> GossipsubPeers { get; } + ConcurrentDictionary> Mesh { get; } + ConcurrentDictionary> Fanout { get; } + ConcurrentDictionary FanoutLastPublished { get; } + ICollection ConnectedPeers { get; } + bool Started { get; } + Task Heartbeat(); +} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 9b21ed22..1d4951e8 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using Nethermind.Libp2p.Core; -using Nethermind.Libp2p.Core.Exceptions; using Nethermind.Libp2p.Protocols.Pubsub; using Nethermind.Libp2p.Protocols.Pubsub.Dto; @@ -28,12 +27,10 @@ public PubsubProtocol(string protocolId, PubsubRouter router, ILoggerFactory? lo public async Task DialAsync(IChannel channel, ISessionContext context) { - PeerId? remotePeerId = context.State.RemotePeerId ?? throw new Libp2pException(); + ArgumentNullException.ThrowIfNull(context.State.RemoteAddress); + ArgumentNullException.ThrowIfNull(context.State.RemotePeerId); - if (context.State.RemoteAddress is null) - { - throw new Libp2pException(); - } + PeerId? remotePeerId = context.State.RemotePeerId; _logger?.LogDebug($"Dialed({context.Id}) {context.State.RemoteAddress}"); @@ -58,12 +55,10 @@ public async Task DialAsync(IChannel channel, ISessionContext context) public async Task ListenAsync(IChannel channel, ISessionContext context) { - PeerId? remotePeerId = context.State.RemotePeerId ?? throw new Libp2pException(); + ArgumentNullException.ThrowIfNull(context.State.RemoteAddress); + ArgumentNullException.ThrowIfNull(context.State.RemotePeerId); - if (context.State.RemoteAddress is null) - { - throw new Libp2pException(); - } + PeerId? remotePeerId = context.State.RemotePeerId; _logger?.LogDebug($"Listen({context.Id}) to {context.State.RemoteAddress}"); diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 7e91c9b3..ba4a5629 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -11,18 +11,6 @@ namespace Nethermind.Libp2p.Protocols.Pubsub; -public interface IRoutingStateContainer -{ - ConcurrentDictionary> FloodsubPeers { get; } - ConcurrentDictionary> GossipsubPeers { get; } - ConcurrentDictionary> Mesh { get; } - ConcurrentDictionary> Fanout { get; } - ConcurrentDictionary FanoutLastPublished { get; } - ICollection ConnectedPeers { get; } - bool Started { get; } - Task Heartbeat(); -} - public partial class PubsubRouter : IRoutingStateContainer, IDisposable { static int routerCounter = 0; @@ -138,7 +126,7 @@ public Action? SendRpc private readonly TtlCache _limboMessageCache; private readonly TtlCache<(PeerId, MessageId)> _dontWantMessages; - private IPeer? localPeer; + private ILocalPeer? localPeer; private readonly ILogger? logger; // all floodsub peers in topics @@ -181,7 +169,7 @@ public PubsubRouter(PeerStore store, PubsubSettings? settings = null, ILoggerFac _dontWantMessages = new(_settings.MessageCacheTtl); } - public async Task StartAsync(IPeer localPeer, CancellationToken token = default) + public async Task StartAsync(ILocalPeer localPeer, CancellationToken token = default) { logger?.LogDebug($"Running pubsub for {string.Join(",", localPeer.ListenAddresses)}"); @@ -474,246 +462,280 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI } } - internal async Task OnRpc(PeerId peerId, Rpc rpc) + internal void OnRpc(PeerId peerId, Rpc rpc) { try { ConcurrentDictionary peerMessages = new(); lock (this) { - if (rpc.Publish.Any()) + if (rpc.Publish.Count != 0) { - logger?.LogDebug($"Messages received: {rpc.Publish.Select(_settings.GetMessageId).Count(messageId => _limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId))}/{rpc.Publish.Count}: {rpc.Publish.Count}"); - - foreach (Message? message in rpc.Publish) - { - MessageId messageId = _settings.GetMessageId(message); + HandleNewMessages(peerId, rpc.Publish, peerMessages); + } - if (_limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId)) - { - continue; - } + if (rpc.Subscriptions.Count != 0) + { + HandleSubscriptions(peerId, rpc.Subscriptions); + } - switch (VerifyMessage?.Invoke(message)) - { - case MessageValidity.Rejected: - case MessageValidity.Ignored: - _limboMessageCache.Add(messageId, message); - continue; - case MessageValidity.Trottled: - continue; - } + if (rpc.Control is not null) + { + if (rpc.Control.Graft.Count != 0) + { + HandleGraft(peerId, rpc.Control.Graft, peerMessages); + } - if (!message.VerifySignature(_settings.DefaultSignaturePolicy)) - { - _limboMessageCache!.Add(messageId, message); - continue; - } + if (rpc.Control.Prune.Count != 0) + { + HandlePrune(peerId, rpc.Control.Prune, peerMessages); + } - _messageCache.Add(messageId, message); + if (rpc.Control.Ihave.Count != 0) + { + HandleIhave(peerId, rpc.Control.Ihave, peerMessages); + } - PeerId author = new(message.From.ToArray()); - OnMessage?.Invoke(message.Topic, message.Data.ToByteArray()); + if (rpc.Control.Iwant.Count != 0) + { + HandleIwant(peerId, rpc.Control.Iwant, peerMessages); + } - if (fPeers.TryGetValue(message.Topic, out HashSet? topicPeers)) - { - foreach (PeerId peer in topicPeers) - { - if (peer == author || peer == peerId) - { - continue; - } - peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); - } - } - if (mesh.TryGetValue(message.Topic, out topicPeers)) - { - foreach (PeerId peer in topicPeers) - { - if (peer == author || peer == peerId) - { - continue; - } - peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); - } - } + if (rpc.Control.Idontwant.Count != 0) + { + HandleIdontwant(peerId, rpc.Control.Idontwant); } } + } + foreach (KeyValuePair peerMessage in peerMessages) + { + peerState.GetValueOrDefault(peerMessage.Key)?.Send(peerMessage.Value); + } + } + catch (Exception ex) + { + logger?.LogError(ex, "Exception while processing RPC"); + } + } - if (rpc.Subscriptions.Any()) + private void HandleNewMessages(PeerId peerId, IEnumerable messages, ConcurrentDictionary peerMessages) + { + logger?.LogDebug($"Messages received: {messages.Select(_settings.GetMessageId).Count(messageId => _limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId))}/{rpc.Publish.Count}: {rpc.Publish.Count}"); + + foreach (Message? message in messages) + { + MessageId messageId = _settings.GetMessageId(message); + + if (_limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId)) + { + continue; + } + + switch (VerifyMessage?.Invoke(message)) + { + case MessageValidity.Rejected: + case MessageValidity.Ignored: + _limboMessageCache.Add(messageId, message); + continue; + case MessageValidity.Trottled: + continue; + } + + if (!message.VerifySignature(_settings.DefaultSignaturePolicy)) + { + _limboMessageCache!.Add(messageId, message); + continue; + } + + _messageCache.Add(messageId, message); + + PeerId author = new(message.From.ToArray()); + OnMessage?.Invoke(message.Topic, message.Data.ToByteArray()); + + if (fPeers.TryGetValue(message.Topic, out HashSet? topicPeers)) + { + foreach (PeerId peer in topicPeers) { - foreach (Rpc.Types.SubOpts? sub in rpc.Subscriptions) + if (peer == author || peer == peerId) { - PubsubPeer? state = peerState.GetValueOrDefault(peerId); - if (state is null) - { - return; - } - if (sub.Subscribe) - { - if (state.IsGossipSub) - { - gPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); - } - else if (state.IsFloodSub) - { - fPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); - } - } - else - { - if (state.IsGossipSub) - { - gPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); - if (mesh.ContainsKey(sub.Topicid)) - { - mesh[sub.Topicid].Remove(peerId); - } - if (fanout.ContainsKey(sub.Topicid)) - { - fanout[sub.Topicid].Remove(peerId); - } - } - else if (state.IsFloodSub) - { - fPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); - } - } + continue; } + peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); } - - if (rpc.Control is not null) + } + if (mesh.TryGetValue(message.Topic, out topicPeers)) + { + foreach (PeerId peer in topicPeers) { - if (rpc.Control.Graft.Any()) + if (peer == author || peer == peerId) { - foreach (ControlGraft? graft in rpc.Control.Graft) - { - if (!topicState.ContainsKey(graft.TopicID)) - { - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Prune) - .Add(new ControlPrune { TopicID = graft.TopicID }); - } - else - { - HashSet topicMesh = mesh[graft.TopicID]; - - if (topicMesh.Count >= _settings.HighestDegree) - { - ControlPrune prune = new() { TopicID = graft.TopicID }; - - if (peerState.TryGetValue(peerId, out PubsubPeer? state) && state.IsGossipSub && state.Protocol >= PubsubPeer.PubsubProtocol.GossipsubV11) - { - state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); - prune.Peers.AddRange(topicMesh.ToArray().Select(pid => (PeerId: pid, Record: _peerStore.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo - { - PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), - SignedPeerRecord = pid.Record, - })); - } - - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Prune) - .Add(prune); - } - else - { - if (!topicMesh.Contains(peerId)) - { - topicMesh.Add(peerId); - gPeers[graft.TopicID].Add(peerId); - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Graft) - .Add(new ControlGraft { TopicID = graft.TopicID }); - } - } - } - } + continue; } + peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); + } + } + } + } - if (rpc.Control.Prune.Any()) + private void HandleSubscriptions(PeerId peerId, IEnumerable subscriptions) + { + foreach (Rpc.Types.SubOpts? sub in subscriptions) + { + PubsubPeer? state = peerState.GetValueOrDefault(peerId); + if (state is null) + { + return; + } + if (sub.Subscribe) + { + if (state.IsGossipSub) + { + gPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); + } + else if (state.IsFloodSub) + { + fPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); + } + } + else + { + if (state.IsGossipSub) + { + gPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); + if (mesh.ContainsKey(sub.Topicid)) { - foreach (ControlPrune? prune in rpc.Control.Prune) - { - if (topicState.ContainsKey(prune.TopicID) && mesh[prune.TopicID].Contains(peerId)) - { - if (peerState.TryGetValue(peerId, out PubsubPeer? state)) - { - state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); - } - mesh[prune.TopicID].Remove(peerId); - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Prune) - .Add(new ControlPrune { TopicID = prune.TopicID }); - - foreach (PeerInfo? peer in prune.Peers) - { - _peerStore.Discover(peer.SignedPeerRecord); - } - } - } + mesh[sub.Topicid].Remove(peerId); } - - if (rpc.Control.Ihave.Any()) + if (fanout.ContainsKey(sub.Topicid)) { - List messageIds = []; + fanout[sub.Topicid].Remove(peerId); + } + } + else if (state.IsFloodSub) + { + fPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); + } + } + } + } - foreach (ControlIHave? ihave in rpc.Control.Ihave - .Where(iw => topicState.ContainsKey(iw.TopicID))) - { - messageIds.AddRange(ihave.MessageIDs.Select(m => new MessageId(m.ToByteArray())) - .Where(mid => !_messageCache.Contains(mid))); - } + private void HandleGraft(PeerId peerId, IEnumerable grafts, ConcurrentDictionary peerMessages) + { + foreach (ControlGraft? graft in grafts) + { + if (!topicState.ContainsKey(graft.TopicID)) + { + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Prune) + .Add(new ControlPrune { TopicID = graft.TopicID }); + } + else + { + HashSet topicMesh = mesh[graft.TopicID]; - if (messageIds.Any()) - { - ControlIWant ciw = new(); - foreach (MessageId mId in messageIds) - { - ciw.MessageIDs.Add(ByteString.CopyFrom(mId.Bytes)); - } - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Iwant) - .Add(ciw); - } - } + if (topicMesh.Count >= _settings.HighestDegree) + { + ControlPrune prune = new() { TopicID = graft.TopicID }; - if (rpc.Control.Iwant.Any()) + if (peerState.TryGetValue(peerId, out PubsubPeer? state) && state.IsGossipSub && state.Protocol >= PubsubPeer.PubsubProtocol.GossipsubV11) { - IEnumerable messageIds = rpc.Control.Iwant.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())); - List messages = []; - foreach (MessageId? mId in messageIds) - { - Message message = _messageCache.Get(mId); - if (message != default) - { - messages.Add(message); - } - } - if (messages.Any()) + state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); + prune.Peers.AddRange(topicMesh.ToArray().Select(pid => (PeerId: pid, Record: _peerStore.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo { - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Publish.AddRange(messages); - } + PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), + SignedPeerRecord = pid.Record, + })); } - if (rpc.Control.Idontwant.Any()) + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Prune) + .Add(prune); + } + else + { + if (!topicMesh.Contains(peerId)) { - foreach (MessageId messageId in rpc.Control.Iwant.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())).Take(_settings.MaxIdontwantMessages)) - { - _dontWantMessages.Add((peerId, messageId)); - } + topicMesh.Add(peerId); + gPeers[graft.TopicID].Add(peerId); + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Graft) + .Add(new ControlGraft { TopicID = graft.TopicID }); } } } - foreach (KeyValuePair peerMessage in peerMessages) + } + } + + private void HandlePrune(PeerId peerId, IEnumerable prunes, ConcurrentDictionary peerMessages) + { + foreach (ControlPrune? prune in prunes) + { + if (topicState.ContainsKey(prune.TopicID) && mesh[prune.TopicID].Contains(peerId)) { - peerState.GetValueOrDefault(peerMessage.Key)?.Send(peerMessage.Value); + if (peerState.TryGetValue(peerId, out PubsubPeer? state)) + { + state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); + } + mesh[prune.TopicID].Remove(peerId); + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Prune) + .Add(new ControlPrune { TopicID = prune.TopicID }); + + foreach (PeerInfo? peer in prune.Peers) + { + _peerStore.Discover(peer.SignedPeerRecord); + } } } - catch (Exception ex) + } + + private void HandleIhave(PeerId peerId, IEnumerable ihaves, ConcurrentDictionary peerMessages) + { + List messageIds = []; + + foreach (ControlIHave? ihave in ihaves.Where(iw => topicState.ContainsKey(iw.TopicID))) { - logger?.LogError(ex, "Exception while processing RPC"); + messageIds.AddRange(ihave.MessageIDs.Select(m => new MessageId(m.ToByteArray())) + .Where(mid => !_messageCache.Contains(mid))); + } + + if (messageIds.Any()) + { + ControlIWant ciw = new(); + foreach (MessageId mId in messageIds) + { + ciw.MessageIDs.Add(ByteString.CopyFrom(mId.Bytes)); + } + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Iwant) + .Add(ciw); + } + } + + private void HandleIwant(PeerId peerId, IEnumerable iwants, ConcurrentDictionary peerMessages) + { + IEnumerable messageIds = iwants.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())); + List messages = []; + foreach (MessageId? mId in messageIds) + { + Message message = _messageCache.Get(mId); + if (message != default) + { + messages.Add(message); + } + } + if (messages.Any()) + { + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Publish.AddRange(messages); + } + } + + private void HandleIdontwant(PeerId peerId, IEnumerable idontwants) + { + foreach (MessageId messageId in idontwants.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())).Take(_settings.MaxIdontwantMessages)) + { + _dontWantMessages.Add((peerId, messageId)); } } } diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs index 28327853..560020f4 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery.E2eTests/PubsubDiscoveryE2eTestSetup.cs @@ -31,6 +31,6 @@ protected override void AddAt(int index) base.AddAt(index); Discovery[index] = new PubsubPeerDiscoveryProtocol(Routers[index], PeerStores[index], DefaultDiscoverySettings, Peers[index], loggerFactory); - _ = Discovery[index].DiscoverAsync(Peers[index].ListenAddresses, Token); + _ = Discovery[index].StartDiscoveryAsync(Peers[index].ListenAddresses, Token); } } diff --git a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs index 523adcda..591f05ea 100644 --- a/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.PubsubPeerDiscovery/PubsubPeerDiscoveryProtocol.cs @@ -6,7 +6,7 @@ namespace Nethermind.Libp2p.Protocols; -public class PubsubPeerDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore peerStore, PubsubPeerDiscoverySettings settings, IPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol +public class PubsubPeerDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore peerStore, PubsubPeerDiscoverySettings settings, ILocalPeer peer, ILoggerFactory? loggerFactory = null) : IDiscoveryProtocol { private readonly PubsubRouter _pubSubRouter = pubSubRouter; private IReadOnlyList? _localPeerAddrs; @@ -15,12 +15,11 @@ public class PubsubPeerDiscoveryProtocol(PubsubRouter pubSubRouter, PeerStore pe private readonly PubsubPeerDiscoverySettings _settings = settings; private readonly ILogger? logger = loggerFactory?.CreateLogger(); - public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, CancellationToken token = default) + public Task StartDiscoveryAsync(IReadOnlyList localPeerAddrs, CancellationToken token = default) { _localPeerAddrs = localPeerAddrs; localPeerId = localPeerAddrs.First().GetPeerId(); - topics = _settings.Topics.Select(topic => { ITopic subscription = _pubSubRouter.GetTopic(topic); @@ -38,11 +37,18 @@ public async Task DiscoverAsync(IReadOnlyList localPeerAddrs, Canc if (!_settings.ListenOnly) { - while (!token.IsCancellationRequested) - { - await Task.Delay(_settings.Interval, token); - BroadcastPeerInfo(); - } + _ = RunAsync(token); + } + + return Task.CompletedTask; + } + + private async Task RunAsync(CancellationToken token) + { + while (!token.IsCancellationRequested) + { + await Task.Delay(_settings.Interval, token); + BroadcastPeerInfo(); } } @@ -50,7 +56,7 @@ internal void BroadcastPeerInfo() { if (topics is null) { - throw new NullReferenceException($"{nameof(topics)} should be previously set in ${nameof(DiscoverAsync)}"); + throw new NullReferenceException($"{nameof(topics)} should be previously set in ${nameof(StartDiscoveryAsync)}"); } foreach (ITopic topic in topics) diff --git a/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs b/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs new file mode 100644 index 00000000..6b32d93d --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs @@ -0,0 +1,307 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: Exchange.proto +// +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Nethermind.Libp2p.Protocols.PlainText.Dto { + + /// Holder for reflection information generated from Exchange.proto + public static partial class ExchangeReflection { + + #region Descriptor + /// File descriptor for Exchange.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static ExchangeReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "Cg5FeGNoYW5nZS5wcm90byImCghFeGNoYW5nZRIKCgJpZBgBIAEoDBIOCgZw", + "dWJrZXkYAiABKAxCLKoCKU5ldGhlcm1pbmQuTGlicDJwLlByb3RvY29scy5Q", + "bGFpblRleHQuRHRv")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Protocols.PlainText.Dto.Exchange), global::Nethermind.Libp2p.Protocols.PlainText.Dto.Exchange.Parser, new[]{ "Id", "Pubkey" }, null, null, null, null) + })); + } + #endregion + + } + #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class Exchange : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Exchange()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Nethermind.Libp2p.Protocols.PlainText.Dto.ExchangeReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Exchange() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Exchange(Exchange other) : this() { + id_ = other.id_; + pubkey_ = other.pubkey_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Exchange Clone() { + return new Exchange(this); + } + + /// Field number for the "id" field. + public const int IdFieldNumber = 1; + private readonly static pb::ByteString IdDefaultValue = pb::ByteString.Empty; + + private pb::ByteString id_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pb::ByteString Id { + get { return id_ ?? IdDefaultValue; } + set { + id_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + /// Gets whether the "id" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool HasId { + get { return id_ != null; } + } + /// Clears the value of the "id" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void ClearId() { + id_ = null; + } + + /// Field number for the "pubkey" field. + public const int PubkeyFieldNumber = 2; + private readonly static pb::ByteString PubkeyDefaultValue = pb::ByteString.Empty; + + private pb::ByteString pubkey_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pb::ByteString Pubkey { + get { return pubkey_ ?? PubkeyDefaultValue; } + set { + pubkey_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + /// Gets whether the "pubkey" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool HasPubkey { + get { return pubkey_ != null; } + } + /// Clears the value of the "pubkey" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void ClearPubkey() { + pubkey_ = null; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Exchange); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Exchange other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Id != other.Id) return false; + if (Pubkey != other.Pubkey) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (HasId) hash ^= Id.GetHashCode(); + if (HasPubkey) hash ^= Pubkey.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (HasId) { + output.WriteRawTag(10); + output.WriteBytes(Id); + } + if (HasPubkey) { + output.WriteRawTag(18); + output.WriteBytes(Pubkey); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (HasId) { + output.WriteRawTag(10); + output.WriteBytes(Id); + } + if (HasPubkey) { + output.WriteRawTag(18); + output.WriteBytes(Pubkey); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (HasId) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(Id); + } + if (HasPubkey) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(Pubkey); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Exchange other) { + if (other == null) { + return; + } + if (other.HasId) { + Id = other.Id; + } + if (other.HasPubkey) { + Pubkey = other.Pubkey; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + Id = input.ReadBytes(); + break; + } + case 18: { + Pubkey = input.ReadBytes(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + Id = input.ReadBytes(); + break; + } + case 18: { + Pubkey = input.ReadBytes(); + break; + } + } + } + } + #endif + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto b/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto new file mode 100644 index 00000000..078e675c --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +option csharp_namespace = "Nethermind.Libp2p.Protocols.PlainText.Dto"; + +message Exchange { + optional bytes id = 1; + optional bytes pubkey = 2; +} \ No newline at end of file diff --git a/src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj b/src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj new file mode 100644 index 00000000..8c452f4d --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj @@ -0,0 +1,37 @@ + + + + enable + enable + latest + Nethermind.$(MSBuildProjectName) + Nethermind.$(MSBuildProjectName.Replace(" ", "_")) + + + + README.md + libp2p network plaintext + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/libp2p/Libp2p.Protocols.Relay/README.md b/src/libp2p/Libp2p.Protocols.Relay/README.md new file mode 100644 index 00000000..83546086 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Relay/README.md @@ -0,0 +1,3 @@ +# Relay protocols - WIP + +See the [libp2p spec](https://github.com/libp2p/specs/tree/master/relay) diff --git a/src/libp2p/Libp2p.Protocols.Relay/RelayHopProtocol.cs b/src/libp2p/Libp2p.Protocols.Relay/RelayHopProtocol.cs new file mode 100644 index 00000000..868e6862 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Relay/RelayHopProtocol.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; + +namespace Nethermind.Libp2p.Protocols; + +public class RelayHopProtocol : ISessionProtocol +{ + public string Id => "/libp2p/circuit/relay/0.2.0/hop"; + + public Task DialAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } + + public Task ListenAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } +} diff --git a/src/libp2p/Libp2p.Protocols.Relay/RelayStopProtocol.cs b/src/libp2p/Libp2p.Protocols.Relay/RelayStopProtocol.cs new file mode 100644 index 00000000..b7dd9531 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Relay/RelayStopProtocol.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Nethermind.Libp2p.Core; + +namespace Nethermind.Libp2p.Protocols; + +public class RelayStopProtocol : ISessionProtocol +{ + public string Id => "/libp2p/circuit/relay/0.2.0/stop"; + + public Task DialAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } + + public Task ListenAsync(IChannel downChannel, ISessionContext context) + { + throw new NotImplementedException(); + } +} diff --git a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs index 8ad134ad..388da7dc 100644 --- a/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Tls/TlsProtocol.cs @@ -57,9 +57,10 @@ public async Task ListenAsync(IChannel downChannel, IConnectionContext context) await ExchangeData(sslStream, upChannel, _logger); _ = upChannel.CloseAsync(); } - catch (Exception e) + catch (Exception ex) { - + _logger?.LogError(ex, "Error during TLS protocol negotiation."); + throw; } } @@ -112,9 +113,10 @@ public async Task DialAsync(IChannel downChannel, IConnectionContext context) _logger?.LogDebug("Connection closed for PeerId {RemotePeerId}.", context.State.RemotePeerId); _ = upChannel.CloseAsync(); } - catch (Exception e) + catch (Exception ex) { - + _logger?.LogError(ex, "Error during TLS protocol negotiation."); + throw; } } @@ -145,6 +147,7 @@ private static async Task ExchangeData(SslStream sslStream, IChannel upChannel, await upChannel.CloseAsync(); } }); + Task readTask = Task.Run(async () => { try @@ -177,6 +180,7 @@ private static async Task ExchangeData(SslStream sslStream, IChannel upChannel, logger?.LogError(ex, "Error while reading from sslStream"); } }); + await Task.WhenAll(writeTask, readTask); } } diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index a1da66b1..60494d35 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -80,6 +80,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.E2eTests", "Libp2p.E EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.Protocols.PubsubPeerDiscovery.E2eTests", "Libp2p.Protocols.PubsubPeerDiscovery.E2eTests\Libp2p.Protocols.PubsubPeerDiscovery.E2eTests.csproj", "{EC0B1626-C006-4138-A119-FE61CDAB824D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.Protocols.Relay", "Libp2p.Protocols.Relay\Libp2p.Protocols.Relay.csproj", "{F29F5376-4F93-486F-B933-3278177704DE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -214,6 +216,10 @@ Global {EC0B1626-C006-4138-A119-FE61CDAB824D}.Debug|Any CPU.Build.0 = Debug|Any CPU {EC0B1626-C006-4138-A119-FE61CDAB824D}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC0B1626-C006-4138-A119-FE61CDAB824D}.Release|Any CPU.Build.0 = Release|Any CPU + {F29F5376-4F93-486F-B933-3278177704DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F29F5376-4F93-486F-B933-3278177704DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F29F5376-4F93-486F-B933-3278177704DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F29F5376-4F93-486F-B933-3278177704DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -243,6 +249,7 @@ Global {C3CDBAAE-C790-443A-A293-D6E2330160F7} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {89BD907E-1399-4BE7-98CC-E541EAB21842} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {EC0B1626-C006-4138-A119-FE61CDAB824D} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} + {F29F5376-4F93-486F-B933-3278177704DE} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E337E37C-3DB8-42FA-9A83-AC4E3B2557B4} diff --git a/src/libp2p/Libp2p/Libp2pPeerFactory.cs b/src/libp2p/Libp2p/Libp2pPeerFactory.cs index 644dc068..5f5319b1 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactory.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactory.cs @@ -10,7 +10,7 @@ namespace Nethermind.Libp2p; public class Libp2pPeerFactory(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, ILoggerFactory? loggerFactory = null) : PeerFactory(protocolStackSettings, peerStore, loggerFactory) { - public override IPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, PeerStore, identity ?? new Identity(), LoggerFactory); + public override ILocalPeer Create(Identity? identity = null) => new Libp2pPeer(protocolStackSettings, PeerStore, identity ?? new Identity(), LoggerFactory); } class Libp2pPeer(IProtocolStackSettings protocolStackSettings, PeerStore peerStore, Identity identity, ILoggerFactory? loggerFactory = null) : LocalPeer(identity, peerStore, protocolStackSettings, loggerFactory) diff --git a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs index 56c665e3..79a53b07 100644 --- a/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs +++ b/src/libp2p/Libp2p/Libp2pPeerFactoryBuilder.cs @@ -38,7 +38,7 @@ public ILibp2pPeerFactoryBuilder WithQuic() return this; } - protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) + protected override ProtocolRef[] BuildStack(IEnumerable additionalProtocols) { ProtocolRef tcp = Get(); diff --git a/src/samples/chat/Program.cs b/src/samples/chat/Program.cs index 9512e337..c6d94047 100644 --- a/src/samples/chat/Program.cs +++ b/src/samples/chat/Program.cs @@ -32,7 +32,7 @@ "/ip4/0.0.0.0/udp/0/quic-v1" : "/ip4/0.0.0.0/tcp/0"; - IPeer localPeer = peerFactory.Create(); + ILocalPeer localPeer = peerFactory.Create(); logger.LogInformation("Dialing {remote}", remoteAddr); ISession remotePeer = await localPeer.DialAsync(remoteAddr, ts.Token); @@ -43,7 +43,7 @@ else { Identity optionalFixedIdentity = new(Enumerable.Repeat((byte)42, 32).ToArray()); - IPeer peer = peerFactory.Create(optionalFixedIdentity); + ILocalPeer peer = peerFactory.Create(optionalFixedIdentity); string addrTemplate = args.Contains("-quic") ? "/ip4/0.0.0.0/udp/{0}/quic-v1" : diff --git a/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs b/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs index fe90774c..999c20f4 100644 --- a/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs +++ b/src/samples/perf-benchmarks/NoStackPeerFactoryBuilder.cs @@ -16,7 +16,7 @@ public NoStackPeerFactoryBuilder() : base(default) public static Libp2pPeerFactoryBuilder Create => new(); - protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) + protected override ProtocolRef[] BuildStack(IEnumerable additionalProtocols) { return [Get()]; } diff --git a/src/samples/perf-benchmarks/Program.cs b/src/samples/perf-benchmarks/Program.cs index c5a0a335..d2b3f7a5 100644 --- a/src/samples/perf-benchmarks/Program.cs +++ b/src/samples/perf-benchmarks/Program.cs @@ -18,12 +18,12 @@ IPeerFactory peerFactory = serviceProvider.GetService()!; Identity optionalFixedIdentity = new(Enumerable.Repeat((byte)42, 32).ToArray()); - IPeer peer = peerFactory.Create(optionalFixedIdentity); + ILocalPeer peer = peerFactory.Create(optionalFixedIdentity); await peer.StartListenAsync([$"/ip4/0.0.0.0/tcp/0/p2p/{peer.Identity.PeerId}"]); Multiaddress remoteAddr = peer.ListenAddresses.First(); - IPeer localPeer = peerFactory.Create(); + ILocalPeer localPeer = peerFactory.Create(); ISession remotePeer = await localPeer.DialAsync(remoteAddr); Stopwatch timeSpent = Stopwatch.StartNew(); @@ -41,11 +41,11 @@ .AddAppLayerProtocol() .Build(); - IPeer peer = peerFactory.Create(); + ILocalPeer peer = peerFactory.Create(); await peer.StartListenAsync([$"/ip4/0.0.0.0/tcp/0"]); Multiaddress remoteAddr = peer.ListenAddresses.First(); - IPeer localPeer = peerFactory.Create(); + ILocalPeer localPeer = peerFactory.Create(); ISession remotePeer = await localPeer.DialAsync(remoteAddr); Stopwatch timeSpent = Stopwatch.StartNew(); diff --git a/src/samples/pubsub-chat/Program.cs b/src/samples/pubsub-chat/Program.cs index 4daa6dea..89e67d1c 100644 --- a/src/samples/pubsub-chat/Program.cs +++ b/src/samples/pubsub-chat/Program.cs @@ -31,7 +31,7 @@ Identity localPeerIdentity = new(); string addr = $"/ip4/0.0.0.0/tcp/0/p2p/{localPeerIdentity.PeerId}"; -IPeer peer = peerFactory.Create(localPeerIdentity); +ILocalPeer peer = peerFactory.Create(localPeerIdentity); PubsubRouter router = serviceProvider.GetService()!; ITopic topic = router.GetTopic("chat-room:awesome-chat-room"); @@ -55,7 +55,7 @@ await peer.StartListenAsync([addr], ts.Token); string peerId = peer.Identity.PeerId.ToString(); -_ = serviceProvider.GetService()!.DiscoverAsync(peer.ListenAddresses, token: ts.Token); +_ = serviceProvider.GetService()!.StartDiscoveryAsync(peer.ListenAddresses, token: ts.Token); await router.StartAsync(peer, token: ts.Token); diff --git a/src/samples/transport-interop/Program.cs b/src/samples/transport-interop/Program.cs index 10007b2a..62cc206d 100644 --- a/src/samples/transport-interop/Program.cs +++ b/src/samples/transport-interop/Program.cs @@ -34,7 +34,7 @@ if (isDialer) { - IPeer localPeer = peerFactory.Create(); + ILocalPeer localPeer = peerFactory.Create(); Log($"Picking an address to dial..."); @@ -82,7 +82,7 @@ ip = addresses.First().Address.ToString()!; } Log("Starting to listen..."); - IPeer localPeer = peerFactory.Create(); + ILocalPeer localPeer = peerFactory.Create(); CancellationTokenSource listennTcs = new(); await localPeer.StartListenAsync([builder.MakeAddress(ip)], listennTcs.Token); @@ -130,7 +130,7 @@ public TestPlansPeerFactoryBuilder(string transport, string? muxer, string? secu private static readonly string[] stacklessProtocols = ["quic", "quic-v1", "webtransport"]; - protected override ProtocolRef[] BuildStack(ProtocolRef[] additionalProtocols) + protected override ProtocolRef[] BuildStack(IEnumerable additionalProtocols) { ProtocolRef[] transportStack = [transport switch { From 0c233a2bc60ea7a3bf6b58d18859f4ac29b9281b Mon Sep 17 00:00:00 2001 From: Alexey Date: Mon, 16 Dec 2024 15:53:38 +0300 Subject: [PATCH 23/25] Update src/libp2p/Libp2p.Core/Peer.cs Co-authored-by: Marc --- src/libp2p/Libp2p.Core/Peer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libp2p/Libp2p.Core/Peer.cs b/src/libp2p/Libp2p.Core/Peer.cs index 62d09e6b..0cc4acf9 100644 --- a/src/libp2p/Libp2p.Core/Peer.cs +++ b/src/libp2p/Libp2p.Core/Peer.cs @@ -194,7 +194,7 @@ internal IEnumerable GetProtocolsFor(ProtocolRef protocol) } - // TODO: Remove lloking in the entire stack, look only on level for the given parent protocol + // TODO: Remove locking in the entire stack, look only on level for the given parent protocol internal IProtocol? GetProtocolInstance() { return _protocolStackSettings.Protocols?.Keys.FirstOrDefault(p => p.Protocol.GetType() == typeof(TProtocol))?.Protocol; From 71e6c6e409fb8d59b34040432a8a941c89c6e278 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 16 Dec 2024 15:58:46 +0300 Subject: [PATCH 24/25] Fix compilation --- src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs | 2 +- src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs | 2 +- src/libp2p/Libp2p/Libp2p.csproj | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs index 1d4951e8..e2752e5f 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubProtocol.cs @@ -82,7 +82,7 @@ public async Task ListenAsync(IChannel channel, ISessionContext context) else { _logger?.LogTrace($"Received message from {remotePeerId}: {rpc}"); - _ = router.OnRpc(remotePeerId, rpc); + router.OnRpc(remotePeerId, rpc); } } diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index ba4a5629..9574cf25 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -520,7 +520,7 @@ internal void OnRpc(PeerId peerId, Rpc rpc) private void HandleNewMessages(PeerId peerId, IEnumerable messages, ConcurrentDictionary peerMessages) { - logger?.LogDebug($"Messages received: {messages.Select(_settings.GetMessageId).Count(messageId => _limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId))}/{rpc.Publish.Count}: {rpc.Publish.Count}"); + logger?.LogDebug($"Messages received: {messages.Select(_settings.GetMessageId).Count(messageId => _limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId))}/{messages.Count()}"); foreach (Message? message in messages) { diff --git a/src/libp2p/Libp2p/Libp2p.csproj b/src/libp2p/Libp2p/Libp2p.csproj index 80c6613c..cc739c23 100644 --- a/src/libp2p/Libp2p/Libp2p.csproj +++ b/src/libp2p/Libp2p/Libp2p.csproj @@ -23,6 +23,7 @@ + From 1e8d8be3b3c9d8e4ae0bc60bb80bebc407bcdb50 Mon Sep 17 00:00:00 2001 From: Alexey Osipov Date: Mon, 16 Dec 2024 20:24:01 +0300 Subject: [PATCH 25/25] Fix tests --- src/libp2p/Directory.Packages.props | 2 +- .../PubsubRouter.Rpc.cs | 290 +++++++++++++++++ .../Libp2p.Protocols.Pubsub/PubsubRouter.cs | 287 +--------------- .../Libp2p.Protocols.Relay/Dto/Exchange.cs | 307 ------------------ .../Libp2p.Protocols.Relay/Dto/Exchange.proto | 8 - .../Libp2p.Protocols.Relay.csproj | 4 - src/libp2p/Libp2p.sln | 14 +- .../transport-interop/packages.lock.json | 6 +- 8 files changed, 306 insertions(+), 612 deletions(-) create mode 100644 src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Rpc.cs delete mode 100644 src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs delete mode 100644 src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto diff --git a/src/libp2p/Directory.Packages.props b/src/libp2p/Directory.Packages.props index bdb7f4a2..40d639fe 100644 --- a/src/libp2p/Directory.Packages.props +++ b/src/libp2p/Directory.Packages.props @@ -23,7 +23,7 @@ - + diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Rpc.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Rpc.cs new file mode 100644 index 00000000..5eb582d0 --- /dev/null +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.Rpc.cs @@ -0,0 +1,290 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: MIT + +using Google.Protobuf; +using Microsoft.Extensions.Logging; +using Nethermind.Libp2p.Core; +using Nethermind.Libp2p.Protocols.Pubsub.Dto; +using System.Collections.Concurrent; + +namespace Nethermind.Libp2p.Protocols.Pubsub; + +public partial class PubsubRouter : IRoutingStateContainer, IDisposable +{ + internal void OnRpc(PeerId peerId, Rpc rpc) + { + try + { + ConcurrentDictionary peerMessages = new(); + lock (this) + { + if (rpc.Publish.Count != 0) + { + HandleNewMessages(peerId, rpc.Publish, peerMessages); + } + + if (rpc.Subscriptions.Count != 0) + { + HandleSubscriptions(peerId, rpc.Subscriptions); + } + + if (rpc.Control is not null) + { + if (rpc.Control.Graft.Count != 0) + { + HandleGraft(peerId, rpc.Control.Graft, peerMessages); + } + + if (rpc.Control.Prune.Count != 0) + { + HandlePrune(peerId, rpc.Control.Prune, peerMessages); + } + + if (rpc.Control.Ihave.Count != 0) + { + HandleIhave(peerId, rpc.Control.Ihave, peerMessages); + } + + if (rpc.Control.Iwant.Count != 0) + { + HandleIwant(peerId, rpc.Control.Iwant, peerMessages); + } + + if (rpc.Control.Idontwant.Count != 0) + { + HandleIdontwant(peerId, rpc.Control.Idontwant); + } + } + } + foreach (KeyValuePair peerMessage in peerMessages) + { + peerState.GetValueOrDefault(peerMessage.Key)?.Send(peerMessage.Value); + } + } + catch (Exception ex) + { + logger?.LogError(ex, "Exception while processing RPC"); + } + } + + private void HandleNewMessages(PeerId peerId, IEnumerable messages, ConcurrentDictionary peerMessages) + { + logger?.LogDebug($"Messages received: {messages.Select(_settings.GetMessageId).Count(messageId => _limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId))}/{messages.Count()}"); + + foreach (Message? message in messages) + { + MessageId messageId = _settings.GetMessageId(message); + + if (_limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId)) + { + continue; + } + + switch (VerifyMessage?.Invoke(message)) + { + case MessageValidity.Rejected: + case MessageValidity.Ignored: + _limboMessageCache.Add(messageId, message); + continue; + case MessageValidity.Trottled: + continue; + } + + if (!message.VerifySignature(_settings.DefaultSignaturePolicy)) + { + _limboMessageCache!.Add(messageId, message); + continue; + } + + _messageCache.Add(messageId, message); + + PeerId author = new(message.From.ToArray()); + OnMessage?.Invoke(message.Topic, message.Data.ToByteArray()); + + if (fPeers.TryGetValue(message.Topic, out HashSet? topicPeers)) + { + foreach (PeerId peer in topicPeers) + { + if (peer == author || peer == peerId) + { + continue; + } + peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); + } + } + if (mesh.TryGetValue(message.Topic, out topicPeers)) + { + foreach (PeerId peer in topicPeers) + { + if (peer == author || peer == peerId) + { + continue; + } + peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); + } + } + } + } + + private void HandleSubscriptions(PeerId peerId, IEnumerable subscriptions) + { + foreach (Rpc.Types.SubOpts? sub in subscriptions) + { + PubsubPeer? state = peerState.GetValueOrDefault(peerId); + if (state is null) + { + return; + } + if (sub.Subscribe) + { + if (state.IsGossipSub) + { + gPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); + } + else if (state.IsFloodSub) + { + fPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); + } + } + else + { + if (state.IsGossipSub) + { + gPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); + if (mesh.ContainsKey(sub.Topicid)) + { + mesh[sub.Topicid].Remove(peerId); + } + if (fanout.ContainsKey(sub.Topicid)) + { + fanout[sub.Topicid].Remove(peerId); + } + } + else if (state.IsFloodSub) + { + fPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); + } + } + } + } + + private void HandleGraft(PeerId peerId, IEnumerable grafts, ConcurrentDictionary peerMessages) + { + foreach (ControlGraft? graft in grafts) + { + if (!topicState.ContainsKey(graft.TopicID)) + { + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Prune) + .Add(new ControlPrune { TopicID = graft.TopicID }); + } + else + { + HashSet topicMesh = mesh[graft.TopicID]; + + if (topicMesh.Count >= _settings.HighestDegree) + { + ControlPrune prune = new() { TopicID = graft.TopicID }; + + if (peerState.TryGetValue(peerId, out PubsubPeer? state) && state.IsGossipSub && state.Protocol >= PubsubPeer.PubsubProtocol.GossipsubV11) + { + state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); + prune.Peers.AddRange(topicMesh.ToArray().Select(pid => (PeerId: pid, Record: _peerStore.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo + { + PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), + SignedPeerRecord = pid.Record, + })); + } + + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Prune) + .Add(prune); + } + else + { + if (!topicMesh.Contains(peerId)) + { + topicMesh.Add(peerId); + gPeers[graft.TopicID].Add(peerId); + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Graft) + .Add(new ControlGraft { TopicID = graft.TopicID }); + } + } + } + } + } + + private void HandlePrune(PeerId peerId, IEnumerable prunes, ConcurrentDictionary peerMessages) + { + foreach (ControlPrune? prune in prunes) + { + if (topicState.ContainsKey(prune.TopicID) && mesh[prune.TopicID].Contains(peerId)) + { + if (peerState.TryGetValue(peerId, out PubsubPeer? state)) + { + state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); + } + mesh[prune.TopicID].Remove(peerId); + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Prune) + .Add(new ControlPrune { TopicID = prune.TopicID }); + + foreach (PeerInfo? peer in prune.Peers) + { + _peerStore.Discover(peer.SignedPeerRecord); + } + } + } + } + + private void HandleIhave(PeerId peerId, IEnumerable ihaves, ConcurrentDictionary peerMessages) + { + List messageIds = []; + + foreach (ControlIHave? ihave in ihaves.Where(iw => topicState.ContainsKey(iw.TopicID))) + { + messageIds.AddRange(ihave.MessageIDs.Select(m => new MessageId(m.ToByteArray())) + .Where(mid => !_messageCache.Contains(mid))); + } + + if (messageIds.Any()) + { + ControlIWant ciw = new(); + foreach (MessageId mId in messageIds) + { + ciw.MessageIDs.Add(ByteString.CopyFrom(mId.Bytes)); + } + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Ensure(r => r.Control.Iwant) + .Add(ciw); + } + } + + private void HandleIwant(PeerId peerId, IEnumerable iwants, ConcurrentDictionary peerMessages) + { + IEnumerable messageIds = iwants.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())); + List messages = []; + foreach (MessageId? mId in messageIds) + { + Message message = _messageCache.Get(mId); + if (message != default) + { + messages.Add(message); + } + } + if (messages.Any()) + { + peerMessages.GetOrAdd(peerId, _ => new Rpc()) + .Publish.AddRange(messages); + } + } + + private void HandleIdontwant(PeerId peerId, IEnumerable idontwants) + { + foreach (MessageId messageId in idontwants.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())).Take(_settings.MaxIdontwantMessages)) + { + _dontWantMessages.Add((peerId, messageId)); + } + } +} diff --git a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs index 9574cf25..c4c3a3db 100644 --- a/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs +++ b/src/libp2p/Libp2p.Protocols.Pubsub/PubsubRouter.cs @@ -169,7 +169,7 @@ public PubsubRouter(PeerStore store, PubsubSettings? settings = null, ILoggerFac _dontWantMessages = new(_settings.MessageCacheTtl); } - public async Task StartAsync(ILocalPeer localPeer, CancellationToken token = default) + public Task StartAsync(ILocalPeer localPeer, CancellationToken token = default) { logger?.LogDebug($"Running pubsub for {string.Join(",", localPeer.ListenAddresses)}"); @@ -180,7 +180,6 @@ public async Task StartAsync(ILocalPeer localPeer, CancellationToken token = def this.localPeer = localPeer; - _peerStore.OnNewPeer += (addrs) => { if (addrs.Any(a => a.GetPeerId()! == localPeer.Identity.PeerId)) @@ -204,7 +203,7 @@ public async Task StartAsync(ILocalPeer localPeer, CancellationToken token = def } catch { - reconnections.Add(new Reconnection(addrs, this._settings.ReconnectionAttempts)); + reconnections.Add(new Reconnection(addrs, _settings.ReconnectionAttempts)); } }); }; @@ -213,7 +212,7 @@ public async Task StartAsync(ILocalPeer localPeer, CancellationToken token = def { while (!token.IsCancellationRequested) { - await Task.Delay(this._settings.HeartbeatInterval); + await Task.Delay(_settings.HeartbeatInterval); await Heartbeat(); } }, token); @@ -223,12 +222,13 @@ public async Task StartAsync(ILocalPeer localPeer, CancellationToken token = def { while (!token.IsCancellationRequested) { - await Task.Delay(this._settings.ReconnectionPeriod); + await Task.Delay(_settings.ReconnectionPeriod); await Reconnect(token); } }, token); logger?.LogInformation("Started"); + return Task.CompletedTask; } public void Dispose() @@ -461,283 +461,6 @@ internal CancellationToken InboundConnection(Multiaddress addr, string protocolI } } } - - internal void OnRpc(PeerId peerId, Rpc rpc) - { - try - { - ConcurrentDictionary peerMessages = new(); - lock (this) - { - if (rpc.Publish.Count != 0) - { - HandleNewMessages(peerId, rpc.Publish, peerMessages); - } - - if (rpc.Subscriptions.Count != 0) - { - HandleSubscriptions(peerId, rpc.Subscriptions); - } - - if (rpc.Control is not null) - { - if (rpc.Control.Graft.Count != 0) - { - HandleGraft(peerId, rpc.Control.Graft, peerMessages); - } - - if (rpc.Control.Prune.Count != 0) - { - HandlePrune(peerId, rpc.Control.Prune, peerMessages); - } - - if (rpc.Control.Ihave.Count != 0) - { - HandleIhave(peerId, rpc.Control.Ihave, peerMessages); - } - - if (rpc.Control.Iwant.Count != 0) - { - HandleIwant(peerId, rpc.Control.Iwant, peerMessages); - } - - if (rpc.Control.Idontwant.Count != 0) - { - HandleIdontwant(peerId, rpc.Control.Idontwant); - } - } - } - foreach (KeyValuePair peerMessage in peerMessages) - { - peerState.GetValueOrDefault(peerMessage.Key)?.Send(peerMessage.Value); - } - } - catch (Exception ex) - { - logger?.LogError(ex, "Exception while processing RPC"); - } - } - - private void HandleNewMessages(PeerId peerId, IEnumerable messages, ConcurrentDictionary peerMessages) - { - logger?.LogDebug($"Messages received: {messages.Select(_settings.GetMessageId).Count(messageId => _limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId))}/{messages.Count()}"); - - foreach (Message? message in messages) - { - MessageId messageId = _settings.GetMessageId(message); - - if (_limboMessageCache.Contains(messageId) || _messageCache!.Contains(messageId)) - { - continue; - } - - switch (VerifyMessage?.Invoke(message)) - { - case MessageValidity.Rejected: - case MessageValidity.Ignored: - _limboMessageCache.Add(messageId, message); - continue; - case MessageValidity.Trottled: - continue; - } - - if (!message.VerifySignature(_settings.DefaultSignaturePolicy)) - { - _limboMessageCache!.Add(messageId, message); - continue; - } - - _messageCache.Add(messageId, message); - - PeerId author = new(message.From.ToArray()); - OnMessage?.Invoke(message.Topic, message.Data.ToByteArray()); - - if (fPeers.TryGetValue(message.Topic, out HashSet? topicPeers)) - { - foreach (PeerId peer in topicPeers) - { - if (peer == author || peer == peerId) - { - continue; - } - peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); - } - } - if (mesh.TryGetValue(message.Topic, out topicPeers)) - { - foreach (PeerId peer in topicPeers) - { - if (peer == author || peer == peerId) - { - continue; - } - peerMessages.GetOrAdd(peer, _ => new Rpc()).Publish.Add(message); - } - } - } - } - - private void HandleSubscriptions(PeerId peerId, IEnumerable subscriptions) - { - foreach (Rpc.Types.SubOpts? sub in subscriptions) - { - PubsubPeer? state = peerState.GetValueOrDefault(peerId); - if (state is null) - { - return; - } - if (sub.Subscribe) - { - if (state.IsGossipSub) - { - gPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); - } - else if (state.IsFloodSub) - { - fPeers.GetOrAdd(sub.Topicid, _ => []).Add(peerId); - } - } - else - { - if (state.IsGossipSub) - { - gPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); - if (mesh.ContainsKey(sub.Topicid)) - { - mesh[sub.Topicid].Remove(peerId); - } - if (fanout.ContainsKey(sub.Topicid)) - { - fanout[sub.Topicid].Remove(peerId); - } - } - else if (state.IsFloodSub) - { - fPeers.GetOrAdd(sub.Topicid, _ => []).Remove(peerId); - } - } - } - } - - private void HandleGraft(PeerId peerId, IEnumerable grafts, ConcurrentDictionary peerMessages) - { - foreach (ControlGraft? graft in grafts) - { - if (!topicState.ContainsKey(graft.TopicID)) - { - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Prune) - .Add(new ControlPrune { TopicID = graft.TopicID }); - } - else - { - HashSet topicMesh = mesh[graft.TopicID]; - - if (topicMesh.Count >= _settings.HighestDegree) - { - ControlPrune prune = new() { TopicID = graft.TopicID }; - - if (peerState.TryGetValue(peerId, out PubsubPeer? state) && state.IsGossipSub && state.Protocol >= PubsubPeer.PubsubProtocol.GossipsubV11) - { - state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); - prune.Peers.AddRange(topicMesh.ToArray().Select(pid => (PeerId: pid, Record: _peerStore.GetPeerInfo(pid)?.SignedPeerRecord)).Where(pid => pid.Record is not null).Select(pid => new PeerInfo - { - PeerID = ByteString.CopyFrom(pid.PeerId.Bytes), - SignedPeerRecord = pid.Record, - })); - } - - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Prune) - .Add(prune); - } - else - { - if (!topicMesh.Contains(peerId)) - { - topicMesh.Add(peerId); - gPeers[graft.TopicID].Add(peerId); - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Graft) - .Add(new ControlGraft { TopicID = graft.TopicID }); - } - } - } - } - } - - private void HandlePrune(PeerId peerId, IEnumerable prunes, ConcurrentDictionary peerMessages) - { - foreach (ControlPrune? prune in prunes) - { - if (topicState.ContainsKey(prune.TopicID) && mesh[prune.TopicID].Contains(peerId)) - { - if (peerState.TryGetValue(peerId, out PubsubPeer? state)) - { - state.Backoff[prune.TopicID] = DateTime.Now.AddSeconds(prune.Backoff == 0 ? 60 : prune.Backoff); - } - mesh[prune.TopicID].Remove(peerId); - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Prune) - .Add(new ControlPrune { TopicID = prune.TopicID }); - - foreach (PeerInfo? peer in prune.Peers) - { - _peerStore.Discover(peer.SignedPeerRecord); - } - } - } - } - - private void HandleIhave(PeerId peerId, IEnumerable ihaves, ConcurrentDictionary peerMessages) - { - List messageIds = []; - - foreach (ControlIHave? ihave in ihaves.Where(iw => topicState.ContainsKey(iw.TopicID))) - { - messageIds.AddRange(ihave.MessageIDs.Select(m => new MessageId(m.ToByteArray())) - .Where(mid => !_messageCache.Contains(mid))); - } - - if (messageIds.Any()) - { - ControlIWant ciw = new(); - foreach (MessageId mId in messageIds) - { - ciw.MessageIDs.Add(ByteString.CopyFrom(mId.Bytes)); - } - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Ensure(r => r.Control.Iwant) - .Add(ciw); - } - } - - private void HandleIwant(PeerId peerId, IEnumerable iwants, ConcurrentDictionary peerMessages) - { - IEnumerable messageIds = iwants.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())); - List messages = []; - foreach (MessageId? mId in messageIds) - { - Message message = _messageCache.Get(mId); - if (message != default) - { - messages.Add(message); - } - } - if (messages.Any()) - { - peerMessages.GetOrAdd(peerId, _ => new Rpc()) - .Publish.AddRange(messages); - } - } - - private void HandleIdontwant(PeerId peerId, IEnumerable idontwants) - { - foreach (MessageId messageId in idontwants.SelectMany(iw => iw.MessageIDs).Select(m => new MessageId(m.ToByteArray())).Take(_settings.MaxIdontwantMessages)) - { - _dontWantMessages.Add((peerId, messageId)); - } - } } internal enum ConnectionInitiation diff --git a/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs b/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs deleted file mode 100644 index 6b32d93d..00000000 --- a/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.cs +++ /dev/null @@ -1,307 +0,0 @@ -// -// Generated by the protocol buffer compiler. DO NOT EDIT! -// source: Exchange.proto -// -#pragma warning disable 1591, 0612, 3021, 8981 -#region Designer generated code - -using pb = global::Google.Protobuf; -using pbc = global::Google.Protobuf.Collections; -using pbr = global::Google.Protobuf.Reflection; -using scg = global::System.Collections.Generic; -namespace Nethermind.Libp2p.Protocols.PlainText.Dto { - - /// Holder for reflection information generated from Exchange.proto - public static partial class ExchangeReflection { - - #region Descriptor - /// File descriptor for Exchange.proto - public static pbr::FileDescriptor Descriptor { - get { return descriptor; } - } - private static pbr::FileDescriptor descriptor; - - static ExchangeReflection() { - byte[] descriptorData = global::System.Convert.FromBase64String( - string.Concat( - "Cg5FeGNoYW5nZS5wcm90byImCghFeGNoYW5nZRIKCgJpZBgBIAEoDBIOCgZw", - "dWJrZXkYAiABKAxCLKoCKU5ldGhlcm1pbmQuTGlicDJwLlByb3RvY29scy5Q", - "bGFpblRleHQuRHRv")); - descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, - new pbr::FileDescriptor[] { }, - new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::Nethermind.Libp2p.Protocols.PlainText.Dto.Exchange), global::Nethermind.Libp2p.Protocols.PlainText.Dto.Exchange.Parser, new[]{ "Id", "Pubkey" }, null, null, null, null) - })); - } - #endregion - - } - #region Messages - [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] - public sealed partial class Exchange : pb::IMessage - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - , pb::IBufferMessage - #endif - { - private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Exchange()); - private pb::UnknownFieldSet _unknownFields; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pb::MessageParser Parser { get { return _parser; } } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pbr::MessageDescriptor Descriptor { - get { return global::Nethermind.Libp2p.Protocols.PlainText.Dto.ExchangeReflection.Descriptor.MessageTypes[0]; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - pbr::MessageDescriptor pb::IMessage.Descriptor { - get { return Descriptor; } - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Exchange() { - OnConstruction(); - } - - partial void OnConstruction(); - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Exchange(Exchange other) : this() { - id_ = other.id_; - pubkey_ = other.pubkey_; - _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public Exchange Clone() { - return new Exchange(this); - } - - /// Field number for the "id" field. - public const int IdFieldNumber = 1; - private readonly static pb::ByteString IdDefaultValue = pb::ByteString.Empty; - - private pb::ByteString id_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pb::ByteString Id { - get { return id_ ?? IdDefaultValue; } - set { - id_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - /// Gets whether the "id" field is set - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool HasId { - get { return id_ != null; } - } - /// Clears the value of the "id" field - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void ClearId() { - id_ = null; - } - - /// Field number for the "pubkey" field. - public const int PubkeyFieldNumber = 2; - private readonly static pb::ByteString PubkeyDefaultValue = pb::ByteString.Empty; - - private pb::ByteString pubkey_; - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public pb::ByteString Pubkey { - get { return pubkey_ ?? PubkeyDefaultValue; } - set { - pubkey_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); - } - } - /// Gets whether the "pubkey" field is set - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool HasPubkey { - get { return pubkey_ != null; } - } - /// Clears the value of the "pubkey" field - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void ClearPubkey() { - pubkey_ = null; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override bool Equals(object other) { - return Equals(other as Exchange); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool Equals(Exchange other) { - if (ReferenceEquals(other, null)) { - return false; - } - if (ReferenceEquals(other, this)) { - return true; - } - if (Id != other.Id) return false; - if (Pubkey != other.Pubkey) return false; - return Equals(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override int GetHashCode() { - int hash = 1; - if (HasId) hash ^= Id.GetHashCode(); - if (HasPubkey) hash ^= Pubkey.GetHashCode(); - if (_unknownFields != null) { - hash ^= _unknownFields.GetHashCode(); - } - return hash; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public override string ToString() { - return pb::JsonFormatter.ToDiagnosticString(this); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void WriteTo(pb::CodedOutputStream output) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - output.WriteRawMessage(this); - #else - if (HasId) { - output.WriteRawTag(10); - output.WriteBytes(Id); - } - if (HasPubkey) { - output.WriteRawTag(18); - output.WriteBytes(Pubkey); - } - if (_unknownFields != null) { - _unknownFields.WriteTo(output); - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { - if (HasId) { - output.WriteRawTag(10); - output.WriteBytes(Id); - } - if (HasPubkey) { - output.WriteRawTag(18); - output.WriteBytes(Pubkey); - } - if (_unknownFields != null) { - _unknownFields.WriteTo(ref output); - } - } - #endif - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public int CalculateSize() { - int size = 0; - if (HasId) { - size += 1 + pb::CodedOutputStream.ComputeBytesSize(Id); - } - if (HasPubkey) { - size += 1 + pb::CodedOutputStream.ComputeBytesSize(Pubkey); - } - if (_unknownFields != null) { - size += _unknownFields.CalculateSize(); - } - return size; - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(Exchange other) { - if (other == null) { - return; - } - if (other.HasId) { - Id = other.Id; - } - if (other.HasPubkey) { - Pubkey = other.Pubkey; - } - _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); - } - - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(pb::CodedInputStream input) { - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - input.ReadRawMessage(this); - #else - uint tag; - while ((tag = input.ReadTag()) != 0) { - if ((tag & 7) == 4) { - // Abort on any end group tag. - return; - } - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); - break; - case 10: { - Id = input.ReadBytes(); - break; - } - case 18: { - Pubkey = input.ReadBytes(); - break; - } - } - } - #endif - } - - #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE - [global::System.Diagnostics.DebuggerNonUserCodeAttribute] - [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { - uint tag; - while ((tag = input.ReadTag()) != 0) { - if ((tag & 7) == 4) { - // Abort on any end group tag. - return; - } - switch(tag) { - default: - _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); - break; - case 10: { - Id = input.ReadBytes(); - break; - } - case 18: { - Pubkey = input.ReadBytes(); - break; - } - } - } - } - #endif - - } - - #endregion - -} - -#endregion Designer generated code diff --git a/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto b/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto deleted file mode 100644 index 078e675c..00000000 --- a/src/libp2p/Libp2p.Protocols.Relay/Dto/Exchange.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto2"; - -option csharp_namespace = "Nethermind.Libp2p.Protocols.PlainText.Dto"; - -message Exchange { - optional bytes id = 1; - optional bytes pubkey = 2; -} \ No newline at end of file diff --git a/src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj b/src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj index 8c452f4d..c4e19369 100644 --- a/src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj +++ b/src/libp2p/Libp2p.Protocols.Relay/Libp2p.Protocols.Relay.csproj @@ -13,10 +13,6 @@ libp2p network plaintext - - - - diff --git a/src/libp2p/Libp2p.sln b/src/libp2p/Libp2p.sln index 60494d35..a21c51e8 100644 --- a/src/libp2p/Libp2p.sln +++ b/src/libp2p/Libp2p.sln @@ -66,8 +66,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Yamux.Tests", "Libp2p.Protocols.Yamux.Tests\Libp2p.Protocols.Yamux.Tests.csproj", "{D9003366-1562-49CA-B32D-087BBE3973ED}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransportInterop", "..\samples\transport-interop\TransportInterop.csproj", "{EC505F21-FC69-4432-88A8-3CD5F7899B08}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.PubsubPeerDiscovery", "Libp2p.Protocols.PubsubPeerDiscovery\Libp2p.Protocols.PubsubPeerDiscovery.csproj", "{F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Libp2p.Protocols.Pubsub.E2eTests", "Libp2p.Protocols.Pubsub.E2eTests\Libp2p.Protocols.Pubsub.E2eTests.csproj", "{BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7}" @@ -82,6 +80,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.Protocols.PubsubPeer EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Libp2p.Protocols.Relay", "Libp2p.Protocols.Relay\Libp2p.Protocols.Relay.csproj", "{F29F5376-4F93-486F-B933-3278177704DE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TransportInterop", "..\samples\transport-interop\TransportInterop.csproj", "{2C00E91D-79CE-470B-A38B-975F03C8B8A3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -188,10 +188,6 @@ Global {D9003366-1562-49CA-B32D-087BBE3973ED}.Debug|Any CPU.Build.0 = Debug|Any CPU {D9003366-1562-49CA-B32D-087BBE3973ED}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9003366-1562-49CA-B32D-087BBE3973ED}.Release|Any CPU.Build.0 = Release|Any CPU - {EC505F21-FC69-4432-88A8-3CD5F7899B08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC505F21-FC69-4432-88A8-3CD5F7899B08}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC505F21-FC69-4432-88A8-3CD5F7899B08}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC505F21-FC69-4432-88A8-3CD5F7899B08}.Release|Any CPU.Build.0 = Release|Any CPU {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}.Debug|Any CPU.Build.0 = Debug|Any CPU {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -220,6 +216,10 @@ Global {F29F5376-4F93-486F-B933-3278177704DE}.Debug|Any CPU.Build.0 = Debug|Any CPU {F29F5376-4F93-486F-B933-3278177704DE}.Release|Any CPU.ActiveCfg = Release|Any CPU {F29F5376-4F93-486F-B933-3278177704DE}.Release|Any CPU.Build.0 = Release|Any CPU + {2C00E91D-79CE-470B-A38B-975F03C8B8A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C00E91D-79CE-470B-A38B-975F03C8B8A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C00E91D-79CE-470B-A38B-975F03C8B8A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C00E91D-79CE-470B-A38B-975F03C8B8A3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -243,13 +243,13 @@ Global {FC0E9BCE-2848-45DC-AE20-FB7E862A199E} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {EEECB761-A3C3-4598-AD03-EFABBF6CAA77} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {D9003366-1562-49CA-B32D-087BBE3973ED} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} - {EC505F21-FC69-4432-88A8-3CD5F7899B08} = {0DC1C6A1-0A5B-43BA-9605-621C21A16716} {F14C0226-D2B1-48B8-BC6A-163BE2C8A4C6} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {BFE1CCB2-59A3-4A69-B543-EBC9C16E39F7} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {C3CDBAAE-C790-443A-A293-D6E2330160F7} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {89BD907E-1399-4BE7-98CC-E541EAB21842} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {EC0B1626-C006-4138-A119-FE61CDAB824D} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} {F29F5376-4F93-486F-B933-3278177704DE} = {6F3D9AA9-C92D-4998-BC4E-D5EA068E8D0D} + {2C00E91D-79CE-470B-A38B-975F03C8B8A3} = {0DC1C6A1-0A5B-43BA-9605-621C21A16716} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E337E37C-3DB8-42FA-9A83-AC4E3B2557B4} diff --git a/src/samples/transport-interop/packages.lock.json b/src/samples/transport-interop/packages.lock.json index 77f360a2..880a92b3 100644 --- a/src/samples/transport-interop/packages.lock.json +++ b/src/samples/transport-interop/packages.lock.json @@ -176,8 +176,8 @@ }, "Nethermind.Multiformats.Address": { "type": "Transitive", - "resolved": "1.1.7", - "contentHash": "fp8qIQqHOQVetRgkR8nz047UcTYF1Z8uxSNxkRL6jwYhJI9ETsGcGL4FDZP2U56RyRFiin4k0nwCja0ayrJPYQ==", + "resolved": "1.1.8", + "contentHash": "+nRuuVXjj/Okj/RAJtJUZ/nDRjwMfjJnF1+4Z7gKX2MjMsxR92KPJbsP4fY4IaEwXMDjNJsXJu78z2C06tElzw==", "dependencies": { "BinaryEncoding": "1.4.0", "Nethermind.Multiformats.Base": "2.0.3-preview.1", @@ -1200,7 +1200,7 @@ "Microsoft.Extensions.DependencyInjection": "[8.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.0, )", "Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )", - "Nethermind.Multiformats.Address": "[1.1.7, )", + "Nethermind.Multiformats.Address": "[1.1.8, )", "SimpleBase": "[4.0.0, )" } },